OpenCV笔记(7) 轮廓
1. 查找轮廓 FindContours
public static void FindContours(InputOutputArray image, //输入8-bit单通道的图片
out Mat[] contours, //一组数组,contours[i]是一条轮廓,而contours[i][j]是点
OutputArray hierarchy, //输出一个数组,表示轮廓的树结构,每个值都是四元数组
RetrievalModes mode, //轮廓提取方式
ContourApproximationModes method, //轮廓如何被表达
Point? offset = null); public static void FindContours(InputOutputArray image,
out Point[][] contours,
out HierarchyIndex[] hierarchy,
RetrievalModes mode,
ContourApproximationModes method,
Point? offset = null);
2. 绘制轮廓 DrawContours
public static void DrawContours(InputOutputArray image,
IEnumerable<Mat> contours,
int contourIdx, //需要绘制的轮廓索引,若为负,绘制所有轮廓
Scalar color, //颜色
int thickness = 1, //线粗
LineTypes lineType = LineTypes.Link8, //四联通、八联通、AA线
Mat hierarchy = null,
int maxLevel = int.MaxValue,
Point? offset = null); public static 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);
示例:滑动条和找轮廓
代码:
using System; using OpenCvSharp; namespace ConsoleApp2 { class Program { static void Main(string[] args) { Mat g_gray = new Mat(); Mat g_binary = new Mat(); int g_thresh = 100; void on_trackbar(int pos, object userdata) { Cv2.Threshold((Mat)userdata, g_binary, pos, 255, ThresholdTypes.BinaryInv); Point[][] contours; HierarchyIndex[] hierarchy; Cv2.FindContours(g_binary, out contours, out hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxSimple); Mat mask = new Mat(g_binary.Rows, g_binary.Cols, MatType.CV_8UC1, new Scalar(0)); Cv2.DrawContours(mask, contours, -1, new Scalar(255)); Cv2.ImShow("Contours", mask); } g_gray = Cv2.ImRead("C:\\Users\\ATWER\\Desktop\\b.png",ImreadModes.Grayscale); Cv2.ImShow("gray", g_gray); CvTrackbar cvTrackbar = new CvTrackbar("Threshold", "gray", g_thresh, 255, on_trackbar,g_gray); Cv2.ImShow("gray", g_gray); Cv2.WaitKey(0); } } }
3. 查找连通区域 ConnectedComponentsWithStats
示例:连通区域
using System; using OpenCvSharp; namespace ConsoleApp2 { class Program { static void Main(string[] args) { Mat img = Cv2.ImRead("C:\\Users\\ATWER\\Desktop\\c.png"); Mat labelMat = new Mat(); Mat stats = new Mat(); Mat centroids = new Mat(); Cv2.CvtColor(img, img, ColorConversionCodes.BGR2GRAY);
Cv2.NamedWindow("img",WindowMode.Normal); Cv2.ImShow("img", img); int num = Cv2.ConnectedComponentsWithStats(img, labelMat, stats, centroids, PixelConnectivity.Connectivity4); Console.WriteLine(num.ToString()); for (int i = 0; i < num; i++) { double a = centroids.At<double>(i, 0); double b = centroids.At<double>(i, 1); Point point = new OpenCvSharp.Point(a, b); Cv2.Circle(img, point, 2, new Scalar(0, 0, 255)); int x = stats.At<int>(i, 0); int y = stats.At<int>(i, 1); int width = stats.At<int>(i, 2); int height = stats.At<int>(i, 3); Rect rect = new Rect(x, y, width, height); Cv2.Rectangle(img, rect, new Scalar(255)); } Cv2.NamedWindow("绘制质心", WindowMode.Normal); Cv2.ImShow("绘制质心", img); Cv2.WaitKey(0); } } }
4. 多边形逼近轮廓 ApproxPolyDP()
public static void ApproxPolyDP(InputArray curve,
OutputArray approxCurve,
double epsilon,
bool closed);
示例:多边形逼近一个圆的外轮廓
代码:
using System; using System.Numerics; using OpenCvSharp; namespace ConsoleApp10 { class Program { static void Main(string[] args) { Mat img = new Mat(); Mat src = Cv2.ImRead("D:\\Backup\\桌面\\c.png");
//预处理 Cv2.CvtColor(src, img, ColorConversionCodes.BGR2GRAY); Cv2.BitwiseNot(img, img); Cv2.Threshold(img, img, 128, 255, ThresholdTypes.Binary); Cv2.ImShow("img", img);
//查找轮廓 Mat[] contours; Mat hierarchy = new Mat(); Cv2.FindContours(img,out contours, hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple); Mat[] contours_poly = (Mat[])contours.Clone(); //Cv2.DrawContours(img, contours, -1, new Scalar(255),5); for (int i = 0; i < contours.Length; i++) { Cv2.ApproxPolyDP(contours[i], contours_poly[i], 10, true);//多边形逼近 }
//绘制轮廓 Cv2.DrawContours(src, contours_poly, -1, new Scalar(0,255, 0),2); Cv2.ImShow("a", src); Cv2.WaitKey(0); } } }
5. 获得矩形包围框 BoundingRect
public static Rect BoundingRect(InputArray curve); public static RotatedRect MinAreaRect(InputArray points);
示例:矩形包围框
代码:
using System; using System.Numerics; using OpenCvSharp; namespace ConsoleApp10 { class Program { static void Main(string[] args) { Mat img = new Mat(); Mat src = Cv2.ImRead("D:\\Backup\\桌面\\c.png"); Cv2.ImShow("src", src); Cv2.CvtColor(src, img, ColorConversionCodes.BGR2GRAY); Cv2.BitwiseNot(img, img); Cv2.Threshold(img, img, 128, 255, ThresholdTypes.Binary); Cv2.ImShow("img", img); //查找轮廓
Mat[] contours; Mat hierarchy = new Mat(); Cv2.FindContours(img,out contours, hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxSimple); Mat[] contours_poly = (Mat[])contours.Clone(); for (int i = 0; i < contours.Length; i++) { Cv2.ApproxPolyDP(contours[i], contours_poly[i], 10, true); //获得轮廓长度 double len = Cv2.ArcLength(contours_poly[i], true); Console.WriteLine(len.ToString()); //获得矩形包围框 Rect rect = Cv2.BoundingRect(contours_poly[i]); Cv2.Rectangle(src, rect, new Scalar(0),5); //获得最小矩形框 RotatedRect rect_rotate = Cv2.MinAreaRect(contours_poly[i]); Console.WriteLine(rect_rotate.Angle.ToString()); }
//绘制轮廓 Cv2.DrawContours(src, contours_poly, -1, new Scalar(0,255, 0),2); Cv2.ImShow("a", src); Cv2.WaitKey(0); } } }
6. 获得轮廓凸包 ConvexHull
public static void ConvexHull(InputArray points,
OutputArray hull,
bool clockwise = false,
bool returnPoints = true);
示例:轮廓凸包
代码:
using System; using System.Numerics; using OpenCvSharp; namespace ConsoleApp10 { class Program { static void Main(string[] args) { Mat img = new Mat(); Mat src = Cv2.ImRead("D:\\Backup\\桌面\\d.png"); Cv2.ImShow("src", src); Cv2.CvtColor(src, img, ColorConversionCodes.BGR2GRAY); Cv2.BitwiseNot(img, img); Cv2.Threshold(img, img, 128, 255, ThresholdTypes.Binary); Cv2.ImShow("img", img);
//查找轮廓 Mat[] contours; Mat hierarchy = new Mat(); Cv2.FindContours(img,out contours, hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxSimple);
//凸包 居然想不到第二个方法把hulls画出来 Mat hulls = new Mat(); Cv2.ConvexHull(contours[0],hulls,false); Mat[] r = new Mat[] { hulls }; //绘制轮廓 Cv2.DrawContours(src, r, -1, new Scalar(0, 255, 0), 2); Cv2.ImShow("a", src); Cv2.WaitKey(0); } } }
7. Tips
学到一个新知识点,在二值化和查找轮廓之间,运用开运算,可以去除小的区域干扰。相比于在for循环遍历中再判断大小,要更节约时间。——2021.10.18
源自:蝴蝶书第十四章轮廓