您的位置:首页 > 编程语言 > C#

【C#】用EmguCV 绘制各种轮廓线

2017-10-11 19:25 639 查看
一般在做影像处理时,为提升效率,常会将影像转为二值影像后再进行处理。

在EmguCV内有许多找轮廓线的方法,但是随着版本更新,不同版本的函数

不见得会一样,每次都要重新查询实在很麻烦,那不如把他们记下来。

版本概要:

EmguCV版本:3.2.0.2682

编译器版本: Visual Studio 2017 Community

方案平台: x64 (许多导致程式无法执行的原因是因为没有改执行平台!)

正文开始。

首先我们用小画家画了一张图来作为范本—一朵云。

因为形状奇特,非常适合用来说明。



1. BoundingBox: 可以框住全部范围的矩形。

这是没有经过旋转地矩形,有经过旋转的矩形在后面讨论。

using System;
using System.Windows.Forms;
using System.Drawing;

using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.CvEnum;
using Emgu.CV.Util;

namespace Test
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{

Image<Gray, byte> I = new Image<Gray, byte>(@"D:\Test\1.jpg");
Image<Bgr, byte> DrawI = I.Convert<Bgr, byte>();

Image<Gray, byte> CannyImage = I.Clone();
CvInvoke.Canny(I, CannyImage, 255, 255, 5, true);

MyCV.BoundingBox(CannyImage, DrawI);
pictureBox1.Image = DrawI.Bitmap;
}
}

public class MyCV
{
public static void BoundingBox(Image<Gray, byte> src, Image<Bgr, byte> draw)
{
using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
{
CvInvoke.FindContours(src, contours, null, RetrType.External,
ChainApproxMethod.ChainApproxSimple);

int count = contours.Size;
for (int i = 0; i < count; i++)
{
using (VectorOfPoint contour = contours[i])
{
Rectangle BoundingBox = CvInvoke.BoundingRectangle(contour);
CvInvoke.Rectangle(draw, BoundingBox, new MCvScalar(255, 0, 255, 255), 3);
}
}
}
}
}
}




注:后面的程式码仅写出操作的函数,省略主视窗及名称空间,请自行代换主视窗的程式码。

在这边常有看到一些范例程式会建议使用ApproxPolyDP这个方法,取得近似的形状,

经过测试,若是在一些精度需求不高的情况下可以这么做,但就这个云形的例子而言不建议这样做。

下面是采用ApproxPolyDP函数的程式码与结果。

public static void ApproxBoundingBox(Image<Gray, byte> src, Image<Bgr, byte> draw)
{
using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
{
CvInvoke.FindContours(src, contours, null, RetrType.External,
ChainApproxMethod.ChainApproxSimple);

int count = contours.Size;
for (int i = 0; i < count; i++)
{
using (VectorOfPoint contour = contours[i])
using (VectorOfPoint approxContour = new VectorOfPoint())
{
CvInvoke.ApproxPolyDP(contour, approxContour, CvInvoke.ArcLength(contour, true) * 0.05, true);
Rectangle BoundingBox = CvInvoke.BoundingRectangle(approxContour);
CvInvoke.Rectangle(draw, BoundingBox, new MCvScalar(255, 0, 255, 255), 3);
}
}
}
}




可以看到有许多卷卷的地方,都被近似掉了,以至于框选出来的范围会失真。

但,若目标是长方形或三角形这种比较规则的形状,使用近似的方法可以提升执行的效率。

其实若是直接把轮廓线画出来就可以看得更清楚,近似后许多细节会消失。

以下是程式码与执行结果。

