第9章 色で抜き出す

色の分布を調べる

カラー画像
上のフルーツのカラー画像のBMP形式、fruit-color.bmpは 圧縮ファイルfruit_color.zipをダウンロードし、 それを展開して得られる。
この画像のR, G, B各プレーンのヒストグラムを、3章のヒストグラムをファイル出力 する関数を利用して出力し、gnuplotを用いてグラフを描いた例を以下に示す。

カラー画像のヒストグラム

Rプレーン

Rプレーン

Gプレーン

Gプレーン

Bプレーン

Bプレーン

いずれのプレーンも0〜50に山ができている。これは背景のようである。
このように、面積の小さい領域の分布の山は、背景などの大きな面積の領域の分布の 山に埋もれてしまい、見分けがつかなくなってしまうことがある。
そこで、2つのプレーンの関係をみるために、2次元ヒストグラムを利用する。

2次元ヒストグラム

2次元ヒストグラムをを求めて画像化する関数を用いて、 2次元ヒストグラムを画像化した。
グラフの軸や数値は、図9.3と同じである。

RとGの関係
横軸にR、縦軸にGをとった。
RとGを同じ割ありで混ぜると黄色になることから、 RとGがほぼ同じ割合で現れている部分がバナナ、 ややRの方が強く出ている方がオレンジ、 Rがほとんどを占めているのがりんごであろう。

RとBの関係
横軸にR、縦軸にBをとった。
今回のサンプルでは見づらいが、Bが最も強く出ている部分がリンゴのハイライト、 少量のBを含み多くの画素がでているのがバナナ、 Bをほとんど含まないのがオレンジであろう。

色の分布を使って切り出す

色の分布を使って、バナナを切り出した例を示す。

R100〜255
Rの値に注目して、R = 100以上の値を抜き出した。
背景が取り除かれた。

G110〜255
次にG = 110以上の値の画素を抜き出した。
赤いリンゴが除去された。

B50〜150
次にBが50〜150の画素を取り出した。
オレンジが除去されたが、今回の画像では、オレンジとバナナの色が近かったため、 バナナも一部除去されてしまった。

画像を合成する

クロマキー
上の画像のBMP形式は、chroma.zipをダウンロードし、 解凍して得られる。
この画像に対して、ハード・キー、ソフト・キーを用いた 画像合成例を示す。
背景には下の画像を用いる。

背景画像

ハード・キー

ハード・キー
(9.1式)で得られる値に対して、閾値-50で合成キー信号を作成した。
閾値で黒か白か分離されているので、2値画像になっている。

ハード・キー合成画像
上のキー信号を元に画像を合成した。
ガタガタしており、グラスの質感は認識しづらい。

ソフト・キー

ソフト・キー
-100〜0で合成キー信号を作成した。
ハード・キーに対して、前景と背景がミックスされるようになるので、 キーはグレー画像になっているのがわかる。

ソフト・キー合成画像
上のキー信号を元に画像を合成した。
グラスの質感も認識できる。

しかし、まだ背景画像の色がぼやけている。 これをのぞくために、境界部色消し付きの画像合成を行う関数を利用して、 画像合成した結果を以下に示す。

境界部色消し付きの画像合成
多少色は残ったものの、背景画像の色が消され、違和感のない画像になった。

プログラミング例

2次元ヒストグラムをを求めて画像化する

#include  "Params.h"

#define BIAS    128    /* ヒストグラム画像のバイアス値 */

/*--- hist2_image --- 2次元ヒストグラムを求め画像化する ----------------------
    image_in1:    画像データ X軸用
    image_in2:    画像データ Y軸用
    image_hist:    2次元ヒストグラム
-----------------------------------------------------------------------------*/
void hist2_image(unsigned char  image_in1[Y_SIZE][X_SIZE], 
    unsigned char image_in2[Y_SIZE][X_SIZE], 
    unsigned char image_hist[Y_SIZE][X_SIZE])
{
    int   i, j, kx, ky;
    int   hx, hy, max, kk;

    for (i = 0; i < Y_SIZE; i++)                      /* 初期化 */
        for (j = 0; j < X_SIZE; j++)
            image_hist[i][j] = 0;
    max = 0;
    ky = 256 / Y_SIZE;
    kx = 256 / X_SIZE; 
    for (i = 0; i < Y_SIZE; i++) {
        for (j = 0; j < X_SIZE; j++) {
            hy = (HIGH - (int)image_in2[i][j]) / ky;
            hx = ((int)image_in1[i][j]) / kx;
            if (image_hist[hy][hx] < HIGH) image_hist[hy][hx]++;
            if (max < image_hist[hy][hx]) max = image_hist[hy][hx];
        }
    }
    for (i = 0; i < Y_SIZE; i++) {
        for (j = 0; j < X_SIZE; j++) {
            if (image_hist[i][j] != 0) {
                kk = (long)image_hist[i][j] * HIGH / max + BIAS;
                if (kk > HIGH)    image_hist[i][j] = HIGH;
                else            image_hist[i][j] = kk;
            }
        }
    }
    for (i = 0; i < Y_SIZE; i++) image_hist[i][0] = HIGH;              /* X軸 */
    for (j = 0; j < X_SIZE; j++) image_hist[Y_SIZE-1][j] = HIGH;     /* Y軸 */
}

