我正在使用Winforms。在我的表格中,我有一个显示黑白图像的图片框。我还有一个按钮,如果你点击它,该按钮将删除图像上的斑点/点。当图像尺寸不大时,它会快速消除斑点。如果图像很大,则需要一段时间。此外,有时这个函数会从它认为是一个点的图像中删除一些单词。如何提高此功能的性能,更准确地去除斑点或点基本上去除图像?
更新 经过研究,我发现这个图书馆对这个问题似乎很有希望:
http://www.aforgenet.com/framework/docs/html/cdf93487-0659-e371-fed9-3b216efb6954.htm
发现图片的链接: http://www.filedropper.com/testing-image3
图片示例
注意链接中的图片有更大的版本:
图片信息
这里需要注意的是它是一张黑白图像 - Bit Depth 1
我的代码
private int[] mask = new int[9];
private void remove_spot_btn_Click(object sender, EventArgs e)
{
Bitmap img = new Bitmap(pictureBox1.Image);
Color c;
for (int ii = 0; ii < img.Width; ii++)
{
for (int jj = 0; jj < img.Height; jj++)
{
if (ii - 1 >= 0 && jj - 1 >= 0)
{
c = img.GetPixel(ii - 1, jj - 1);
mask[0] = Convert.ToInt16(c.R);
}
else
{
mask[0] = 0;
}
if (jj - 1 >= 0 && ii + 1 < img.Width)
{
c = img.GetPixel(ii + 1, jj - 1);
mask[1] = Convert.ToInt16(c.R);
}
else
mask[1] = 0;
if (jj - 1 >= 0)
{
c = img.GetPixel(ii, jj - 1);
mask[2] = Convert.ToInt16(c.R);
}
else
mask[2] = 0;
if (ii + 1 < img.Width)
{
c = img.GetPixel(ii + 1, jj);
mask[3] = Convert.ToInt16(c.R);
}
else
mask[3] = 0;
if (ii - 1 >= 0)
{
c = img.GetPixel(ii - 1, jj);
mask[4] = Convert.ToInt16(c.R);
}
else
mask[4] = 0;
if (ii - 1 >= 0 && jj + 1 < img.Height)
{
c = img.GetPixel(ii - 1, jj + 1);
mask[5] = Convert.ToInt16(c.R);
}
else
mask[5] = 0;
if (jj + 1 < img.Height)
{
c = img.GetPixel(ii, jj + 1);
mask[6] = Convert.ToInt16(c.R);
}
else
mask[6] = 0;
if (ii + 1 < img.Width && jj + 1 < img.Height)
{
c = img.GetPixel(ii + 1, jj + 1);
mask[7] = Convert.ToInt16(c.R);
}
else
mask[7] = 0;
c = img.GetPixel(ii, jj);
mask[8] = Convert.ToInt16(c.R);
Array.Sort(mask);
int mid = mask[4];
img.SetPixel(ii, jj, Color.FromArgb(mid, mid, mid));
}
}
pictureBox1.Image = img;
MessageBox.Show("Complete");
}
答案 0 :(得分:4)
正如comments中所述,要更改Bitmap
中的像素而不是SetPixel
,您可以使用Bitmap.LockBits
方法访问位图数据。
为了使代码更快,只需更改,您就可以使用LockBits
创建一个封装快速访问位图数据的类,并为该类创建GetPixel
和SetPixel
方法。
例如,我使用了一个由Vano Maisuradze编写的类,只有很小的更改(我从代码中删除了不必要的try / catch块)。该课程正在使用LockBits
方法,并提供GetPixel
和SetPixel
方法的快速版本。
然后您的代码应更改为:
var bmp = new Bitmap(pictureBox1.Image);
var img = new LockBitmap(bmp);
img.LockBits();
Color c;
//...
//...
//...
img.UnlockBits();
pictureBox1.Image = bmp;
MessageBox.Show("Complete");
以下是该类的实现:
public class LockBitmap
{
Bitmap source = null;
IntPtr Iptr = IntPtr.Zero;
BitmapData bitmapData = null;
public byte[] Pixels { get; set; }
public int Depth { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
public LockBitmap(Bitmap source)
{
this.source = source;
}
/// <summary>
/// Lock bitmap data
/// </summary>
public void LockBits()
{
// Get width and height of bitmap
Width = source.Width;
Height = source.Height;
// get total locked pixels count
int PixelCount = Width * Height;
// Create rectangle to lock
Rectangle rect = new Rectangle(0, 0, Width, Height);
// get source bitmap pixel format size
Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat);
// Check if bpp (Bits Per Pixel) is 8, 24, or 32
if (Depth != 8 && Depth != 24 && Depth != 32)
{
throw new ArgumentException("Only 8, 24 and 32 bpp images are supported.");
}
// Lock bitmap and return bitmap data
bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite,
source.PixelFormat);
// create byte array to copy pixel values
int step = Depth / 8;
Pixels = new byte[PixelCount * step];
Iptr = bitmapData.Scan0;
// Copy data from pointer to array
Marshal.Copy(Iptr, Pixels, 0, Pixels.Length);
}
/// <summary>
/// Unlock bitmap data
/// </summary>
public void UnlockBits()
{
// Copy data from byte array to pointer
Marshal.Copy(Pixels, 0, Iptr, Pixels.Length);
// Unlock bitmap data
source.UnlockBits(bitmapData);
}
/// <summary>
/// Get the color of the specified pixel
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public Color GetPixel(int x, int y)
{
Color clr = Color.Empty;
// Get color components count
int cCount = Depth / 8;
// Get start index of the specified pixel
int i = ((y * Width) + x) * cCount;
if (i > Pixels.Length - cCount)
throw new IndexOutOfRangeException();
if (Depth == 32) // For 32 bpp get Red, Green, Blue and Alpha
{
byte b = Pixels[i];
byte g = Pixels[i + 1];
byte r = Pixels[i + 2];
byte a = Pixels[i + 3]; // a
clr = Color.FromArgb(a, r, g, b);
}
if (Depth == 24) // For 24 bpp get Red, Green and Blue
{
byte b = Pixels[i];
byte g = Pixels[i + 1];
byte r = Pixels[i + 2];
clr = Color.FromArgb(r, g, b);
}
if (Depth == 8)
// For 8 bpp get color value (Red, Green and Blue values are the same)
{
byte c = Pixels[i];
clr = Color.FromArgb(c, c, c);
}
return clr;
}
/// <summary>
/// Set the color of the specified pixel
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="color"></param>
public void SetPixel(int x, int y, Color color)
{
// Get color components count
int cCount = Depth / 8;
// Get start index of the specified pixel
int i = ((y * Width) + x) * cCount;
if (Depth == 32) // For 32 bpp set Red, Green, Blue and Alpha
{
Pixels[i] = color.B;
Pixels[i + 1] = color.G;
Pixels[i + 2] = color.R;
Pixels[i + 3] = color.A;
}
if (Depth == 24) // For 24 bpp set Red, Green and Blue
{
Pixels[i] = color.B;
Pixels[i + 1] = color.G;
Pixels[i + 2] = color.R;
}
if (Depth == 8)
// For 8 bpp set color value (Red, Green and Blue values are the same)
{
Pixels[i] = color.B;
}
}
}
答案 1 :(得分:3)
正如您所知,使用AForge.NET是一个好主意(您只需将其添加为nuget)。我建议您使用通常用于去噪的Median filter(请参阅维基百科中的Median Filter)。
AForge需要24bpp的RGB图像,所以你需要先在你的示例中转换它,但这里有一个代码似乎在它上面工作得很好的例子:
// load the file as 24bpp RGB
using (var bmp = LoadForFiltering(@"C:\temp\Testing-Image3.tif"))
{
var filter = new Median();
// run the filter
filter.ApplyInPlace(bmp);
// save the file back (here, I used png as the output format)
bmp.Save(@"C:\temp\Testing-Image3.png");
}
private static Bitmap LoadForFiltering(string filePath)
{
var bmp = (Bitmap)Bitmap.FromFile(filePath);
if (bmp.PixelFormat == PixelFormat.Format24bppRgb)
return bmp;
try
{
// from AForge's sample code
if (bmp.PixelFormat == PixelFormat.Format16bppGrayScale || Bitmap.GetPixelFormatSize(bmp.PixelFormat) > 32)
throw new NotSupportedException("Unsupported image format");
return AForge.Imaging.Image.Clone(bmp, PixelFormat.Format24bppRgb);
}
finally
{
bmp.Dispose();
}
}
如果你真的需要高性能,那么你可以选择NVidia CUDA / NPP(直接使用GPU),但这是更多的工作,而不是C#直接支持(当然需要NVidia卡)。相关问题:2D CUDA median filter optimization和关于CUDA的白皮书:Image Processing and Video Algorithms with CUDA
答案 2 :(得分:1)
您的代码采用的是附近9个像素的中值,实际上只是模糊。这不是一个好的降噪算法 - 它更像是一种模糊算法。研究解决方案所需的降噪算法(取决于您的噪音类型)并从那里开始。