對圖像邊緣提取,常見的方式是先對圖片進行灰度處理,然后再利用圖像梯度算法提取出邊框。我們先來看效果圖

經過處理后的前后對比,可以看到,圖形的輪廓已經大致提取了。現在來看實現的代碼
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
轉載請注明出處。