RGBによる閾値処理

#include  "Params.h"

/*--- thresh_color --- R,G,B値による閾値処理 ----------------------------------
    image_in_r:        入力R画像
    image_in_g:        入力G画像
    image_in_b:        入力B画像
    image_out_r:    出力R画像
    image_out_g:    出力G画像
    image_out_b:    出力B画像
    thdrl, thdrm:    Rの閾値 (min,max)
    thdgl, thdgm:    Gの閾値 (min,max)
    thdbl, thdbm:    Bの閾値 (min,max)
-----------------------------------------------------------------------------*/
void thresh_color(unsigned char image_in_r[Y_SIZE][X_SIZE], 
    unsigned char image_in_g[Y_SIZE][X_SIZE], 
    unsigned char image_in_b[Y_SIZE][X_SIZE],
    unsigned char image_out_r[Y_SIZE][X_SIZE], 
    unsigned char image_out_g[Y_SIZE][X_SIZE], 
    unsigned char image_out_b[Y_SIZE][X_SIZE],
    int thdrl, int thdrm, int thdgl, int thdgm, int thdbl, int thdbm)
{
    int   i, j;

    for (i = 0; i < Y_SIZE; i++) {
        for (j = 0; j < X_SIZE; j++) {
            image_out_r[i][j] = image_in_r[i][j];
            image_out_g[i][j] = image_in_g[i][j];
            image_out_b[i][j] = image_in_b[i][j];
        }
    }
    for (i = 0; i < Y_SIZE; i++) {
        for (j = 0; j < X_SIZE; j++) {
            if (image_out_r[i][j] < thdrl)
                image_out_r[i][j] = image_out_g[i][j] = image_out_b[i][j] = 0;
            if (image_out_r[i][j] > thdrm)
                image_out_r[i][j] = image_out_g[i][j] = image_out_b[i][j] = 0;
            if (image_out_g[i][j] < thdgl)
                image_out_r[i][j] = image_out_g[i][j] = image_out_b[i][j] = 0;
            if (image_out_g[i][j] > thdgm)
                image_out_r[i][j] = image_out_g[i][j] = image_out_b[i][j] = 0;
            if (image_out_b[i][j] < thdbl)
                image_out_r[i][j] = image_out_g[i][j] = image_out_b[i][j] = 0;
            if (image_out_b[i][j] > thdbm)
                image_out_r[i][j] = image_out_g[i][j] = image_out_b[i][j] = 0;
        }
    }
}

合成用ハード・キーの生成

#include  "Params.h"

/*--- hard_mask --- 合成キー(ハードキー)の生成 ------------------------------
    image_in_r:    入力R画像
    image_in_g:    入力G画像
    image_in_b:    入力R画像
    image_key:    入力B画像
    thresh:        閾値
-----------------------------------------------------------------------------*/
void hard_mask(unsigned char image_in_r[Y_SIZE][X_SIZE], 
    unsigned char image_in_g[Y_SIZE][X_SIZE], 
    unsigned char image_in_b[Y_SIZE][X_SIZE], 
    unsigned char image_key[Y_SIZE][X_SIZE], int thresh)
{
    int   i, j, d;
    
    for (i = 0; i < Y_SIZE; i++) {
        for (j = 0; j < X_SIZE; j++) {
            d = ((int)image_in_r[i][j] + (int)image_in_g[i][j]) / 2
                - (int)image_in_b[i][j];
            if (d >= thresh) image_key[i][j] = 255;
            else             image_key[i][j] = 0;
        }
    }
}

画面合成

#include   "Params.h"

/*--- synth --- クロマキーによる画面合成 --------------------------------------
    image_in1_r:        入力前景R画像
    image_in1_g:        入力前景G画像
    image_in1_b:        入力前景B画像
    image_in2_r:        入力背景R画像
    image_in2_g:        入力背景G画像
    image_in2_b:        入力背景B画像
    image_out_r:        出力合成R画像
    image_out_g:        出力合成G画像
    image_out_b:        出力合成B画像
    image_key:            合成用キー画像
-----------------------------------------------------------------------------*/
void synth(unsigned char image_in1_r[Y_SIZE][X_SIZE], 
    unsigned char image_in1_g[Y_SIZE][X_SIZE],
    unsigned char image_in1_b[Y_SIZE][X_SIZE],
    unsigned char image_in2_r[Y_SIZE][X_SIZE],
    unsigned char image_in2_g[Y_SIZE][X_SIZE],
    unsigned char image_in2_b[Y_SIZE][X_SIZE],
    unsigned char image_out_r[Y_SIZE][X_SIZE],
    unsigned char image_out_g[Y_SIZE][X_SIZE],
    unsigned char image_out_b[Y_SIZE][X_SIZE],
    unsigned char image_key[Y_SIZE][X_SIZE])
{
    int   i, j;
    int   rr1, gg1, bb1;
    int   rr2, gg2, bb2;
    long  kk;

    for (i = 0; i < Y_SIZE; i++) {
        for (j = 0; j < X_SIZE; j++) {
            rr1 = (int)image_in1_r[i][j];
            gg1 = (int)image_in1_g[i][j];
            bb1 = (int)image_in1_b[i][j];
            rr2 = (int)image_in2_r[i][j];
            gg2 = (int)image_in2_g[i][j];
            bb2 = (int)image_in2_b[i][j];
            kk = (long)image_key[i][j];
            image_out_r[i][j] = (unsigned char)((rr1*kk+rr2*(255-kk))/255);
            image_out_g[i][j] = (unsigned char)((gg1*kk+gg2*(255-kk))/255);
            image_out_b[i][j] = (unsigned char)((bb1*kk+bb2*(255-kk))/255);
        }
    }
}

