第7章 特徴を調べる

画像のラベリング

下の果物の画像fruit.bmp、それを閾値34で二値化した画像fruit34.bmpは BMP形式の圧縮ファイルをダウンロードし、それを展開して得られる。
fruit.bmp fruit34.bmp
この2値画像に対して、特徴パラメータを用いた処理例を示す。

特徴パラメータを使って抜き出す

上の2値画像に対して、ラベリングを行い、 特徴パラメータを調べた結果、以下の結果が得られた。

物体の番号 面積 周囲長 円形度 重心位置
03145221.620.8046(169, 50)
12069174.270.8561( 78, 90)
24293459.400.2556(197,123)
31868165.440.8577( 27,159)
45917508.360.2877(147,171)

また、ラベリングを行うプログラム を用いて、輪郭線、重心位置を表示させた結果は以下の通りである。
ラベリング
バナナの重心位置が描画されていないのは、バナナの輪郭と重なってしまった ためである。

上の表を参考に、特徴パラメータを使って対象を抜き出す。

バナナを抜き出す

円軽度0.5以下を抜き出す
バナナの円軽度が他より低いことを利用して、バナナを抜き出した。
ここでは、円軽度が0.5以下のの連結成分を抜き出した。

バナナを抜き出した
上で作成したラベルをマスク画像に使って、バナナを抜き出した。

特徴パラメータを使って雑音を除く

上に示した4章で作成した微分画像に対して、特徴パラメータを使って雑音を除去する例を示す。

元画像

元画像

微分画像

微分画像
1次微分(Gradient)を施した(4章)。

二値画像

二値画像
閾値50で二値化した。

雑音を除去

面積10以上を抜き出す
二値画像に対して、面積10以上の連結成分を取り出した。

プログラミング例

ラベリングを行う

#include    <stdio.h>
#include    "Params.h"

#define    L_BASE    100        /* 連結成分のラベルのベース値 */

void labelset(unsigned char image[Y_SIZE][X_SIZE], int xs, int ys, int label);

/*--- labeling --- 画像のラベリングを行う -------------------------------------
    image_in:        入力画像配列(2値画像)
    image_label:    出力画像配列(ラベル画像)
    cnt:            ラベルの個数
    buf:            バッファ
-----------------------------------------------------------------------------*/
int labeling(unsigned char image_in[Y_SIZE][X_SIZE], 
        unsigned char image_label[Y_SIZE][X_SIZE], int *cnt, char *buf)    
{
    int    i, j, label;

    for (i = 0; i < Y_SIZE; i++)
        for (j = 0; j < X_SIZE; j++)
            image_label[i][j] = image_in[i][j];
    label = L_BASE;
    for (i = 0; i < Y_SIZE; i++)
        for (j = 0; j < X_SIZE; j++) {
            if (image_label[i][j] == HIGH) {
                if (label >= HIGH) {
                    sprintf(buf, "--Error! too many labels. \n");
                    return -1;
                    }
                labelset(image_label, j, i, label); label++;
            }
        }
    *cnt = label-L_BASE;
    sprintf(buf, "  number of labels : %d \n", *cnt);
    return 0;
}

/*--- labelset --- 連結している画素すべてにラベル付けする ---------------------
    image:    画像配列
    xs, ys:    スタート位置
    label:    ラベル番号
-----------------------------------------------------------------------------*/
void labelset(unsigned char    image[Y_SIZE][X_SIZE], int xs, int ys, int label)
{
    int    i, j, cnt, im, ip, jm, jp;

    image[ys][xs] = label;
    for (;;) {
        cnt = 0;
        for (i = 0; i < Y_SIZE; i++)
            for (j = 0; j < X_SIZE; j++)
                if (image[i][j] == label) {
                    im = i-1; ip = i+1; jm = j-1; jp = j+1;
                    if (im < 0) im = 0; if (ip >= Y_SIZE) ip = Y_SIZE-1;
                    if (jm < 0) jm = 0; if (jp >= X_SIZE) jp = X_SIZE-1;
                    if (image[i ][jp] == HIGH) {
                        image[i ][jp] = label; cnt++;
                    }
                    if (image[im][jp] == HIGH) {
                        image[im][jp] = label; cnt++;
                    }
                    if (image[im][j ] == HIGH) {
                        image[im][j ] = label; cnt++;
                    }
                    if (image[im][jm] == HIGH) {
                        image[im][jm] = label; cnt++;
                    }
                    if (image[i ][jm] == HIGH) {
                        image[i ][jm] = label; cnt++;
                    }
                    if (image[ip][jm] == HIGH) {
                        image[ip][jm] = label; cnt++;
                    }
                    if (image[ip][j ] == HIGH) {
                        image[ip][j ] = label; cnt++;
                    }
                    if (image[ip][jp] == HIGH) {
                        image[ip][jp] = label; cnt++;
                    }
                }
        if (cnt == 0) break;
    }
}

