【问题标题】:C#, Lockbits image processing with artefacts, changing pixel formatC#,Lockbits 图像处理与人工制品,改变像素格式
【发布时间】:2018-08-14 07:23:55
【问题描述】:

我正在实现我自己的函数来二值化指纹图像。在我的方法中,我第一次尝试使用LockBits

你能解释一下,为什么我的图像上有很多人工制品吗?示例:

在左边的图片上,我通过Get/SetPixel 对图像进行了二值化处理,效果还不错,但是为什么我在右边的图像上不能得到同样好的结果(很多红点)?我是忘记了还是不知道什么?

private Bitmap Binarization(Bitmap tempBmp)
    {

        int threshold = otsuValue(tempBmp); //calculating threshold with Otsu method

        unsafe
        {
            BitmapData bmpData = tempBmp.LockBits(new System.Drawing.Rectangle(0, 0, tempBmp.Width, tempBmp.Height), ImageLockMode.ReadWrite, tempBmp.PixelFormat);
            byte* ptr = (byte*)bmpData.Scan0;


            int height = tempBmp.Height;
            int width = bmpData.Width * 4;
            Parallel.For(0, height, y =>
            {
                byte* offset = ptr + (y * bmpData.Stride); //set row
                for (int x = 0; x < width; x = x + 4)
                {
                    //changing pixel value 
                    offset[x] = offset[x] > threshold ? Byte.MaxValue : Byte.MinValue;
                    offset[x+1] = offset[x+1] > threshold ? Byte.MaxValue : Byte.MinValue;
                    offset[x+2] = offset[x+2] > threshold ? Byte.MaxValue : Byte.MinValue;
                    offset[x+3] = offset[x+3] > threshold ? Byte.MaxValue : Byte.MinValue;

                }
            });

            tempBmp.UnlockBits(bmpData);
        }

        return tempBmp;
    }

同样的历史,当我想从图像中删除一点字节,但问题看起来有点复杂。

为什么它甚至没有进入好的“if”语句?

private Bitmap Binarization(Bitmap tempBmp)
    {

        int threshold = otsuValue(tempBmp);

        unsafe
        {
            BitmapData bmpData = tempBmp.LockBits(new System.Drawing.Rectangle(0, 0, tempBmp.Width, tempBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); 
            //Format8bpp, not pixel format from image
            byte* ptr = (byte*)bmpData.Scan0;


            int height = tempBmp.Height;
            int width = bmpData.Width; //i cut "* 4" here because of one channel image
            Parallel.For(0, height, y =>
            {
                byte* offset = ptr + (y * bmpData.Stride); //set row
                for (int x = 0; x < width; x++)
                {
                    //changing pixel values
                    offset[x] = offset[x] > threshold ? Byte.MaxValue : Byte.MinValue;

                }
            });

            tempBmp.UnlockBits(bmpData);
        }

        return tempBmp;
    }

感谢任何关于改进我的功能的建议。

【问题讨论】:

  • offset[x+3] = offset[x+3] > 阈值 ? Byte.MaxValue : Byte.MinValue; - 你不应该将 alpha 值设置为 255 以外的任何值!试试:offset[x+3] = Byte.MaxValue;
  • 我尝试了很多东西,即使没有offset+3,我也得到了那个红点,我不知道为什么。也许我应该使用Marshall.Copy
  • 在彩色图像中,我在 lockbits 中获得了更多的伪像(Get/SetPixel 非常适合二值化)
  • 我尝试了很多东西,即使没有 offset+3 嗯??你知道offset+3 是什么吗? (它是 alpha 通道,即透明度,始终应为 255 !!!)
  • 好吧,这叫三元运算符。好吧,您确实需要将 alpha 通道设置为 255。没有 with or without !至于您使用的算法:它并不适合保证黑白结果。您正在分别测试 RGB 通道,因此如果 R > 阈值,它将被打开。事实上,真正的问题是为什么没有更多的人工制品出现。必须与源图像有关。解决方案:将所有三个通道相加并与阈值* 3 比较;然后将所有设置为相同的黑色或白色值!

标签: c# parallel-processing task-parallel-library lockbits


【解决方案1】:

1) 您使用的算法不保证黑白结果。

您正在测试 RGB 通道单独,所以如果R &gt; thresholdG or B &lt; threshold 它将被打开但G and/or B 不是等等..