public static void DrawContour(Image<Gray, byte> src, Image<Bgr, byte> draw)
{
using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
{
CvInvoke.FindContours(src, contours, null, RetrType.External,
ChainApproxMethod.ChainApproxSimple);

int count = contours.Size;
for (int i = 0; i < count; i++)
{
using (VectorOfPoint contour = contours[i])
using (VectorOfPoint approxContour = new VectorOfPoint())
{
// 原始輪廓線
CvInvoke.DrawContours(draw, contours, i, new MCvScalar(255, 0, 255, 255), 3);

// 近似後輪廓線
CvInvoke.ApproxPolyDP(contour, approxContour,
CvInvoke.ArcLength(contour, true) * 0.02, true);
Point[] pts = approxContour.ToArray();
for(int j=0; j<pts.Length; j++)
{
Point p1 = new Point(pts[j].X, pts[j].Y);
Point p2;

if (j == pts.Length - 1)
p2 = new Point(pts[0].X, pts[0].Y);
else
p2 = new Point(pts[j+1].X, pts[j+1].Y);

CvInvoke.Line(draw, p1, p2, new MCvScalar(255, 0, 0, 0), 3);
}
}
}
}
}




2. ConvexHull: 可以框住区块的最小多边形。

public static void ConvexHull(Image<Gray, byte> src, Image<Bgr, byte> draw)
{
using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
{
CvInvoke.FindContours(src, contours, null, RetrType.External,
ChainApproxMethod.ChainApproxSimple);

int count = contours.Size;
for (int i = 0; i < count; i++)
{
using (VectorOfPoint contour = contours[i])
{
PointF[] temp = Array.ConvertAll(contour.ToArray(),
new Converter<Point, PointF>(Point2PointF));
PointF[] pts = CvInvoke.ConvexHull(temp, true);

for (int j = 0; j < pts.Length; j++)
{
Point p1 = new Point((int)pts[j].X, (int)pts[j].Y);
Point p2;

if (j == pts.Length - 1)
p2 = new Point((int)pts[0].X, (int)pts[0].Y);
else
p2 = new Point((int)pts[j + 1].X, (int)pts[j + 1].Y);

CvInvoke.Line(draw, p1, p2, new MCvScalar(255, 0, 255, 255), 3);
}
}
}
}
}

private static PointF Point2PointF(Point P)
{
PointF PF = new PointF
{
X = P.X,
Y = P.Y
};
return PF;
}




3. MinAreaBoundingBox: 可框住区域的最小矩形。

这是可旋转的矩形,意即找到面积最小,又可以框住该区域的矩形。

public static void MinAreaBoundingBox(Image<Gray, byte> src, Image<Bgr, byte> draw)
{
using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
{
CvInvoke.FindContours(src, contours, null, RetrType.External,
ChainApproxMethod.ChainApproxSimple);

int count = contours.Size;
for (int i = 0; i < count; i++)
{
using (VectorOfPoint contour = contours[i])
{
RotatedRect BoundingBox = CvInvoke.MinAreaRect(contour);
CvInvoke.Polylines(draw, Array.ConvertAll(BoundingBox.GetVertices(), Point.Round),
true, new Bgr(Color.DeepPink).MCvScalar, 3);
}
}
}
}




4. MinAreaCircle:可框住区域的最小圆形。

public static void MinAreaCircle(Image<Gray, byte> src, Image<Bgr, byte> draw)
{
using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
{
CvInvoke.FindContours(src, contours, null, RetrType.External,
ChainApproxMethod.ChainApproxSimple);

int count = contours.Size;
for (int i = 0; i < count; i++)
{
using (VectorOfPoint contour = contours[i])
{
CircleF circle = CvInvoke.MinEnclosingCircle(contour);
CvInvoke.Circle(draw, new Point((int)circle.Center.X, (int) circle.Center.Y),
(int)circle.Radius, new MCvScalar(255, 0, 255, 255), 3);
}
}
}
}




在EmguCV内一种轮廓线就一种画法。

真的是要足够熟练才能够驾驭这些函数唉!

像是可怜的小夏已经陷在这些函数内好几天了,真是头昏眼花,临表泣涕,不知所云。

翻译自:dotblogs.com.tw 夏恩的程式笔记
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c# EmguCV