特徴パラメータを調べる

#include    <stdio.h>
#include    "Params.h"

#define    L_BASE    100            /* 連結成分のラベルのベース値 */
#define    PI        (float)3.14159265
#define    ROOT2    (float)1.41421356

float calc_size(unsigned char image_label[Y_SIZE][X_SIZE], 
    int label, int *cx, int *cy);
float calc_length(unsigned char image_label[Y_SIZE][X_SIZE], int label);
float trace(unsigned char image_label[Y_SIZE][X_SIZE], int xs, int ys);

/*--- features --- 特徴パラメータを調べる -------------------------------------
    image_label_in:        入力ラベル画像配列
    image_label_out:    出力ラベル画像配列
    cnt:                物体の個数
    size:                面積
    ratio:                円形度
    buf:                メッセージ用バッファ
-----------------------------------------------------------------------------*/
void features(unsigned char    image_label_in[Y_SIZE][X_SIZE], 
    unsigned char image_label_out[Y_SIZE][X_SIZE], 
    int cnt, float size[], float ratio[], char *buf)
{
    int        i, j, center_x, center_y;
    float    l;
    int        posi, m;

    posi = 0;
    for (i = 0; i < Y_SIZE; i++)
        for (j = 0; j < X_SIZE; j++)
            image_label_out[i][j] = image_label_in[i][j];
    m = sprintf(buf, "   no   area   circum    round   grav x,y\n");
    posi += m;
    for (i = 0; i < cnt; i++) {
        size[i] = calc_size(image_label_out, i+L_BASE, 
            &center_x, &center_y);
        l = calc_length(image_label_out, i+L_BASE);
        ratio[i] = 4*PI*size[i]/(l*l);
        image_label_out[center_y][center_x] = HIGH;    /* 重心 */
        m = sprintf(&buf[posi], "  %3d %6d %8.2f %8.4f  (%3d,%3d)\n",
            i, (int)size[i], l, ratio[i], center_x, center_y);
        posi += m;
    }                                                      
}

/*--- calc_size --- 面積,重心位置を求める ------------------------------------
    image_label:    ラベル画像配列
    label:            ラベル番号
    cx, cy:            重心位置
-----------------------------------------------------------------------------*/
float calc_size(unsigned char image_label[Y_SIZE][X_SIZE], 
    int label, int *cx, int *cy)
{
    int        i, j;
    float    tx, ty, total;

    tx = 0; ty = 0; total = 0;
    for (i = 0; i < Y_SIZE; i++)
        for (j = 0; j < X_SIZE; j++)
            if (image_label[i][j] == label) {
                tx += j; ty += i; total++;
            }
    if (total == 0.0) return 0.0;
    *cx = (int)(tx/total); *cy = (int)(ty/total);
    return total;
}

/*--- calc_length --- 周囲長を求める ------------------------------------------
    image_label:    ラベル画像配列
    label:            ラベル番号
-----------------------------------------------------------------------------*/
float calc_length(unsigned char image_label[Y_SIZE][X_SIZE], int label)
{
    int        i, j;

    for (i = 0; i < Y_SIZE; i++)
        for (j = 0; j < X_SIZE; j++)
            if (image_label[i][j] == label)    return trace(image_label, j-1, i);
    return 0;
}

