OpenCV Matに基づく画像連結領域ラベリング処理

序文

OpenCVには独自に接続ドメインを検索する機能があるようですが、私が使っているバージョンにはその機能がないので、
接続ドメインを検索するにはorzと書く必要があります。

Two-Pass(2パス走査方式)

種子の充填

このブロガーのブログにアクセスして、2 つのアルゴリズムの原理を確認してください。

実装プロセス

この記事では、2 番目の方法であるシード充填法を使用する必要があります。

  1. パラメータ マットと入力 説明: これはバイナリ イメージです。値は 0 または 255 です。0 (黒) は背景領域を表し、255 (白) はマテリアル領域を表します。
  2. 関数 labelmark は、2 つの for ループ層を使用して Mat 入力行列を走査します。
    内側のループには 2 つのシリアル if ステートメント ブロックがあります。
// 第一个防止 fill_4 函数递归次数太大,内存不够。
 if((input.at<unsigned char>(i, j) != 255 && input.at<unsigned char>(i, j) != 0))
//(2)第二个就是实现四邻域的递归判断
 if (input.at<unsigned char>(i,j)==255)
  1. int labelnum;
    ラベル値。テスト時の区別を容易にするため、初期値は 1 であり、毎回 10 ずつ増加します。
  2. input.at(i, j) = labelnum + 50;
    50 を足すのはグレーの値を少し大きくするためでもあるので、問題ありません。
  3. コードの残りの部分はコメントで十分です。

コード

void fill_4(int x, int y, int count, Mat& input, int & jishu) {
    
    
	//cout << "x = "<< x << ", y = " << y <<", count = "<< count << endl;
	jishu++;
	if (x-1<=0 || x+1 >=319 || y-1<=0 ||y+1>=319)	// 没有考虑四边,偷懒先把四边就设置为0了。
	{
    
    
		return;
	}
	if (input.at<unsigned char>(x-1, y) == 255) {
    
    
		input.at<unsigned char>(x-1, y) = count + 50;
		if (jishu == 2000)
		{
    
    
			return;
		}
		fill_4(x-1, y, count, input, jishu);
	}
	if (input.at<unsigned char>(x, y-1) == 255) {
    
    
		input.at<unsigned char>(x, y-1) = count + 50;
		if (jishu == 2000)
		{
    
    
			return;
		}
		fill_4(x, y-1, count, input, jishu);
	}
	if (input.at<unsigned char>(x+1, y) == 255) {
    
    
		input.at<unsigned char>(x + 1, y) = count + 50;
		if (jishu == 2000)
		{
    
    
			return;
		}
		fill_4(x+1, y, count, input,  jishu);
	}
	if (input.at<unsigned char>(x, y+1) == 255) {
    
    
		input.at<unsigned char>(x, y+1) = count + 50;
		if (jishu == 2000)
		{
    
    
			return;
		}
		fill_4(x, y+1, count, input,  jishu);
	}
	return;
}
int labelmark(Mat& input) {
    
    
	// 用四邻域,八邻域斜角情况较复杂
	/*
		按行遍历元素:
			如果值=0,则跳过;
			如果值=255,则判断其上、右两个邻域是否
	*/
	int temp = 0;
	int jishu = 0;	// 记录递归次数,本函数在本机上,测试的最大可递归次数为2089,本函数中设置为2000,jishu==2000则结束递归。
	int labelnum = 1;
	for (int i = 1; i < input.rows-1; i++)
	{
    
    
		for (int j = 1; j < input.cols-1; j++)
		{
    
    
			// 这里是找到递归超限时添加的代码,没删,记录一下提醒自己
			//cout << "这是for循环: " << i << ", " << j << endl;
			//if (labelnum == 17 && input.at<unsigned char>(i, j) == 255) {
    
    
			//	// cout << "这是for循环: " << i << ", " << j << endl;
			//	//input.at<unsigned char>(i, j) = 0;
			//	cout << jishu << endl;
			//	jishu++;
			//	continue;
			//}

			if (input.at<unsigned char>(i, j) != 255 && input.at<unsigned char>(i, j) != 0)	// 已经被标记过的
			{
    
    
				// 判断该像素四邻域是否存在值=255的,如果有,则将label变为当前像素值,再次进行递归
				if (input.at<unsigned char>(i-1, j) == 255
					|| input.at<unsigned char>(i, j - 1) == 255
					|| input.at<unsigned char>(i + 1, j) == 255
					|| input.at<unsigned char>(i, j + 1) == 255)
				{
    
    
					temp = input.at<unsigned char>(i, j)-50;	// -50别忘了
					fill_4(i, j, temp, input, jishu);
					jishu = 0;
				}
				
			}
			if (input.at<unsigned char>(i,j)==255)
			{
    
    
				input.at<unsigned char>(i, j) = labelnum + 50;
				fill_4(i, j, labelnum, input,  jishu);
				labelnum+=10;
				jishu = 0;
				break;
			}
		}
	}
	return (labelnum - 1) / 10 + 1;
}

結果

とにかく、正解でした…
実験で使用した写真は論文に使用される場合があるので、当分公開できません(良いこと考えてくださいorz)

補充する

これは私が使用しているライブラリです。バージョン 2、バージョン 3 には **connectedComponents()** 接続ドメイン解決関数が付属しています。嫌いだ!
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/Chauncyxu/article/details/118167553