【机器视觉学习笔记】二值图像连通区域提取算法(C++)

【机器视觉学习笔记】⼆值图像连通区域提取算法(C++)
⽬录
平台:Windows 10 20H2
Visual Studio 2015
OpenCV 4.5.3
本⽂摘⾃
原理
⼆值图像
顾名思义就是图像的亮度值只有两个状态:⿊(0)和⽩(255)。⼆值图像在图像分析与识别中有着举⾜轻重的地位,因为其模式简单,对像素在空间上的关系有着极强的表现⼒。在实际应⽤中,很多图像的分析最终都转换为⼆值图像的分析,⽐如:医学图像分析、前景检测、字符识别,形状识别。⼆值化+数学形态学能解决很多计算机识别⼯程中⽬标提取的问题。
⼆值图像分析最重要的⽅法就是连通区域标记,它是所有⼆值图像分析的基础,它通过对⼆值图像中⽩⾊像素(⽬标)的标记,让每个单独的连通区域形成⼀个被标识的块,进⼀步的我们就可以获取这些块的轮廓、外接矩形、质⼼、不变矩等⼏何参数。
连通区域(Connected Component)
⼀般是指图像中具有相同像素值且位置相邻的前景像素点组成的图像区域(Region,Blob)。
在图像中,最⼩的单位是像素,每个像素周围有8个邻接像素,常见的邻接关系有2种:4邻接与8邻接。4邻接⼀共4个点,即上下左右,如下左图所⽰。8邻接的点⼀共有8个,包括了对⾓线位置的点,如下右图所⽰。
如果像素点A与B邻接,我们称A与B连通,于是我们不加证明的有如下的结论:
如果A与B连通,B与C连通,则A与C连通。
在视觉上看来,彼此连通的点形成了⼀个区域,⽽不连通的点形成了不同的区域。这样的⼀个所有的点彼此连通点构成的集合,我们称为⼀个连通区域。
下⾯这符图中,如果考虑4邻接,则有3个连通区域;如果考虑8邻接,则有2个连通区域。(注:图像是被放⼤的效果,图像正⽅形实际只有4个像素)
连通区域分析(Connected Component Analysis,Connected Component Labeling)
是指将图像中的各个连通区域出并标记。
连通区域分析是⼀种在CVPR和图像分析处理的众多应⽤领域中较为常⽤和基本的⽅法。例如:
OCR识别中字符分割提取(车牌识别、⽂本识别、字幕识别等)、
视觉跟踪中的运动前景⽬标分割与提取(⾏⼈⼊侵检测、遗留物体检测、基于视觉的车辆检测与跟踪等)、
医学图像处理(感兴趣⽬标区域提取)等等。
也就是说,在需要将前景⽬标提取出来以便后续进⾏处理的应⽤场景中都能够⽤到连通区域分析⽅法,通常连通区域分析处理的对象是⼀张⼆值化后的图像。
算法:Two-Pass(两遍扫描法)
从连通区域的定义可以知道,⼀个连通区域是由具有相同像素值的相邻像素组成像素集合,因此,我们就可以通过这两个条件在图像中寻连通区域,对于到的每个连通区域,我们赋予其⼀个唯⼀的标识(Label),以区别其他连通区域。
两遍扫描法指的就是通过扫描两遍图像,就可以将图像中存在的所有连通区域出并标记。
思路:
第⼀遍扫描时赋予每个像素位置⼀个label,扫描过程中同⼀个连通区域内的像素集合中可能会被赋予⼀个或多个不同label,因此需要将这些属于同⼀个连通区域但具有不同值的label合并,也就是记录它们之间的相等关系;
第⼆遍扫描就是将具有相等关系的equal_labels所标记的像素归为⼀个连通区域并赋予⼀个相同的label(通常这个label是equal_labels中的最⼩值)。
Two-Pass算法的简单步骤:
1)第⼀次扫描:
访问当前像素B(x,y),如果B(x,y) == 1:
a、如果B(x,y)的领域中像素值都为0,则赋予B(x,y)⼀个新的label:
label += 1, B(x,y) = label;
b、如果B(x,y)的领域中有像素值 > 1的像素Neighbors:
(1)将Neighbors中的最⼩值赋予给B(x,y): B(x,y) = min{Neighbors}
(2)记录Neighbors中各个值(label)之间的相等关系,即这些值(label)同属同⼀个连通区域;
labelSet[i] = { label_m, …, label_n },labelSet[i]中的所有label都属于同⼀个连通区域(注:这⾥可以有多种实现⽅式,只要能够记录这些具有相等关系的label之间的关系即可)
2)第⼆次扫描:
访问当前像素B(x,y),如果B(x,y) > 1:
a、到与label = B(x,y)同属相等关系的⼀个最⼩label值,赋予给B(x,y);
完成扫描后,图像中具有相同label值的像素就组成了同⼀个连通区域。
下⾯这张图动态地演⽰了Two-pass算法:
源码
#include<iostream>
#include"opencv2/opencv.hpp"
using namespace std;
using namespace cv;
//------------------------------【两步法新改进版】----------------------------------------------
// 对⼆值图像进⾏连通区域标记,从1开始标号
void Two_PassNew(const Mat &bwImg, Mat &labImg)
{
pe()== CV_8UC1);
labImg =Scalar(0);
labImg.setTo(Scalar(1), bwImg);
assert(labImg.isContinuous());
const int Rows = ws -1, Cols = ls -1;
int label =1;
vector<int> labelSet;
labelSet.push_back(0);
labelSet.push_back(1);
/
/the first pass
int*data_prev =(int*)labImg.data;//0-th row : int* data_prev = labImg.ptr<int>(i-1);
int*data_cur =(int*)(labImg.data + labImg.step);//1-st row : int* data_cur = labImg.ptr<int>(i); for(int i =1; i < Rows; i++)
{
data_cur++;
data_prev++;
for(int j =1; j<Cols; j++, data_cur++, data_prev++)
{木材拉丝机
if(*data_cur !=1)
continue;
int left =*(data_cur -1);
int up =*data_prev;
int neighborLabels[2];
数字高清网络摄像机int cnt =0;
if(left>1)
neighborLabels[cnt++]= left;
if(up >1)
neighborLabels[cnt++]= up;
if(!cnt)
{
labelSet.push_back(++label);
连通区域
labelSet[label]= label;
*data_cur = label;
continue;
}
int smallestLabel = neighborLabels[0];
if(cnt ==2&& neighborLabels[1]<smallestLabel)
smallestLabel = neighborLabels[1];
*data_cur = smallestLabel;
// 保存最⼩等价表
for(int k =0; k<cnt; k++)
{
int tempLabel = neighborLabels[k];
int& oldSmallestLabel = labelSet[tempLabel];//这⾥的&不是取地址符号,⽽是引⽤符号
if(oldSmallestLabel > smallestLabel)
if(oldSmallestLabel > smallestLabel)
{
labelSet[oldSmallestLabel]= smallestLabel;
oldSmallestLabel = smallestLabel;
}
else if(oldSmallestLabel<smallestLabel)
labelSet[smallestLabel]= oldSmallestLabel;
}
}
data_cur++;
data_prev++;
}
//更新等价队列表,将最⼩标号给重复区域
for(size_t i =2; i < labelSet.size(); i++)
{
int curLabel = labelSet[i];
int prelabel = labelSet[curLabel];
while(prelabel != curLabel)
{
curLabel = prelabel;
prelabel = labelSet[prelabel];
}
labelSet[i]= curLabel;
}
//second pass
data_cur =(int*)labImg.data;
for(int i =0; i < Rows; i++)
{
for(int j =0; j < ls -1; j++, data_cur++)
*data_cur = labelSet[*data_cur];
data_cur++;
}
}
//---------------------------------【颜⾊标记程序】-----------------------------------//彩⾊显⽰
cv::Scalar GetRandomColor()
{
uchar r =255*(rand()/(1.0+ RAND_MAX));
uchar g =255*(rand()/(1.0+ RAND_MAX));
uchar b =255*(rand()/(1.0+ RAND_MAX));
return cv::Scalar(b, g, r);
}
void LabelColor(const cv::Mat& labelImg, cv::Mat& colorLabelImg)
{
int num =0;
pty()||
{
return;
}
std::map<int, cv::Scalar> colors;
int rows = ws;
int cols = ls;
colorLabelImg = cv::Scalar::all(0);
for(int i =0; i < rows; i++)
{
const int* data_src =(int*)labelImg.ptr<int>(i);
const int* data_src =(int*)labelImg.ptr<int>(i);
uchar* data_dst = colorLabelImg.ptr<uchar>(i);
for(int j =0; j < cols; j++)
{
int pixelValue = data_src[j];
if(pixelValue >1)
{
unt(pixelValue)<=0)
{
colors[pixelValue]=GetRandomColor();
num++;
}
cv::Scalar color = colors[pixelValue];
*data_dst++= color[0];
*data_dst++= color[1];
*data_dst++= color[2];
}
else
{
data_dst++;
气体收集data_dst++;胎毛画
data_dst++;
}
}
}
printf("color num : %d \n", num);
}
//------------------------------------------【测试主程序】-------------------------------------
int main()
{
cv::Mat binImage = cv::imread("D:\\Work\\OpenCV\\Workplace\\Test_1\\Test.jpg",0); cv::threshold(binImage, binImage,127,255, THRESH_BINARY);
cv::imshow("原图", binImage);
cv::Mat labelImg;
double time;
time =getTickCount();
Two_PassNew(binImage, labelImg);
time =1000*((double)getTickCount()- time)/getTickFrequency();
cout << std::fixed << time <<"ms"<< endl;
//彩⾊显⽰
cv::Mat colorLabelImg;
LabelColor(labelImg, colorLabelImg);
cv::imshow("colorImg", colorLabelImg);
double minval, maxval;
minMaxLoc(labelImg,&minval,&maxval);
幼儿园门禁cout <<"minval"<< minval << endl;
cout <<"maxval"<< maxval << endl;
cv::waitKey(0);
return0;
}
效果
⽰例⼀

本文发布于:2024-09-24 10:16:19,感谢您对本站的认可!

本文链接:https://www.17tex.com/tex/4/349007.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:图像   区域   像素   识别   前景   提取
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议