事实上,真正的问题是为什么没有更多的工件出现。必须与源图像有关。

解决方案:添加所有三个通道并与阈值 * 3 进行比较;然后将所有设置为相同的黑色或白色值!

2)还有另一个问题:您从某处复制的代码盲目地假设图像具有 32 bpp。但是,虽然您发布的图像是png,但它仍然只有 24 bpp。因此,您的代码会做各种有趣的事情..

更新和更正示例:

        ..
        int pixWidth = tempBmp.PixelFormat == IMG.PixelFormat.Format24bppRgb ? 3 : 
                       tempBmp.PixelFormat == IMG.PixelFormat.Format32bppArgb ? 4 : 4;


        byte* ptr = (byte*)bmpData.Scan0;

        threshold3 = threshold * 3;
        int height = tempBmp.Height;
        int width = bmpData.Width * pixWidth;
        Parallel.For(0, height, y =>
        {
            byte* offset = ptr + (y * bmpData.Stride); //set row
            for (int x = 0; x < width; x = x + pixWidth)
            {
                //changing pixel value 
                int v = (offset[x] + offset[x + 1] + offset[x + 2]) > threshold3 ? 
                        Byte.MaxValue : Byte.MinValue;;
                offset[x] = (byte)v ;
                offset[x+1] = (byte)v;
                offset[x+2] = (byte)v;
                if (pixWidth == 4) offset[x+3] = 255;

            }
        });

【讨论】:

  • 感谢您的建议,但复制了您对我的问题的解决方案,我在图像上得到了更多的人工制品。我知道它背后的逻辑,因为它看起来与内置操作像素(颜色、setpixel 等)的方法非常相似,但不知何故我不知道如何正确使用它。在我看来,它应该可以工作,但不能正常工作。我编写了您的示例,输出现在看起来像:IMAGE 我将尝试对 marshall 副本执行相同的操作。也许Threading.Tasks 会造成这个问题?马歇尔独奏也很快
  • 哎呀,你是对的。这就是你发布未经测试的代码所得到的。很快就会更新..
  • 另一个问题是您的图像实际上是 24bpp。看看更新!
  • 我只是不知道,.pngs 图像是 24bpp,现在它看起来很清晰并且完美运行。但是要将图像转换为 8bpp 我需要不同的方法吗?就像我看到的那样,它不像我的例子那样容易(tempBmp.LockBits(new System.Drawing.Rectangle(0, 0, tempBmp.Width, tempBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed
  • pngs 图片是 24bpp 它们可以是 24 或 32 bpp。 - 8bpp:是的,除了通道位之外,他们还需要一个调色板。
【解决方案2】:

对于这个问题,我的解决方案如下所示:

public static bool Offset(Bitmap b_mapa, int threshold)
        {
            int threshold3 = threshold * 3;
            int red, green, blue;
            BitmapData bmData = b_mapa.LockBits(new Rectangle(0, 0, b_mapa.Width, b_mapa.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            int stride = bmData.Stride;
            System.IntPtr Scan0 = bmData.Scan0;
            unsafe
            {
                byte* p = (byte*)(void*)Scan0;
                int nOffset = stride - b_mapa.Width * 3;
                for (int y = 0; y < b_mapa.Height; ++y)
                {
                    for (int x = 0; x < b_mapa.Width; ++x)
                    {
                        blue = p[0];
                        green = p[1];
                        red = p[2];
                        int v = ((int)(blue + green + red) > threshold3 ? (int)255 : (int)0);
                        p[0] = p[1] = p[2] = (byte)v;
                        p += 3;
                    }
                    p += nOffset;
                }
            }
            b_mapa.UnlockBits(bmData);
            return true;
        }

此函数使用名为 b_mapa 的共享资源,我们的函数应根据处理结果返回一个布尔值。我在这里不使用并行功能,因为当我们使用 LockBits 时,我们直接使用内存,而不使用可以快 1000 倍且没有必要并行化的嵌入式函数。如果过滤器是卷积的,那么并行化可以用于更快的代码。购买方式,当我加载图像时,我将它们转换为位图,以后做任何事情都更简单,不用考虑我有多少位用于颜色表示。

如您所见,位图数组中的值是 BGR 而不是 RGB,只是为了让您知道,因为许多人会犯这个错误并弄乱颜色。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-24
    • 1970-01-01
    • 2019-08-28
    • 1970-01-01
    相关资源
    最近更新 更多