floodFillでラベリング
最終更新: 2014年12月19日(金)23:40; Revision: 1.5; by momma
floodFill関数を用いてラベリング処理
任意の色で塗り潰しが可能なfloodFill関数を使ったラベリング処理
概要
- 0 or 255の8bit画像を32bit画像へ変換し,255を0.5にする
- 画像をスキャンして0.5だったら1から始まるラベル番号++で塗る
- ヒストグラムを取得
- 背景のカウントを0
- std::sortまたはqsortを使ってサイズ順に並べ替え
- おしまい
利点
- OpenCVで完結
- 8近傍, 4近傍が選択可能
- 256個以上のBlobでも処理が可能
- それなりに速い
欠点
- 凸包や重心などの計算が未実装(OpenCVのの関数を使えば可能)
- 多分無駄が多い
ソース
C
/* * CfloodFill_Labeling.cpp * * Created on: 2011/02/11 * Author: Eiichiro Momma */ #include <opencv2/core/core_c.h> #include <opencv2/imgproc/imgproc_c.h> #include <opencv2/highgui/highgui_c.h> #include <stdio.h> #include <stdlib.h> //compat.hppから拝借 #define cvGetHistValue_1D( hist, idx0 ) \ ((float*)cvPtr1D( (hist)->bins, (idx0), 0)) enum{ nNeighbors = 8, isVisible = 1, vDelay = 10 }; //並び替え用 typedef struct { int idx; float val; }histo_dat; int comp(const void *d1, const void *d2) { histo_dat left = *(histo_dat *)d1; histo_dat right = *(histo_dat *)d2; return right.val - left.val; } int main(void) { IplImage *fsrc; //0 or 255の2値画像 IplImage *src = cvLoadImage("test3.png", CV_LOAD_IMAGE_GRAYSCALE); if (src==NULL) { return 1; } //255個以上のBlobへ対応するためCV_32Fへ fsrc = cvCreateImage(cvGetSize(src), IPL_DEPTH_32F, 1); cvConvertScale(src, fsrc, 1, 0); // 0~255 -> 0~0.5 cvThreshold(fsrc, fsrc, 128, 0.5, CV_THRESH_BINARY); int nL=1; if(isVisible) { cvNamedWindow("src",CV_WINDOW_AUTOSIZE); cvShowImage("src",fsrc); cvWaitKey(0); } for (int y=0; y< fsrc->height; y++) { for (int x=0; x<fsrc->width; x++) { //floodFillでラベル=濃度の塗り潰し if (cvGetReal2D(fsrc, y, x) > 0.25 && cvGetReal2D(fsrc, y, x) < 0.75) { cvFloodFill(fsrc, cvPoint(x,y), cvScalar(nL), cvScalar(0.25), cvScalar(0.25), NULL, nNeighbors); if (isVisible) { cvShowImage("src",fsrc); cvWaitKey(vDelay); } nL++; } } } //BlobのサイズをcalcHistで求める int size[] = {nL}; float range[]={0,nL}; float* ranges[]={range}; IplImage *imgs[]={fsrc}; CvHistogram *h = cvCreateHist(1, size, CV_HIST_ARRAY, ranges, 1); cvCalcHist(imgs, h); float *fval; histo_dat hd; histo_dat *vhd = (histo_dat*)malloc(sizeof(hd)*nL); for (int i=0; i<nL; i++) { hd.idx = i; fval=cvGetHistValue_1D(h,i); hd.val = *fval; vhd[i] = hd; } //ignore background vhd[0].val = 0.0; cvSetReal1D(h->bins, 0, 0.0); qsort(vhd, nL, sizeof(hd), comp); for (int i=0; i<nL; i++) { printf("%d: %f\n", vhd[i].idx, vhd[i].val); } float maxVal; int mI; cvGetMinMaxHistValue(h, NULL, &maxVal, NULL, &mI); printf("max: %d, %f\n", mI, maxVal); if (isVisible) { cvConvertScale(fsrc, fsrc, 1.0/nL, 0); cvShowImage("src",fsrc); cvWaitKey(0); } free(vhd); return 0; }
C++ I/F
/* * floodFill_Labeling.cpp * * Created on: 2011/02/01 * Author: Eiichiro Momma */ #include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> #include <vector> #include <algorithm> #include <functional> using namespace cv; enum{ nNeighbors = 8, // 4 or 8 近傍 isVisible = 1, // 処理の過程,結果を表示 vDelay = 10 // isVisibleでの遅延時間[ms] }; //並び替え用 struct histo_dat{ int idx; float val; }; bool operator<(const histo_dat& left, const histo_dat& right) { return left.val < right.val; } bool operator>(const histo_dat& left, const histo_dat& right) { return left.val > right.val; } int main(void) { Mat fsrc; //0 or 255の2値画像 Mat src = imread("test3.png", CV_LOAD_IMAGE_GRAYSCALE); if (src.empty()) { return 1; } //255個以上のBlobへ対応するためCV_32Fへ src.convertTo(fsrc, CV_32FC1, 1, 0); // 0~255 -> 0~0.5 threshold(fsrc, fsrc, 128, 0.5, CV_THRESH_BINARY); int nL=1; if(isVisible) { namedWindow("src",CV_WINDOW_AUTOSIZE); imshow("src",fsrc); waitKey(0); } for (int y=0; y<fsrc.rows; y++) { for (int x=0; x<fsrc.cols; x++) { //floodFillでラベル=濃度の塗り潰し if (fsrc.at<float>(y,x) > 0.25 && fsrc.at<float>(y,x)< 0.75) { floodFill(fsrc, Point(x,y), Scalar(nL), NULL, Scalar(0.25), Scalar(0.25), nNeighbors); if (isVisible) { imshow("src",fsrc); waitKey(vDelay); } nL++; } } } //BlobのサイズをcalcHistで求める Mat histo; float range[]={0,nL}; const float* ranges[]={range}; calcHist(&fsrc, 1, 0, Mat(), histo, 1,&nL ,ranges, true, false); //std::sortで並び替え std::vector<histo_dat> vhd; histo_dat hd; for (int i=0; i<nL; i++) { hd.idx = i; hd.val = histo.at<float>(i); vhd.push_back(hd); } //ignore background vhd[0].val = 0.0; std::sort(vhd.begin(), vhd.end(), std::greater<histo_dat>()); for (int i=0; i<nL; i++) { std::cout << vhd[i].idx << ": " << vhd[i].val << " pixel\n"; } double maxVal; Point mP; minMaxLoc(histo, 0, &maxVal, 0, &mP, Mat()); std::cout << "max idx is " << mP.y << ":" << maxVal << std::endl; if (isVisible) { fsrc.convertTo(fsrc, CV_32FC1, 1.0/nL , 0); imshow("src",fsrc); waitKey(0); } return 0; }
テスト用画像
とりあえずテスト
256以上のBlob