边缘:离散的,断开的
轮廓:整体的,连续的
边缘检测主要是通过一些手段检测数字图像中明暗变化剧烈(即梯度变化比较大)像素点,偏向于图像中像素点的变化。如canny边缘检测,结果通常保存在和源图片一样尺寸和类型的边缘图中。
轮廓检测指检测图像中的对象边界,更偏向于关注上层语义对象。如OpenCV中的findContours()函数, 它会得到每一个轮廓并以点向量方式存储,除此也得到一个图像的拓扑信息,即一个轮廓的后一个轮廓、前一个轮廓、父轮廓和内嵌轮廓的索引编号。
// 1. 加载原图
var image1 = new Image<Bgr, byte>("cloud.png");
var image0 = image1.Mat.Clone();
PreviewImage1 = new WriteableBitmap(Bitmap2BitmapImage(Text(image1.Bitmap, "原图")));
// 2. 灰度图
var img2 = image0.Clone();
CvInvoke.CvtColor(image0, img2, ColorConversion.Bgr2Gray);
PreviewImage2 = new WriteableBitmap(Bitmap2BitmapImage(Text(img2.Bitmap, "灰度图")));
// 3. 阈值
var img3 = new Mat();
CvInvoke.Threshold(img2, img3, 0, 255, ThresholdType.Otsu);
PreviewImage3 = new WriteableBitmap(Bitmap2BitmapImage(Text2(img3.Bitmap, "Otsu")));
// 4. 查找轮廓
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
Mat hierarchy = new Mat();
CvInvoke.FindContours(img3, contours, hierarchy, RetrType.Tree, ChainApproxMethod.ChainApproxNone);
// 5. 绘制所有轮廓
var img4 = image0.Clone();
CvInvoke.DrawContours(img4, contours, -1, new MCvScalar(0, 0, 255), 2);
PreviewImage4 = new WriteableBitmap(Bitmap2BitmapImage(Text(img4.Bitmap, "绘制所有轮廓")));
// 6. 绘制最大的轮廓
double MaxArea = 0;
int maxIndex = 0;
for (int i = 0; i < contours.Size; i++) {
var area = CvInvoke.ContourArea(contours[i]);
if (area > MaxArea) {
MaxArea = area;
maxIndex = i;
}
}
var img5 = image0.Clone();
CvInvoke.DrawContours(img5, contours, maxIndex, new MCvScalar(0, 0, 255), 2);
PreviewImage5 = new WriteableBitmap(Bitmap2BitmapImage(Text(img5.Bitmap, "绘制面积最大的轮廓")));
// 7. 轮廓近似
var e = CvInvoke.ArcLength(contours[maxIndex],true)*0.01;
VectorOfPoint s = new VectorOfPoint();
CvInvoke.ApproxPolyDP(contours[maxIndex],s,e,true);
var img6 = image0.Clone();
VectorOfVectorOfPoint contours2 = new VectorOfVectorOfPoint();
contours2.Push(s);
CvInvoke.DrawContours(img6, contours2, -1, new MCvScalar(0, 0, 255), 2);
PreviewImage6 = new WriteableBitmap(Bitmap2BitmapImage(Text(img6.Bitmap, "绘制近似后的轮廓")));
// 8. 边界矩形
var rect = CvInvoke.BoundingRectangle(contours[maxIndex]);
var img7 = image0.Clone();
CvInvoke.Rectangle(img7, rect, new MCvScalar(0, 0, 255), 2);
PreviewImage7 = new WriteableBitmap(Bitmap2BitmapImage(Text(img7.Bitmap, "绘制边界矩形")));
// 9. 外接圆
var img8 = image0.Clone();
var circle = CvInvoke.MinEnclosingCircle(contours[maxIndex]);
CvInvoke.Circle(img8, new Point((int)circle.Center.X, (int)circle.Center.Y), (int)circle.Radius, new MCvScalar(0, 0, 255), 2);
PreviewImage8 = new WriteableBitmap(Bitmap2BitmapImage(Text(img8.Bitmap, "绘制外接圆")));
// 10. 外接三角形
var img9 = image0.Clone();
VectorOfPoint triangle = new VectorOfPoint();
CvInvoke.MinEnclosingTriangle(contours[maxIndex], triangle);
CvInvoke.Line(img9, triangle[0],triangle[1], new MCvScalar(0, 0, 255), 2);
CvInvoke.Line(img9, triangle[1], triangle[2], new MCvScalar(0, 0, 255), 2);
CvInvoke.Line(img9, triangle[2], triangle[0], new MCvScalar(0, 0, 255), 2);
PreviewImage9 = new WriteableBitmap(Bitmap2BitmapImage(Text(img9.Bitmap, "绘制外接三角形")));