/*--- trace --- 輪郭線を追跡する ----------------------------------------------
    image_label:    ラベル画像配列
    xs, ys:            スタート位置
-----------------------------------------------------------------------------*/
float trace(unsigned char image_label[Y_SIZE][X_SIZE], int xs, int ys)
{
    int        x, y, no, vec;
    float    l;

    l = 0;    x = xs; y = ys; no = image_label[y][x+1]; vec = 5;
    for (;;) {
        if (x == xs && y == ys && l != 0) return l;
        image_label[y][x] = HIGH;
        switch (vec) {
            case 3: 
                if (image_label[y][x+1] != no && image_label[y-1][x+1] == no)
                    {x = x+1; y = y  ; l++       ; vec = 0; continue;}    
            case 4: 
                if (image_label[y-1][x+1] != no && image_label[y-1][x] == no)
                    {x = x+1; y = y-1; l += ROOT2; vec = 1; continue;}    
            case 5: 
                if (image_label[y-1][x] != no && image_label[y-1][x-1] == no)
                    {x = x  ; y = y-1; l++       ; vec = 2; continue;}    
            case 6: 
                if (image_label[y-1][x-1] != no && image_label[y][x-1] == no)
                    {x = x-1; y = y-1; l += ROOT2; vec = 3; continue;}    
            case 7: 
                if (image_label[y][x-1] != no && image_label[y+1][x-1] == no)
                    {x = x-1; y = y  ; l++       ; vec = 4; continue;}    
            case 0: 
                if (image_label[y+1][x-1] != no && image_label[y+1][x] == no)
                    {x = x-1; y = y+1; l += ROOT2; vec = 5; continue;}    
            case 1: 
                if (image_label[y+1][x] != no && image_label[y+1][x+1] == no)
                    {x = x  ; y = y+1; l++       ; vec = 6; continue;}    
            case 2: 
                if (image_label[y+1][x+1] != no && image_label[y][x+1] == no)
                    {x = x+1; y = y+1; l += ROOT2; vec = 7; continue;}    
                vec = 3;
        }
    }
}

ある範囲の円形度をもつ連結成分を抜き出す

#include    <stdio.h>
#include    "Params.h"

#define    L_BASE        100    /*  連結成分のラベルのベース値         */

/*--- ratio_extract --- 円形度がある範囲の連結成分を抜き出す ------------------
    image_label_in:            入力ラベル画像配列
    image_label_out:        出力ラベル画像配列
    cnt:                    連結成分の個数
    ratio:                    円形度
    ratio_min, ratio_max:    最小値,最大値
-----------------------------------------------------------------------------*/
void ratio_extract(unsigned char image_label_in[Y_SIZE][X_SIZE], 
    unsigned char image_label_out[Y_SIZE][X_SIZE], 
    int cnt, float ratio[], float ratio_min, float ratio_max)
{
    int    i, j, x, y;
    int    lno[256];

    for (i = 0, j = 0; i < cnt; i++)
        if (ratio[i] >= ratio_min && ratio[i] <= ratio_max)
            lno[j++] = L_BASE+i;
    for (y = 0 ; y < Y_SIZE; y++) {
        for (x = 0; x < X_SIZE; x++) {
            image_label_out[y][x] = 0;
            for (i = 0; i < j; i++)
                if (image_label_in[y][x] == lno[i])
                    image_label_out[y][x] = image_label_in[y][x];
        }
    }
}

マスク領域だけ画像をコピーする

#include "Params.h"

/*---- masking --- マスク領域だけ画像データをコピーする -----------------------
    image_in:    入力画像配列
    image_out:    出力画像配列
    image_mask:    マスク画像配列(2値画像)
-----------------------------------------------------------------------------*/
void masking(unsigned char image_in[Y_SIZE][X_SIZE],
    unsigned char image_out[Y_SIZE][X_SIZE], 
    unsigned char image_mask[Y_SIZE][X_SIZE])
{
    int    i, j;

    for (i = 0; i < Y_SIZE; i++) {
        for (j = 0; j < X_SIZE; j++) {
              if (image_mask[i][j] == HIGH)    image_out[i][j] = image_in[i][j];
              else                            image_out[i][j] = 0;
        }
    }
}

ある範囲の面積をもつ連結成分を抜き出す

#include    <stdio.h>
#include    "Params.h"

#define    L_BASE        100    /*  連結成分のラベルのベース値         */

/*--- size_extract --- 面積がある範囲の連結成分を抜き出す ---------------------
    image_label_in:        入力ラベル画像配列
    image_label_out:    出力ラベル画像配列
    cnt:                連結成分の個数
    size:                面積
    size_min, size_max:    最小値,最大値
-----------------------------------------------------------------------------*/
void size_extract(unsigned char image_label_in[Y_SIZE][X_SIZE], 
    unsigned char image_label_out[Y_SIZE][X_SIZE], 
    int cnt, float size[], float size_min, float size_max)
{
    int    i, j, x, y;
    int    lno[256];

    for (i = 0, j = 0; i < cnt; i++)
        if (size[i] >= size_min && size[i] <= size_max)    lno[j++] = L_BASE+i;
    for (y = 0; y < Y_SIZE; y++) {
        for (x = 0; x < X_SIZE; x++) {
            image_label_out[y][x] = 0;
            for (i=0 ; i<j ; i++)
                if (image_label_in[y][x] == lno[i]) 
                    image_label_out[y][x] = image_label_in[y][x];
        }
    }
}