理科系の勉強日記

Linux/Ubuntu/Mac/Emacs/Computer vision/Robotics

LabelingクラスをOpenCVで利用

学生時代にも一度お世話になったラベリングクラスhttp://imura-lab.org/products/labeling/を久々に使いました。
OpenCVで使える関数を作ったのでメモ。

2値化が大津の手法なので、琵琶湖の画像にしてみました。
素敵な写真はこちらから使わせていただきました。綺麗ですね。
https://www.itoen.co.jp/itoen-motherlake/photocontest.html



#include <iostream>
#include <vector>
#include "Labeling.h"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace std;

// 適当な色を出す用
cv::RNG rnd(1192);
cv::Scalar randomColor(){
  return cv::Scalar(rnd.next() & 0xFF, rnd.next() & 0xFF, rnd.next() & 0xFF);
}

/**
 * @brief Label構造体
 */
struct LabelData{
  int ind;// インデックス
  cv::Point g;// 重心
  vector<cv::Point> points; // 座標
};


/**
 * @brief 2値画像をラベリングする関数
 * @param[in] in_img
 * @param[out] out_img
 * @param[out] points
 * @param[in] min_pix 最小ラベリング画像サイズ
 */
void Label(const cv::Mat& in_img, 
	   cv::Mat& out_img,
	   vector<LabelData>& labels,
	   int min_pix){
  
  // ラベリングの結果を受け取る行列
  cv::Mat label(in_img.size(), CV_16SC1);
    
  // ラベリングを実施 2値化した画像に対して実行する
  LabelingBS labeling;
  labeling.Exec( in_img.data,	      // 二値化画像
		 (short *)label.data, // ラベリング結果
		 in_img.cols, 
		 in_img.rows, 
		 true,		// sortするかどうか
		 min_pix);	// TODO: sort == falseにするとmin_pixが有効にならない?

  // ラベルされた領域をひとつずつ描画
  int num_label = labeling.GetNumOfRegions();
  for( int i=0; i<num_label; i++){

    // ラベリング結果の領域を抽出する
    cv::Mat labelarea;
    cv::compare(label, i+1, labelarea, CV_CMP_EQ);

    // 抽出結果の非ゼロ領域の座標を取得
    LabelData l;
    vector<cv::Point> points;
    
    for(int x=0; x<in_img.cols; x++){
      for(int y=0; y<in_img.rows; y++){
	int ind = x*in_img.rows + y; // 1次元
	if( labelarea.data[ind] != 0 ){
	  cv::Point pnt(x,y);
	  points.push_back(pnt);
	}
      }}

    // 重心座標の取得
    RegionInfoBS *ri;
    ri = labeling.GetResultRegionInfo(i);
    float x,y;
    ri->GetCenter(x, y);

    // ラベル構造体のvectorへ格納
    l.g = {(int)x, (int)y};
    l.ind = i;
    l.points = points;
    labels.push_back(l);

    
    // 抽出した領域にランダムな色を設定して出力画像に追加
    cv::Mat color(in_img.size(), CV_8UC3, randomColor());
    color.copyTo(out_img, labelarea);
  }
}


int main(int argc, char* argv[]){

  // 元画像の用意
  char* filename = argv[1];
  cv::Mat src_img = cv::imread(filename, 0);

  // 二値化
  cv::Mat bin_img;
  cv::threshold(src_img, bin_img, 0, 255, cv::THRESH_BINARY|cv::THRESH_OTSU);

  // ラベリング
  cv::Mat labeled_img;
  vector<LabelData> labels;
  Label(bin_img, labeled_img, labels, 2);

  // 結果の表示
  cv::imshow("bin_img", bin_img);
  cv::imshow("labeled_img", labeled_img);
  cv::waitKey(0);

  // 結果の保存
  cv::imwrite("binary.png", bin_img);
  cv::imwrite("labeled.png", labeled_img);

  return 0;
}

コンパイルは以下を実行(c++11じゃないとコケます)

$ g++ labeling.cpp -o labeling `pkg-config --libs opencv` `pkg-config --cflags opencv` -std=c++11

このラベリングクラスは4連結しか対応していないので、8連結できるものを探し中です。