下の果物の画像fruit.bmp、それを閾値34で二値化した画像fruit34.bmpは
BMP形式の圧縮ファイルをダウンロードし、それを展開して得られる。
この2値画像に対して、特徴パラメータを用いた処理例を示す。
上の2値画像に対して、ラベリングを行い、 特徴パラメータを調べた結果、以下の結果が得られた。
物体の番号 | 面積 | 周囲長 | 円形度 | 重心位置 |
0 | 3145 | 221.62 | 0.8046 | (169, 50) |
1 | 2069 | 174.27 | 0.8561 | ( 78, 90) |
2 | 4293 | 459.40 | 0.2556 | (197,123) |
3 | 1868 | 165.44 | 0.8577 | ( 27,159) |
4 | 5917 | 508.36 | 0.2877 | (147,171) |
また、ラベリングを行うプログラム
を用いて、輪郭線、重心位置を表示させた結果は以下の通りである。
バナナの重心位置が描画されていないのは、バナナの輪郭と重なってしまった
ためである。
上の表を参考に、特徴パラメータを使って対象を抜き出す。
バナナの円軽度が他より低いことを利用して、バナナを抜き出した。
ここでは、円軽度が0.5以下のの連結成分を抜き出した。
上で作成したラベルをマスク画像に使って、バナナを抜き出した。
上に示した4章で作成した微分画像に対して、特徴パラメータを使って雑音を除去する例を示す。
1次微分(Gradient)を施した(4章)。
閾値50で二値化した。
二値画像に対して、面積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, ¢er_x, ¢er_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]; } } }