合成用ソフト・キーの生成

#include  "Params.h"

/*--- soft_mask --- 合成用キー(ソフトキー)の生成 ----------------------------
    image_in_r:        入力R画像
    image_in_g:        入力G画像
    image_in_b:        入力B画像
    image_key:        合成用キー画像
    thdh, thdl:        閾値 (max,min) 
-----------------------------------------------------------------------------*/
void soft_mask(unsigned char image_in_r[Y_SIZE][X_SIZE], 
    unsigned char image_in_g[Y_SIZE][X_SIZE], 
    unsigned char image_in_b[Y_SIZE][X_SIZE],
    unsigned char image_key[Y_SIZE][X_SIZE], int thdh, int thdl)
{
    int     i, j, d;
    int     kk;

    for (i = 0; i < Y_SIZE; i++) {
        for (j = 0; j < X_SIZE; j++) {
            d = ((int)image_in_r[i][j] + (int)image_in_g[i][j]) / 2
               - (int)image_in_b[i][j];
            kk = ((long)(d - thdl) * 255 / (thdh - thdl));
            if (kk > 255)        image_key[i][j] = 255;
            else if (kk < 0)     image_key[i][j] = 0;
            else                image_key[i][j] = kk;
        }
    }
}

境界部色消し付きの画像合成

#include  "Params.h"

/*--- s_synth --- クロマキーによる画面合成(境界部色消し)---------------------
    image_in1_r:        入力前景R画像
    image_in1_g:        入力前景G画像
    image_in1_b:        入力前景B画像
    image_in2_r:        入力背景R画像
    image_in2_g:        入力背景G画像
    image_in2_b:        入力背景B画像
    image_out_r:        出力合成R画像
    image_out_g:        出力合成G画像
    image_out_b:        出力合成B画像
    image_key:            合成用キー画像
-----------------------------------------------------------------------------*/
void s_synth(unsigned char image_in1_r[Y_SIZE][X_SIZE], 
    unsigned char image_in1_g[Y_SIZE][X_SIZE],
    unsigned char image_in1_b[Y_SIZE][X_SIZE],
    unsigned char image_in2_r[Y_SIZE][X_SIZE],
    unsigned char image_in2_g[Y_SIZE][X_SIZE],
    unsigned char image_in2_b[Y_SIZE][X_SIZE],
    unsigned char image_out_r[Y_SIZE][X_SIZE],
    unsigned char image_out_g[Y_SIZE][X_SIZE],
    unsigned char image_out_b[Y_SIZE][X_SIZE],
    unsigned char image_key[Y_SIZE][X_SIZE])
{
    int   i, j;
    int   rr1, gg1, bb1;
    int   rr2, gg2, bb2;
    long  kk;

    for (i = 0; i < Y_SIZE; i++) {
        for (j = 0; j < X_SIZE; j++) {
            rr1 = (int)image_in1_r[i][j];
            gg1 = (int)image_in1_g[i][j];
            bb1 = (int)image_in1_b[i][j];
            rr2 = (int)image_in2_r[i][j];
            gg2 = (int)image_in2_g[i][j];
            bb2 = (int)image_in2_b[i][j];
            kk = (long)image_key[i][j];
            if (kk == 255 || kk == 0) {       /* 前景または背景 */
              image_out_r[i][j] = (unsigned char)((rr1*kk+rr2*(255-kk))/255);
              image_out_g[i][j] = (unsigned char)((gg1*kk+gg2*(255-kk))/255);
              image_out_b[i][j] = (unsigned char)((bb1*kk+bb2*(255-kk))/255);
            }
            else {                              /* 境界部 */
              image_out_r[i][j] = (unsigned char)((gg1*kk+rr2*(255-kk))/255);
              image_out_g[i][j] = (unsigned char)((gg1*kk+gg2*(255-kk))/255);
              image_out_b[i][j] = (unsigned char)((gg1*kk+bb2*(255-kk))/255);
            }
        }
    }
}