おおいしつかさ


旅行とバイクとドライブと料理と宇宙が好き。
Ubie Discoveryのプログラマ。
Share:  このエントリーをはてなブックマークに追加

サムネイル画像高速作成ツールspeedpetalの開発 - その3 バイキュービック法を試してみた

今度はバイキュービック法を試してみました。バイキュービック法は元画像のポイントの周囲16ピクセルも調べて、それぞれのピクセルの色に距離による重みを加えた上で新しい画像のポイントの色にするというやり方です。当然、ニアレストネイバー法よりもコストがかかります。
一般的にバイキュービックは以下のような式で重みを導き出します。

t : ターゲットの座標と対象のピクセルとの距離  
0<= |t| < 1 なら  
  (a + 2)|t|^3 - (a + 3)|t|^2 + 1  
1<= |t| < 2 なら  
  a|t|^3 - 5a|t|^2 + 8a|t| - 4a  
2 <= |t| なら  
  0  

aは定数で、-1としている人が多いようです。ただ、ぼくが試したときは画像によってノイズが出るときがありました。-0.5くらいがいいのかも。また、処理速度もかなり遅くなります。その上、ぼくが見るかぎりはニアレストネイバー法と大きく差違を感じませんでした。高速にサムネイルを作成するという目的から、ニアレストネイバー法を採用しようと思います。
ちなみにバイキュービック法によるサムネイルの作成のコードは以下のような感じです。

// 重み計算  
double color_weight(double origin, int target) {  
    double distance = fabs(origin - target);  
    double weight = 0.0;  

    if (distance < 1.0) {  
        weight = (CONST_W + 2) * pow(distance, 3.0) - (CONST_W + 3) * pow(distance, 2.0) + 1;  
    } else if(distance < 2.0) {  
        weight = CONST_W * pow(distance, 3.0) - 5 * CONST_W * pow(distance, 2.0) + 8 * CONST_W * distance - 4 * CONST_W;  
    }  
    return weight;  
}  

// バイキュービック法による計算  
void bicubic(JSAMPARRAY in_buffer, JSAMPARRAY out_buffer, int origin_width, int origin_height, int x, int y, double origin_x, double origin_y, int components) {  
    int nearest_x = (int)floor(origin_x);  
    int nearest_y = (int)floor(origin_y);  

    double red = 0.0;  
    double blue = 0.0;  
    double green = 0.0;  
    for(int round_x = nearest_x -1; round_x <= nearest_x + 2; round_x++){  
        for(int round_y = nearest_y - 1; round_y <= nearest_y + 2; round_y++){  
            if(round_x >= 0 && round_x < origin_width && round_y >= 0 && round_y < origin_height) {  
                double weight_x = color_weight(origin_x, round_x);  
                double weight_y = color_weight(origin_y, round_y);  
                red += in_buffer[round_y][round_x * components] * weight_x * weight_y;  
                green += in_buffer[round_y][round_x * components + 1] * weight_x * weight_y;  
                blue += in_buffer[round_y][round_x * components + 2] * weight_x * weight_y;  
            }  
        }  
    }  
    int n_red = (int)floor(red + 0.5);  
    int n_green = (int)floor(green + 0.5);  
    int n_blue = (int)floor(blue + 0.5);  
    n_red = n_red > 255 ? 255 : n_red;  
    n_green = n_green > 255 ? 255 : n_green;  
    n_blue = n_blue > 255 ? 255 : n_blue;  
    out_buffer[y][x * components] = n_red;  
    out_buffer[y][x * components + 1] = n_green;  
    out_buffer[y][x * components + 2] = n_blue;  
}  

// サムネイル生成  
void create_thumbnail(JSAMPARRAY in_buffer, JSAMPARRAY out_buffer, int origin_width, int origin_height, int width, int height, int components, int algorithm) {  
    double width_factor = (double)origin_width / width;  
    double height_factor = (double)origin_height / height;  
    double width_pos[width];  

    for(int x = 0; x < width; x++) {  
        width_pos[x] = x * width_factor;  
    }  

    for(int y = 0; y < height; y++) {  
        double height_pos = y * height_factor;  
        for(int x = 0; x < width; x++) {  
            bicubic(in_buffer, out_buffer, origin_width, origin_height, x, y, width_pos[x], height_pos, components);  
        }  
    }  
}