一、二值图像分析
- 二值图像分析最常见的一个主要方式就是轮廓发现与轮廓分析
其中轮廓发现的目的是为轮廓分析做准备,
经过轮廓分析我们可以得到轮廓各种有用的属性信息。
二、边缘检测和轮廓检测的区别
- 边缘检测
主要是通过一些手段检测数字图像中明暗变化剧烈(即梯度变化比较大)像素点,偏向于图像中像素点的变化。如canny边缘检测,结果通常保存在和源图片一样尺寸和类型的边缘图中。 - 轮廓检测
指检测图像中的对象边界,更偏向于关注上层语义对象。
如OpenCV中的findContours()函数, 它会得到每一个轮廓并以点向量方式存储,除此也得到一个图像的拓扑信息,即一个轮廓的后一个轮廓、前一个轮廓、父轮廓和内嵌轮廓的索引编号。
三、轮廓发现与绘制
- 在OpenCV里面利用findContours()函数和drawContours()函数实现这一功能。
void FindContours(InputOutputArray image,
out Point[][] contours,
out HierarchyIndex[] hierarchy,
RetrievalModes mode,
ContourApproximationModes method,
Point? offset = null);
- FindContours
参数一: image,输入图像、八位单通道的,背景为黑色的二值图像。(一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像)
-
参数二:contours,输出轮廓图像。是一个向量,向量的每个元素都是一个轮廓。因此,这个向量的每个元素仍是一个向量。即:
vector<vector<Point> > contours;
-
参数三:hierarchy,输出各个轮廓的继承关系。hierarchy也是一个向量,长度和contours相等,每个元素和contours的元素对应。hierarchy的每个元素是一个包含四个整型数的向量。即:
vector<Vec4i> hierarchy;
参数四:mode,检测轮廓的方法。有四种方法:
RETR_EXTERNAL:只检测外轮廓。忽略轮廓内部的洞。
RETR_LIST:检测所有轮廓,但不建立继承(包含)关系。
RETR_TREE:检测所有轮廓,并且建立所有的继承(包含)关系。
RETR_CCOMP:检测所有轮廓,但是仅仅建立两层包含关系。参数五:method,每个轮廓的编码信息。也有四种(常用前两种)
CHAIN_APPROX_NONE:把轮廓上所有的点存储。
CHAIN_APPROX_SIMPLE:只存储轮廓上的拐点。
CHAIN_APPROX_TC89_L1,CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法参数六: Point,偏移量。默认为0
注意:该函数将白色区域当作前景物体。所以findContours()函数是黑色背景下找白色轮廓。
void DrawContours(InputOutputArray image,
IEnumerable<IEnumerable<Point>> contours,
int contourIdx, Scalar color,
int thickness = 1,
LineTypes lineType = LineTypes.Link8,
IEnumerable<HierarchyIndex> hierarchy = null,
int maxLevel = int.MaxValue,
Point? offset = null);
- DrawContours()函数
- contours 全部发现的轮廓对象
- contourIdx 轮廓索引号,-1表示绘制所有轮廓
- MaxValue最大层数, 0只绘制当前的,1表示绘制绘制当前及其内嵌的轮廓
- Point? offset 轮廓位移,可选
四、轮廓分析(二值图像分析)
- 在得到图像的轮廓以后,我们就可以进行轮廓分析。
经过轮廓分析我们可以得到轮廓各种有用的属性信息、常见的如下:- 1、轮廓的矩
- 2、轮廓的面积
- 3、轮廓的长度
- 4、轮廓的近似多边形
- 5、轮廓的凸包
- 6、轮廓的直边界矩形
- 7、轮廓的旋转矩形
- 8、轮廓的最小外包圆
- 9、轮廓的拟合椭圆
- 10、轮廓的拟合直线
- 11、轮廓的最小外包三角形
五、程序
private void uiButton7_Click(object sender, EventArgs e)
{
Mat result = src_img.Clone();
Mat m7 = new Mat();
double threshold1 = 10;
double threshold2 = 200;
int select_i = 0;
Cv2.Canny(dst, m7, threshold1, threshold2);
OpenCvSharp.Point[][] contours; //vector<vector<Point>> contours;
HierarchyIndex[] hierarchyIndexes; //vector<Vec4i> hierarchy;
Cv2.FindContours(m7, out contours, out hierarchyIndexes, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
for (int i = 0; i < contours.Length; i++)
{
double length = Cv2.ArcLength(contours[i], true);
if (length >= 500)
{
select_i = i;
Cv2.DrawContours(result, contours, i, new Scalar(0, 255, 255), 2);
Cv2.PutText(result, "i=" + i.ToString(), new OpenCvSharp.Point(20, 120), OpenCvSharp.HersheyFonts.HersheySimplex, 1, new Scalar(0, 255, 255));
double area = Cv2.ContourArea(contours[i], false);
Cv2.PutText(result, "length=" + length.ToString(), new OpenCvSharp.Point(20, 160), OpenCvSharp.HersheyFonts.HersheySimplex, 1, new Scalar(0, 255, 255));
Cv2.PutText(result, "area=" + area.ToString(), new OpenCvSharp.Point(20, 200), OpenCvSharp.HersheyFonts.HersheySimplex, 1, new Scalar(0, 255, 255));
RotatedRect rect=Cv2.MinAreaRect(contours[0]);
Cv2.Rectangle(result, rect.BoundingRect(), new Scalar(0, 0, 255), 2, LineTypes.Link8,0);
Cv2.PutText(result, "width=" + rect.Size.Width.ToString(), new OpenCvSharp.Point(20, 240), OpenCvSharp.HersheyFonts.HersheySimplex, 1, new Scalar(0, 255, 255));
Cv2.PutText(result, "height=" + rect.Size.Height.ToString(), new OpenCvSharp.Point(20, 280), OpenCvSharp.HersheyFonts.HersheySimplex, 1, new Scalar(0, 255, 255));
}
}
pictureBox1.Image = result.ToBitmap();
}
六、资料
「明月清风_@」的博客:
https://blog.csdn.net/qq_44386034/article/details/125637891