winforms 去除杂点-从图像中删除斑点或点

mutmk8jj  于 2022-12-14  发布在  其他
关注(0)|答案(4)|浏览(253)

我使用Winforms。在我的窗体中,我有一个显示白色图像的图片框。我还有一个按钮,如果你单击它,这个按钮可以去除图像上的斑点/点。2当图像尺寸不大时,它可以快速地去除斑点。3如果图像很大,它需要一段时间。4有时这个功能也会从图像中去除一些它认为是斑点的单词。如何提高此功能的性能,并更准确地去除斑点或点,基本上消除图像的斑点?

更新经过研究,我发现这个库似乎很有希望解决这个问题:

http://www.aforgenet.com/framework/docs/html/cdf93487-0659-e371-fed9-3b216efb6954.htm

斑点图像链接:http://www.filedropper.com/testing-image3
图像示例

  • 请注意,链接中的图片有此图片的更大版本:*

图像信息

  • 这里需要注意的是,这是一个白色图像-位深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");
    }
yx2lnoni

yx2lnoni1#

正如在注解中提到的,要在Bitmap中更改像素,使其具有比SetPixel更好的性能,可以使用Bitmap.LockBits方法访问位图数据。
若要以最少的变更使程式码更快,您可以建立类别,使用LockBits封装位图数据的快速存取,并为类别建立GetPixelSetPixel方法。

***注意:****答案只是尝试通过应用最少的更改来提高代码的速度。它不会在算法中应用任何增强功能来更好地减少噪音。*示例

例如,我使用了一个由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;
    }

    /// <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;
        }
    }
}
kmb7vmvb

kmb7vmvb2#

正如你所发现的,使用AForge.NET是一个好主意(你只需要把它作为一个nuget来添加),我建议你使用它的Median filter,它经常被用于去噪(参见wikipedia中的Median Filter)。
AForge需要24 bpp的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

nnsrf1az

nnsrf1az3#

你的代码取了9个附近像素的中值,实际上只是模糊。这不是一个好的降噪算法--它更像是一个模糊算法。研究你的解决方案需要哪种降噪算法(取决于你的噪声类型),然后从那里开始。

of1yzvn4

of1yzvn44#

在处理另一个项目时发现,无需将图像转换为位图,然后使用Get泛指el和Set泛指el函数,只需将图像转换为位图以调用Get泛指el和Set泛指el函数即可。这样效率更高,使用的内存也少得多。**示例:**使用((Bitmap)pictureBox1.Image).GetPixel(ii-1,jj-1);而不是img.GetPixel(ii - 1, jj - 1);

相关问题