对图像边缘提取,常见的方式是先对图片进行灰度处理,然后再利用图像梯度算法提取出边框。我们先来看效果图
经过处理后的前后对比,可以看到,图形的轮廓已经大致提取了。现在来看实现的代码
using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Text; namespace BitmapOutlineSimulate { public class BitmapOutline { #region 内部变量 private Bitmap _bitmap = null; #endregion public BitmapOutline(Bitmap bitmap) { _bitmap = bitmap; } #region Calculate public Bitmap Calculate(GradientTypeEnum gradientType, int threshold) { Bitmap grayBitmap = new Bitmap(_bitmap.Width, _bitmap.Height); for (int i = 0; i < _bitmap.Width; i++) { for (int j = 0; j < _bitmap.Height; j++) { //得到像素的原始色彩 var oColor = _bitmap.GetPixel(i, j); //得到该色彩的亮度 var brightness = oColor.GetBrightness(); //用该亮度计算灰度 var gRGB = (int)(brightness * 255); //组成灰度色彩 var gColor = Color.FromArgb(gRGB, gRGB, gRGB); //最后将该灰度色彩赋予该像素 grayBitmap.SetPixel(i, j, gColor); } } var gradientTemplate = Gradient.GetGradientTemplate(gradientType); var destBitmap = EdgeDectect(grayBitmap, gradientTemplate, threshold); return destBitmap; } //template为模板,nThreshold是一个阈值, //用来将模板游历的结果(也就是梯度)进行划分。 //大于阈值的和小于阈值的分别赋予两种颜色,白或黑来标志边界和背景 private Bitmap EdgeDectect(Bitmap grayBitmap, int[,] template, int nThreshold) { var destBitmap = new Bitmap(grayBitmap.Width, grayBitmap.Height); //取出和模板等大的原图中的区域 int[,] gRGB = new int[3, 3]; //模板值结果,梯度 int templateValue = 0; //遍历灰度图中每个像素 for (int i = 1; i < grayBitmap.Width - 1; i++) { for (int j = 1; j < grayBitmap.Height - 1; j++) { //取得模板下区域的颜色,即灰度 gRGB[0, 0] = grayBitmap.GetPixel(i - 1, j - 1).R; gRGB[0, 1] = grayBitmap.GetPixel(i - 1, j).R; gRGB[0, 2] = grayBitmap.GetPixel(i - 1, j + 1).R; gRGB[1, 0] = grayBitmap.GetPixel(i, j - 1).R; gRGB[1, 1] = grayBitmap.GetPixel(i, j).R; gRGB[1, 2] = grayBitmap.GetPixel(i, j + 1).R; gRGB[2, 0] = grayBitmap.GetPixel(i + 1, j - 1).R; gRGB[2, 1] = grayBitmap.GetPixel(i + 1, j).R; gRGB[2, 2] = grayBitmap.GetPixel(i + 1, j + 1).R; //按模板计算 for (int m = 0; m < 3; m++) { for (int n = 0; n < 3; n++) { templateValue += template[m, n] * gRGB[m, n]; } } //将梯度之按阈值分类,并赋予不同的颜色 if (templateValue > nThreshold) { destBitmap.SetPixel(i, j, Color.FromArgb(255, 255, 255)); //白 } else { destBitmap.SetPixel(i, j, Color.FromArgb(0, 0, 0)); //黑 } templateValue = 0; } } return destBitmap; } #endregion } }在Calculate函数中,先对图片进行了灰度处理,其原理就是获取图片的亮度然后与RGB色运算得到。
在灰度处理后,使用图像梯度和阈值对图像进行处理。选择不同的梯度和阈值会处理出不同的结果,上图是使用Lapacian梯度和阈值125计算的结果。下面梯度的代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace BitmapOutlineSimulate { /// <summary> /// 梯度 /// </summary> public class Gradient { #region 常量 public static int[,] Lapacian = { { 1,1,1}, { 1,-8,1}, { 1,1,1} }; /// <summary> /// Sobel算子-水平 /// </summary> public static int[,] Sobel_Horizontal = { { -1,-2,-1}, { 0,0,0}, { 1,2,1} }; /// <summary> /// Sobel算子-垂直 /// </summary> public static int[,] Sobel_Vertical = { { -1,0,1}, { -2,0,2}, { -1,0,1} }; /// <summary> /// Sobel算子-对角线1 /// </summary> public static int[,] Sobel_Diagonal1 = { { 0,1,2}, { -1,0,1}, { -2,-1,0} }; /// <summary> /// Sobel算子-对角线2 /// </summary> public static int[,] Sobel_Diagonal2 = { { -2,-1,0}, { -1,0,1}, { 0,1,2} }; /// <summary> /// Prewitt算子-水平 /// </summary> public static int[,] Prewitt_Horizontal = { { -1,-1,-1}, { 0,0,0}, { 1,1,1} }; /// <summary> /// Prewitt算子-垂直 /// </summary> public static int[,] Prewitt_Vertical = { { -1,0,1}, { -1,0,1}, { -1,0,1} }; /// <summary> /// Prewitt算子-对角线1 /// </summary> public static int[,] Prewitt_Diagonal1 = { { 0,1,1}, { -1,0,1}, { -1,-1,0} }; /// <summary> /// Prewitt算子-对角线2 /// </summary> public static int[,] Prewitt_Diagonal2 = { { -1,-1,0}, { -1,0,1}, { 0,1,1} }; #endregion public static int[,] GetGradientTemplate(GradientTypeEnum gradientType) { int[,] gradientTemplate = null; switch (gradientType) { case GradientTypeEnum.Lapacian: { gradientTemplate = Lapacian; break; } case GradientTypeEnum.Sobel_Horizontal: { gradientTemplate = Sobel_Horizontal; break; } case GradientTypeEnum.Sobel_Vertical: { gradientTemplate = Sobel_Vertical; break; } case GradientTypeEnum.Sobel_Diagonal1: { gradientTemplate = Sobel_Diagonal1; break; } case GradientTypeEnum.Sobel_Diagonal2: { gradientTemplate = Sobel_Diagonal2; break; } case GradientTypeEnum.Prewitt_Horizontal: { gradientTemplate = Prewitt_Horizontal; break; } case GradientTypeEnum.Prewitt_Vertical: { gradientTemplate = Prewitt_Vertical; break; } case GradientTypeEnum.Prewitt_Diagonal1: { gradientTemplate = Prewitt_Diagonal1; break; } case GradientTypeEnum.Prewitt_Diagonal2: { gradientTemplate = Prewitt_Diagonal2; break; } default: break; } return gradientTemplate; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace BitmapOutlineSimulate { public enum GradientTypeEnum { Lapacian, /// <summary> /// Sobel算子-水平 /// </summary> Sobel_Horizontal, /// <summary> /// Sobel算子-垂直 /// </summary> Sobel_Vertical, /// <summary> /// Sobel算子-对角线1 /// </summary> Sobel_Diagonal1, /// <summary> /// Sobel算子-对角线2 /// </summary> Sobel_Diagonal2, /// <summary> /// Prewitt算子-水平 /// </summary> Prewitt_Horizontal, /// <summary> /// Prewitt算子-垂直 /// </summary> Prewitt_Vertical, /// <summary> /// Prewitt算子-对角线1 /// </summary> Prewitt_Diagonal1, /// <summary> /// Prewitt算子-对角线2 /// </summary> Prewitt_Diagonal2 } }灰度算法参考文章http://blog.csdn.net/chinaxhb/article/details/4038050
图像梯度参考文章http://blog.csdn.net/swj110119/article/details/51777422
转载请注明出处。