C# 和 GDI+ 图像处理入门,第三部分 - 边缘检测过滤器






4.69/5 (8投票s)
这将是主文章“差值边缘检测”中描述的唯一边缘检测算法的替代方案。
程序概述
用于在图像中查找对象轮廓的简单窗口窗体应用程序。要运行程序的发布版本,需要安装 Microsoft .Net framework ver. 4.0 或更高版本。
程序控件
- 点击原始图像(左侧图像面板)将打开一个对话框以加载新图像
- 点击结果图像(右侧图像面板)将打开一个对话框以保存结果图像
- 更改亮度的阈值,将自动开始
对原始图像进行新的处理
- 更改处理后的图像显示类型,将自动开始
对原始图像进行新的处理
引言
"差值边缘检测"基于这样一个假设:当观察点周围的像素对之间存在显著的颜色差异时,人眼就可以注意到图像中对象的轮廓(边缘)。这里指的是可以形成一条穿过观察点的直线的点对。
关注点
尽管 两个 算法的 伪代码 差异很小,但在算法实现上存在显著差异。我们没有使用 unsafe 代码和指针直接访问锁定的位图数据,而是使用了 System.Runtime.Interopservices 中的 Marshal.Copy 方法,该方法允许从托管数组快速安全地复制数据到非托管内存指针,或从非托管内存指针复制到托管数组。
将位图数据从非托管内存指针复制到托管的二维字节数组后,我们使用该数组及其像素颜色 RGB 分量的值来进行边缘检测。
图像处理完成后,新的位图数据被复制从数组到非托管内存指针,并将结果图像显示给用户。
最重要的是,图像处理速度相同,无需考虑使用 unsafe 代码的安全性问题。结果不同,质量取决于许多参数,原始位图图像的分辨率(越高越好,越慢 / 越低越差越快),颜色数量及其 色调(越少越好 / 越多越差)等。
使用代码
最好的方法是下载完整的解决方案,该方案是用 IDE #Develop ver. 4.4,语言 C# ver 4.0 编写的。Microsoft .Net framework 4.0,其中包含完整的程序代码,以及所有必要的图形用户界面控件,可用于构建可执行版本的程序。这里仅展示了轮廓检测的主方法。在此方法中,使用了一个全局变量和几个在该方法外部定义的控件。
Original_image Bitmap 类型
用于存储来自文件的原始图像,Lower_Brightness_Limit NumericUpDown 类型
用于选择亮度下限值Upper_Brightness_Limit NumericUpDown 类型
用于选择亮度上限值Invert_Edge_Color CheckBox 类型
用于选择检测到的边缘颜色类型Black_White RadioButton 类型
用于选择结果图像颜色类型Gray_Scale RadioButton 类型
用于选择结果图像颜色类型Image_2 PictureBox 类型
用于向用户显示结果图像
如果您想使用此处提供的此方法,则上述变量和控件必须定义在您的程序中。
void Start_Contour_Detection()
{
// Copy original image to new bitmap
Bitmap bmp = (Bitmap)Original_image.Clone();
int Image_width = bmp.Width;
int Image_height = bmp.Height;
// Rectangle size
Rectangle Rectangle = new Rectangle(0, 0, Image_width, Image_height);
// Lock bitmap inside memory for faster processing and load bitmap data
BitmapData bmp_Data = bmp.LockBits(Rectangle, ImageLockMode.ReadWrite, bmp.PixelFormat);
// Load adress of the first byte of the bitmap,
// it is pointer to the adress
IntPtr bmp_First_byte_adress = bmp_Data.Scan0;
//
// Declaration of the matrix that should contain all bytes of the bitmap data
//
// Bitmap must contain 24 bits (three bytes) per pixel
// One byte for each component of RGB color of pixels
int Number_of_pixels = Image_width * Image_height;
int Number_of_bytes = Number_of_pixels * 3;
//
// Because of the way the bitmap data are stored in the memory,
// so called 'bytes fine alignment inside the row',
// number of bytes in a row is rounded up to the nearest number divisible by four
// So that one row of the bitmap is always containing the same or larger number of bytes,
// that is necessary for memorising data for the actual number of points in row
int Exact_number_of_bytes_in_row = bmp_Data.Stride;
int Necessary_number_of_bytes = Image_width*3;
//
int Number_of_alignment_bytes = Exact_number_of_bytes_in_row - Necessary_number_of_bytes;
//
// Total count of bytes necesary for all image pixels
Number_of_bytes += Image_height * Number_of_alignment_bytes;
//
// One dimensional matrix for memorizing bitmap
// values of RGB components of color of pixels
byte[] bmp_RGB_values = new byte[Number_of_bytes];
// Copy values of RGB components of pixel color from bitmap to matrix
Marshal.Copy(bmp_First_byte_adress, bmp_RGB_values, 0, Number_of_bytes);
// Two dimensional matrix for memorizing bitmap
// values of RGB components of color of pixels
byte [,,] RGB = new byte[Image_width,Image_height,3];
// Matrix for memorizing values of brightness of pixels
float [,] Brightness = new float[Image_width,Image_height];
// Byte counter inside one dimenzional matrix
int bmp_k = 0;
// Copy bitmap values of RGB components of color of pixels,
// from one dimenzional to two dimenzional matrix
// and fill matrix Brightness with values of brightness of pixels
//
// NOTICE :
// When loading bitmap data
// BitmapData bmp_Data = bmp.LockBits(Rectangle, ImageLockMode.ReadWrite, bmp.PixelFormat); ,
// values of RGB components of color of pixels are returned in opposite direction
// RGB -> BGR
//
for (int i=0;i < Image_height;i++)
{
for(int j=0;j < Image_width;j++)
{
// Value of R component of pixel color
RGB[j,i,0] = bmp_RGB_values[bmp_k+2];
// Value of G component of pixel color
RGB[j,i,1] = bmp_RGB_values[bmp_k+1];
// Value of B component of pixel color
RGB[j,i,2] = bmp_RGB_values[bmp_k+0];
// Value of pixel brightness
Brightness[j,i] = Color.FromArgb
(
bmp_RGB_values[bmp_k+2],
bmp_RGB_values[bmp_k+1],
bmp_RGB_values[bmp_k+0]
).GetBrightness();
bmp_k+=3;
}
bmp_k+= Number_of_alignment_bytes;
}
// Load lower and upper limit of the brightness
float lower_limit = (float) Lower_Brightness_Limit.Value;
float upper_limit = (float) Upper_Brightness_Limit.Value;
// Maximum found value for the difference in brightness
// between the opposing pixels
float mfd = 0;
for(int i=1;i < Image_height-1;i++)
{
for(int j=1;j < Image_width-1;j++)
{
//
mfd = Math.Abs(Brightness[j-1,i-1]-Brightness[j+1,i+1]);
//
if(mfd < Math.Abs(Brightness[j-1,i+1]-Brightness[j+1,i-1]))
mfd=Math.Abs(Brightness[j-1,i+1]-Brightness[j+1,i-1]);
//
if(mfd < Math.Abs(Brightness[j,i+1]-Brightness[j,i-1]))
mfd=Math.Abs(Brightness[j,i+1]-Brightness[j,i-1]);
//
if(mfd < Math.Abs(Brightness[j-1,i]-Brightness[j+1,i]))
mfd=Math.Abs(Brightness[j-1,i]-Brightness[j+1,i]);
//
if(Invert_Edge_Color.Checked)
{
if(mfd < lower_limit)
{
RGB[j,i,0] = (byte) 255;
RGB[j,i,1] = (byte) 255;
RGB[j,i,2] = (byte) 255;
}
else if(mfd > upper_limit)
{
RGB[j,i,0] = (byte) 0;
RGB[j,i,1] = (byte) 0;
RGB[j,i,2] = (byte) 0;
}
}
else
{
if(mfd < lower_limit)
{
RGB[j,i,0] = (byte) 0;
RGB[j,i,1] = (byte) 0;
RGB[j,i,2] = (byte) 0;
}
else if(mfd > upper_limit)
{
RGB[j,i,0] = (byte) 255;
RGB[j,i,1] = (byte) 255;
RGB[j,i,2] = (byte) 255;
}
}
}
}
if(Black_White.Checked)
{
for(int i=1;i < Image_height-1;i++)
{
for(int j=1;j < Image_width-1;j++)
{
if(Invert_Edge_Color.Checked)
{
if(RGB[j,i,0] < 255 || RGB[j,i,1] < 255 || RGB[j,i,2] < 255)
RGB[j,i,0] = RGB[j,i,1] = RGB[j,i,2] = (byte) 0;
}
else
{
if(RGB[j,i,0] > 0 || RGB[j,i,1] > 0 || RGB[j,i,2] > 0)
RGB[j,i,0] = RGB[j,i,1] = RGB[j,i,2] = (byte) 255;
}
}
}
}
if(Gray_Scale.Checked)
{
for(int i=1;i < Image_height-1;i++)
{
for(int j=1;j < Image_width-1;j++)
{
RGB[j,i,0] = RGB[j,i,1] = RGB[j,i,2] =
(byte)
(
(0.299*RGB[j,i,0]) +
(0.587*RGB[j,i,1]) +
(0.114*RGB[j,i,2])
);
}
}
}
// Byte counter inside one dimenzional matrix
bmp_k = 0;
// Copy new bitmap values of RGB components of color of pixels,
// from two dimenzional to one dimenzional matrix
for (int i=0;i < Image_height;i++)
{
for(int j=0;j < Image_width;j++)
{
// Value of R component of pixel color
bmp_RGB_values[bmp_k+2] = RGB[j,i,0];
// Value of G component of pixel color
bmp_RGB_values[bmp_k+1] = RGB[j,i,1];
// Value of B component of pixel color
bmp_RGB_values[bmp_k+0] = RGB[j,i,2];
bmp_k+=3;
}
bmp_k+=Number_of_alignment_bytes;
}
// Copy new values of RGB components of pixel color from matrix back to bitmap
Marshal.Copy(bmp_RGB_values, 0, bmp_First_byte_adress, Number_of_bytes);
// Unlock bitmap inside memory
bmp.UnlockBits(bmp_Data);
// Show the processed image
Image_2.Image = bmp;
}
历史
最后修改日期 2015.05.10. 19:00,作者