OpenCV Labeling - eiichiromomma/CVMLAB GitHub Wiki

(OpenCV) Labeling

floodFill関数を用いてラベリング処理

任意の色で塗り潰しが可能なfloodFill関数を使ったラベリング処理

概要

  1. 0 or 255の8bit画像を32bit画像へ変換し,255を0.5にする
  2. 画像をスキャンして0.5だったら1から始まるラベル番号++で塗る
  3. ヒストグラムを取得
  4. 背景のカウントを0
  5. std::sortまたはqsortを使ってサイズ順に並べ替え
  6. おしまい

利点

  • OpenCVで完結
  • 8近傍, 4近傍が選択可能
  • 256個以上のBlobでも処理が可能
  • それなりに速い

欠点

  • 凸包や重心などの計算が未実装(OpenCVのの関数を使えば可能)
  • 多分無駄が多い

ソース

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;
    }

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;
    }

テスト用画像

とりあえずテスト

256以上のBlob

4or8近傍

⚠️ **GitHub.com Fallback** ⚠️