对图像边缘提取,常见的方式是先对图片进行灰度处理,然后再利用图像梯度算法提取出边框。我们先来看效果图
经过处理后的前后对比,可以看到,图形的轮廓已经大致提取了。现在来看实现的代码
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
转载请注明出处。