去除斑点 – 从图像中去除斑点或点

我正在使用Winforms。 在我的表格中,我有一个显示黑白图像的图片框。 我还有一个按钮,如果你点击它,该按钮将删除图像上的斑点/点。 当图像尺寸不大时,它会快速消除斑点。 如果图像很大,则需要一段时间。 此外,有时这个函数会从它认为是一个点的图像中删除一些单词。 如何提高此function的性能,更准确地去除斑点或点基本上去除图像?

更新研究后,我发现这个库对于这个问题似乎很有希望:

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 = 0 && jj - 1 >= 0) { c = img.GetPixel(ii - 1, jj - 1); mask[0] = Convert.ToInt16(cR); } else { mask[0] = 0; } if (jj - 1 >= 0 && ii + 1 = 0) { c = img.GetPixel(ii, jj - 1); mask[2] = Convert.ToInt16(cR); } else mask[2] = 0; if (ii + 1 = 0) { c = img.GetPixel(ii - 1, jj); mask[4] = Convert.ToInt16(cR); } else mask[4] = 0; if (ii - 1 >= 0 && jj + 1 < img.Height) { c = img.GetPixel(ii - 1, jj + 1); mask[5] = Convert.ToInt16(cR); } else mask[5] = 0; if (jj + 1 < img.Height) { c = img.GetPixel(ii, jj + 1); mask[6] = Convert.ToInt16(cR); } else mask[6] = 0; if (ii + 1 < img.Width && jj + 1 < img.Height) { c = img.GetPixel(ii + 1, jj + 1); mask[7] = Convert.ToInt16(cR); } else mask[7] = 0; c = img.GetPixel(ii, jj); mask[8] = Convert.ToInt16(cR); Array.Sort(mask); int mid = mask[4]; img.SetPixel(ii, jj, Color.FromArgb(mid, mid, mid)); } } pictureBox1.Image = img; MessageBox.Show("Complete"); } 

正如您所知,使用AForge.NET是一个好主意(您只需将其添加为nuget)。 我建议你使用它的中值滤波器 ,它通常用于去噪(参见维基百科中的中位数滤波器 )。

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中值滤波器优化和关于CUDA的白皮书:CUDA的图像处理和video算法

正如评论中所提到的,要更改Bitmap像素而不是SetPixel ,您可以使用Bitmap.LockBits方法来访问位图数据。

为了以最少的更改使代码更快,您可以创建一个类,该类使用LockBits封装对位图数据的快速访问,并为类创建GetPixelSetPixel方法。


注意:答案只是尝试通过应用最少的更改来加快代码速度。 它不会对您的算法应用任何增强function,以便更好地降低噪音。


例如,我使用了由Vano Maisuradze编写的一个小类更改的类(我从代码中删除了不必要的try / catch块)。 该类使用LockBits方法,并提供快速版本的GetPixelSetPixel方法。

然后您的代码应更改为:

 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; } ///  /// Lock bitmap data ///  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); } ///  /// Unlock bitmap data ///  public void UnlockBits() { // Copy data from byte array to pointer Marshal.Copy(Pixels, 0, Iptr, Pixels.Length); // Unlock bitmap data source.UnlockBits(bitmapData); } ///  /// Get the color of the specified pixel ///  ///  ///  ///  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; } ///  /// Set the color of the specified pixel ///  ///  ///  ///  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; } } } 

您的代码采用了邻近像素的9的中值,实际上只是模糊。 这不是一个好的降噪算法 – 它更像是一种模糊算法。 研究解决方案所需的降噪算法(取决于您的噪音类型)并从那里开始。