FindContours轮廓发现
//函数原型1
void FindContours(InputArray image,
out Mat[] contours,
OutputArray hierarchy,
RetrievalModes mode,
ContourApproximationModes method,
Point? offset = null)
//函数原型2
void FindContours(InputArray image,
out Point[][] contours,
out HierarchyIndex[] hierarchy,
RetrievalModes mode,
ContourApproximationModes method,
Point? offset = null)
FindContours轮廓发现,在二值图中查找轮廓 | |
参数 | 说明 |
InputArray image | 输入图像:8位单通道,非0像素当1处理 可处理Compare,InRange,Threshold,AdaptiveThreshold,Canny等生成的二值图。如果mode为CComp或FloodFill,也可以为CV_32SC1 |
out Mat[] contours | 检测到的轮廓数组:返回N个轮廓mat(各轮廓坐标个数*1*CV_32SC2) |
out Point[][] contours | 检测到的轮廓坐标数组:返回N个轮廓坐标数组(与上面的表示形式不同而已) |
OutputArray hierarchy | 轮廓的层次关系:返回1*N*CV_32SC4的mat,对应每个轮廓的, hierarchy[i][0],hierarchy[i][1],hierarchy[i][2],hierarchy[i][3]分别对应该轮廓的Next同层下一个轮廓索引,Previous同层上一个轮廓索引,Child下层第一个孩子轮廓索引,Parent父层轮廓索引,负数表示没有相应元素。 |
out HierarchyIndex[] hierarchy | 轮廓的层次关系:HierarchyIndex有Next,Previous,Child,Parent属性可直接访问 |
RetrievalModes mode | 轮廓检索模式:影响轮廓个数、层次关系 |
ContourApproximationModes method | 轮廓坐标近似模式:影响各轮廓坐标个数 |
Point? offset | 轮廓整体偏移量:如在ROI区域进行轮廓识别后,加上ROI左上角的偏移量后,最终结果即为整图对应的轮廓。 |
RetrievalModes mode轮廓检索模式 | |
External | 仅检索最外层轮廓,hierarchy[i][2]=hierarchy[i][3]=-1 |
List | 检索所有轮廓,但不包含层级关系,hierarchy[i][2]=hierarchy[i][3]=-1 |
CComp | 检索所有轮廓,包含两层层级关系。顶层为最外轮廓,第二层为孔洞轮廓。如果有轮廓在孔洞内,它也设为顶层(Parent=-1) |
Tree | 检索所有轮廓,包含完整的层次关系。与CComp不同的是,孔内的轮廓的Parent为对应的孔 |
FloodFill | 检索所有轮廓,使用FloodFill算法,其它与CComp相同,输入图像需为CV_32SC1类型 |
ContourApproximationModes method 轮廓坐标近似模式 | |
参数 | 说明 |
ApproxNone | 记录轮廓上的所有点 |
ApproxSimple | 压缩记录,只记录垂直、水平、对角线段的结束点 |
ApproxTC89L1 | 应用Teh-Chin链近似算法中的一种 |
ApproxTC89KCOS | 应用Teh-Chin链近似算法中的一种 |
原灰度图
各参数图例对比
下面图例对比RetrievalModes和ContourApproximationModes不同时的不同结果
窗口标题是对应的RetrievalModes、ContourApproximationModes取值及总轮廓数
轮廓左上角的文字格式说明
第一个数字:表示当前轮廓序号
Next:下一个轮廓序号
Pre:Previous前一个轮廓序号
Child:子轮廓序号
Pa:Parent:父轮廓序号
Count:该轮廓包含的坐标个数
外轮廓为坐标为逆针,内轮廓坐标为顺时针
RetrievalModes为External时
只有外轮廓,没有层次关系(Child,Parent为-1)
RetrievalModes为List时
内、外轮廓都有,无层次关系
RetrievalModes为CComp时
内、外轮廓都有,也有层次关系,注意孔内轮廓3的Parent为轮廓2,轮廓2的Parent为-1(即也认为是顶层)
RetrievalModes为Tree时
内、外轮廓都有,完整的树形层次关系。注意轮廓9(对应上图的轮廓3)的Parent为轮廓8(对应上图的轮廓2),轮廓8的Parent为轮廓7。
RetrievalModes为FloodFill时
加了offset=(5,15),
DrawContours绘制轮廓
//函数原型1
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)
//函数原型2
void DrawContours(InputOutputArray image,
IEnumerable<Mat> contours,
int contourIdx,
Scalar color,
int thickness = 1,
LineTypes lineType = LineTypes.Link8,
Mat? hierarchy = null,
int maxLevel = int.MaxValue,
Point? offset = null)
DrawContours绘制轮廓 | |
InputOutputArray image | 待绘制轮廓的图像 |
IEnumerable<IEnumerable<Point>> contours IEnumerable<Mat> contours | 各轮廓坐标数据 |
int contourIdx | 待绘制轮廓的序号,负数表示绘制所有轮廓 |
Scalar color | 待绘制轮廓线的颜色 |
int thickness = 1 | 待绘制轮廓线的宽度,负数表示填充轮廓内部 |
LineTypes lineType = LineTypes.Link8 | 轮廓线类型 |
IEnumerable<HierarchyIndex>? hierarchy = null Mat? hierarchy = null | 轮廓层次关系,绘制指定轮廓时可能要用到,影响maxLevel |
int maxLevel = int.MaxValue | 最大绘制轮廓级数:为0时,只绘制指定序号的轮廓;为1时指定序号及子轮廓;为2时指定序号及子轮廓和子轮廓的子轮廓。依次类推。 |
Point? offset = null | 轮廓偏移量:轮廓坐标+偏移量为实际绘制坐标 |
contourIdx和maxLevel参数图例
测试图像
contourIdx和maxLevel分别为0,1,3,6时的示例(轮廓检索方式为Tree)
源码示例
public void Run()
{
using var srcGray = Cv2.ImRead(ImagePath.Contours, 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.BinaryInv);
//轮廓偏移量
var offset = new Point(5, 15);
//测试各种RetrievalModes
foreach (RetrievalModes mode in Enum.GetValues(typeof(RetrievalModes)))
{
//记录窗口时,为了自动排列窗口用
List<string> winNames = new List<string>();
//测试各种ContourApproximationModes
foreach (ContourApproximationModes caMode in Enum.GetValues(typeof(ContourApproximationModes)))
{
try
{
Point[][] contoursP;
HierarchyIndex[] hierarchyI;
if (mode== RetrievalModes.FloodFill)
{
using var floodFileMat = new Mat();
//用FloodFill方式时,需转为CV_32SC1
srcGray.ConvertTo(floodFileMat, MatType.CV_32SC1);
Cv2.FindContours(floodFileMat, out contoursP, out hierarchyI, mode, caMode, offset);
}
else
{
//检索轮廓
Cv2.FindContours(srcGray, out contoursP, out hierarchyI, mode, caMode);
}
//另一种调用方式
//FindContours(InputArray image, out Mat[] contours, OutputArray hierarchy, RetrievalModes mode,
// ContourApproximationModes method, Point? offset = null)
using var result = srcGray.CvtColor(ColorConversionCodes.GRAY2BGR);
//绘制所有轮廓
Cv2.DrawContours(result, contoursP, -1, Scalar.Red, 2);
/*
int index = 0;
foreach (Point[] points in contoursP)
{
var h = hierarchyI[index];
//在轮廓左上角加白底黑字提示文字
var textPoint = new Point(points[0].X - 30, points[0].Y - 12);
Helper.PutTextWithBack(result, $"{index},Next:{h.Next},Pre:{h.Previous},Child:{h.Child},Pa:{h.Parent},Count:{points.Count()}",
textPoint, fontScale: 0.3);
//文字与轮廓连一条线
Cv2.Line(result, points[0], textPoint, Scalar.Green, 1);
index++;
}*/
var winName = $"RetrievalModes:{mode},ContourApproximationModes:{caMode},contoursCount:{contoursP.Count()}";
if (mode == RetrievalModes.FloodFill)
{//测试参数offset
result.PutText($"offset=({offset.X},{offset.Y})", offset, HersheyFonts.HersheySimplex, 0.3, Scalar.Red);
}
Cv2.ImShow(winName, result);
winNames.Add(winName);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
//窗口对齐
//Helper.ArrangeWins(winNames, 2);
Cv2.WaitKey();
Cv2.DestroyAllWindows();
}
//用于测试多层次轮廓图像
using var nContours = Cv2.ImRead(ImagePath.mContours, ImreadModes.Grayscale);
Cv2.Threshold(nContours, nContours, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.BinaryInv);
//检索轮廓
Cv2.FindContours(nContours, out Point[][] contoursPs, out HierarchyIndex[] hierarchyIs, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple);
for (int contourIdx = 0; contourIdx < contoursPs.Length; contourIdx++)
{
using var nContoursMat = nContours.CvtColor(ColorConversionCodes.GRAY2BGR);
//绘制轮廓,测试contourIdx和maxLevel
Cv2.DrawContours(nContoursMat, contoursPs, contourIdx, Scalar.Red, 2, hierarchy: hierarchyIs, maxLevel: contourIdx);
//var textPoint = contoursPs[contourIdx][0];
//var h = hierarchyIs[contourIdx];
//Helper.PutTextWithBack(nContoursMat, $"{contourIdx},Next:{h.Next},Pre:{h.Previous},Child:{h.Child},Pa:{h.Parent}",
// textPoint, fontScale: 0.3);
Cv2.ImShow($"contourIdx={contourIdx},maxLevel={contourIdx}", nContoursMat);
}
Cv2.WaitKey();
Cv2.DestroyAllWindows();
}
参考