二值图像分析时黑色(0)为背景,白色(非0)为前景
对于任意像素(x,y),把像素的集合{(x+p,y+q)}(p、q是一对适当的整数)叫做像素(x,y)的邻域。即像素(x,y)附近的像素形成的区域。
邻域是指某像素p的周围像素,p与他们间的欧式距离不超过“根号2”即对角线的距离。如果q在p的某种邻域中,则p,q为某种邻接。
4-邻域:即对于像素(x,y),上下左右4个像素称为4-邻域,即以下元素:(x-1,y)(x,y-1)(x+1,y)(x,y+1)。
D-邻域:即对于像素(x,y),其中(max-1>x>1,max-1>y>1)。其D-邻域为以下元素:(x-1,y-1)(x+1,y-1)(x-1,y+1)(x+1,y+1)。
8-邻域:即对于像素(x,y),其中(max-1>x>1,max-1>y>1),上下左右4个元素以及4个对角线像素,称为8-邻域。为以下元素:(x-1,y-1)(x-1,y)(x-1,y+1)(x,y-1)(x,y+1)(x+1,y-1)(x+1,y)(x+1,y+1)
二值图像的邻接
4-邻接:如果q点在p点的4-邻域集合中,则p,q为4-邻接。即互为4-邻域的两个像素叫4-邻接。
8-邻接:如果q点在p点的8-邻域集合中,则p,q为8-邻接。即互为8-邻域的两个像素叫8-邻接。
D-邻接:如果q点在p点的D-邻域集合中,则p,q为D-邻接
m-邻接:如果q在p的4-邻域中,或者q在p的D-邻域中,且q的4-邻域与p的4-邻域交集为空,则p,q为m-邻接。
m-邻域的目的是消除8-邻域的二义性。
联通组件:像素值相同,通过4-领域或8-邻域相互连通的像素块。
联通组件分析过程:
(图片来源于https://edu.csdn.net/learn/38286/608274)
(左图)第一行:从左到右,从上到下,扫描白色(前景),像素(0,0)、(0,1)标记为联通块1;(0,2)不联通;(0,3)、(0,4)、(0,5)为新的联通块标为2;(0,6)不通道;(0,7)为新的联通块标为3;
(左图)第二行:(1,0)、(1,1)与上一行联通也标为1,依次类推。
(右图)等价类合并:高标识符与低标识符相联时,将高标识符合为低标识符,2合并为1,6合并为4,7合并为3
//函数原型1
int ConnectedComponents(InputArray image,
OutputArray labels,
PixelConnectivity connectivity = PixelConnectivity.Connectivity8)
//函数原型2
int ConnectedComponents(InputArray image,
out int[,] labels,
PixelConnectivity connectivity)
//函数原型3
int ConnectedComponents(InputArray image,
OutputArray labels,
PixelConnectivity connectivity,
MatType ltype)
ConnectedComponents获取不带统计信息的联通组件 |
|
返回值(int):返回标签总数N,[0,N-1],0为背景 |
|
参数 |
说明 |
InputArray image |
待标记图像:8位单通道,黑色背景 |
OutputArray labels |
输出标记图像:与输入图像大小一样,对应的联通区域会标记上对应的数字 0为背景,其它数字为对应的联通区域 |
PixelConnectivity connectivity |
通联域:默认是8联通 |
MatType ltype |
输出标记图像的类型:默认是CV_32S,还支持CV_16U |
//函数原型1
int ConnectedComponentsWithStats(InputArray image,
OutputArray labels,
OutputArray stats,
OutputArray centroids,
PixelConnectivity connectivity,
MatType ltype)
//函数原型2(ltype=CV_32S)
int ConnectedComponentsWithStats(InputArray image,
OutputArray labels,
OutputArray stats,
OutputArray centroids,
PixelConnectivity connectivity = PixelConnectivity.Connectivity8)
ConnectedComponentsWithStats获取带统计信息的通道组件 |
|
返回值(int):返回标签总数N,[0,N-1],0为背景 |
|
参数 |
说明 |
InputArray image |
待标记图像:8位单通道,黑色背景 |
OutputArray labels |
输出标记图像:与输入图像大小一样,对应的联通区域会标记上对应的数字 0为背景,其它数字为对应的联通区域 |
OutputArray stats |
各通道组件信息(含背景):N*5*ltype 对应各个通道组件的外接矩形的x,y,width,height和像素点个数 |
OutputArray centroids |
通道组件中心点位置:N*2*CV_64F |
PixelConnectivity connectivity |
通联域:默认是8联通 |
MatType ltype |
输出标记图像的类型:默认是CV_32S,还支持CV_16U |
//函数原型,多了ccltype参数,可指定通道组件分析算法,其它与ConnectedComponents相同
int ConnectedComponentsWithAlgorithm(InputArray image,
OutputArray labels,
PixelConnectivity connectivity,
MatType ltype,
ConnectedComponentsAlgorithmsTypes ccltype)
//函数原型,多了ccltype参数,可指定通道组件分析算法,其它与ConnectedComponentsWithStats相同
int ConnectedComponentsWithStatsWithAlgorithm(InputArray image,
OutputArray labels,
OutputArray stats,
OutputArray centroids,
PixelConnectivity connectivity,
MatType ltype,
ConnectedComponentsAlgorithmsTypes ccltype)
该函数是OpenCvSharp特有的?使用ConnectedComponents的Blob属性访问更便捷。
//函数源码
public static ConnectedComponents ConnectedComponentsEx(InputArray image,
PixelConnectivity connectivity = PixelConnectivity.Connectivity8,
ConnectedComponentsAlgorithmsTypes ccltype = ConnectedComponentsAlgorithmsTypes.Default)
{
using Mat mat = new Mat();
using Mat mat2 = new Mat();
using Mat mat3 = new Mat();
int num = ConnectedComponentsWithStatsWithAlgorithm(image, mat, mat2, mat3, connectivity, 4, ccltype);
int[,] labels = mat.ToRectangularArray();
int[,] array = mat2.ToRectangularArray();
double[,] array2 = mat3.ToRectangularArray();
ConnectedComponents.Blob[] array3 = new ConnectedComponents.Blob[num];
for (int i = 0; i < num; i++)
{
array3[i] = new ConnectedComponents.Blob
{
Label = i,
Left = array[i, 0],
Top = array[i, 1],
Width = array[i, 2],
Height = array[i, 3],
Area = array[i, 4],
Centroid = new Point2d(array2[i, 0], array2[i, 1])
};
}
return new ConnectedComponents(array3, labels, num);
}
public void Run(ParamBase paramBase)
{
using var srcGray = Cv2.ImRead(ImagePath.Shapes, ImreadModes.Grayscale);
if (srcGray.Empty()) throw new Exception("图像读取有误");
Cv2.ImShow("srcGray", srcGray);
//需要去除噪声的话,可应用相关的模糊函数
Cv2.GaussianBlur(srcGray, srcGray, new Size(3, 3), 0);
//用大律法二值化
Cv2.Threshold(srcGray, srcGray, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.Binary);
#region ConnectedComponents相关
//通道组件标记
using var labels = new Mat();
//componentsCount=实际联通区域个数+1(背景)
//labels大小与srcGray,在相同的联通区域标记为对应的数字(1至N-1)
var componentsCount = Cv2.ConnectedComponents(srcGray, labels, PixelConnectivity.Connectivity8, MatType.CV_32S);
//Cv2.ConnectedComponentsWithAlgorithm
//生成随机颜色填充颜色
List colors = new List();
for (int i = 0; i < componentsCount; i++)
{
colors.Add(Scalar.RandomColor().ToVec3b());
}
//用于绘制结果
Mat colorResult = Mat.Zeros(labels.Size(), MatType.CV_8UC3);
for (int row = 0; row < labels.Rows; row++)
{
for (int col = 0; col < labels.Cols; col++)
{
//注意类型必须与labels.MatType一致
var color = labels.At(row, col);//CV_32S
//var color = labels.At(row, col);//CV_16U
if (color == 0) continue;//为0是背景
colorResult.At(row, col) = colors[color];
}
}
Cv2.ImShow("ColorResult:ConnectedComponents", colorResult);
#endregion
#region ConnectedComponentsWithStats相关
//各通道组件外接矩形信息(含背景)
using var stats = new Mat();
//各通道组件中心点信息(含背景)
using var centroids = new Mat();
componentsCount = Cv2.ConnectedComponentsWithStats(srcGray, labels, stats, centroids, PixelConnectivity.Connectivity8, MatType.CV_32S);
//Cv2.ConnectedComponentsWithStatsWithAlgorithm
colorResult = srcGray.CvtColor(ColorConversionCodes.GRAY2BGR);
for (int i = 1; i < componentsCount; i++)
{
//通道组件的中心点
Cv2.Circle(colorResult, new Point(centroids.At(i, 0), centroids.At(i, 1)), 2, Scalar.Blue, -1);
//通道组件的外接矩形
Cv2.Rectangle(colorResult, new Rect(stats.At(i, 0), stats.At(i, 1), stats.At(i, 2), stats.At(i, 3)), Scalar.Red);
}
Cv2.ImShow("ColorResult:ConnectedComponentsWithStats", colorResult);
#endregion
#region ConnectedComponentsEx对ConnectedComponentsWithStatsWithAlgorithm封装
var componentsEx = Cv2.ConnectedComponentsEx(srcGray, PixelConnectivity.Connectivity8, ConnectedComponentsAlgorithmsTypes.Default);
colorResult = srcGray.CvtColor(ColorConversionCodes.GRAY2BGR);
foreach (var blob in componentsEx.Blobs)
{
//背景跳过
if (blob.Label == 0) continue;
//通道组件的中心点
Cv2.Circle(colorResult, blob.Centroid.ToPoint(), 2, Scalar.Blue, -1);
//通道组件的外接矩形
Cv2.Rectangle(colorResult, blob.Rect, Scalar.Red);
}
Cv2.ImShow("ColorResult:ConnectedComponentsEx", colorResult);
#endregion
Cv2.WaitKey();
Cv2.DestroyAllWindows();
}
OpenCvSharp函数示例(目录)
参考
https://edu.csdn.net/learn/38286/608274
https://docs.opencv.org/