为什么这个黑白位图到 Bool 数组创建正确的图像但设置错误的 bools 数量 (C#)

Why Does This Black and White Bitmap to Bool Array create the correct images but set the wrong amount of bools (C#)

这是一个获取透明和白色图像并尝试将其转换为 bool 数组的函数。

我的单元测试代码给了我 2 个我期望的图像(见下文),但 "numberOfMasked" 总是比我期望的高。例如,如果 "maskBuffer" 标记了一个像素(参见下面的 mask_image_test.bmp),那么虽然 "mask_bool_test70.bmp") 创建了一个标记了一个像素的图像。由于某种原因,标记为 true 的 bool 的实际数量要高得多,而且看起来是随机的。我看到它的范围是 25 - > 70。

public static bool[] ConvertImageToBoolAray(Bitmap maskbuffer)
    {
        bool[] mask = null;
        int w = maskbuffer.Width;
        int h = maskbuffer.Height;

        #region unit_test
            maskbuffer.Save("mask_image_test.bmp");
        #endregion

        lock (maskbuffer)
        {
            BitmapData bmpData = maskbuffer.LockBits(new Rectangle(0, 0, w, h),
                ImageLockMode.ReadOnly,
                PixelFormat.Format8bppIndexed);

            const int numBmpChannel = 1;
            int maskIndex = 0;
            int bmpIndex = 0;

            unsafe
            {

                byte* pixels = (byte*) bmpData.Scan0;
                int numPixels = w*h;

                mask = new bool[numPixels];

                for (;
                    maskIndex < numPixels;
                    bmpIndex += numBmpChannel, maskIndex++)
                {
                    byte red = pixels[bmpIndex];
                    bool masked = red != 0;
                    mask[maskIndex] = masked;
                }

            }
            maskbuffer.UnlockBits(bmpData);
        }

        #region unit_test
            byte[] boolAsByte = Array.ConvertAll(mask, b => b ? (byte)1 : (byte)0);
            Bitmap maskBitmap = GLImageConvertor.ConvertByteBufferToBitmap(boolAsByte, w, h, PixelFormat.Format8bppIndexed);
            int numberOfMasked = mask.Count(b => b);
            maskBitmap.Save("mask_bool_test" + numberOfMasked + ".bmp");
        #endregion


        return mask;
    }

有人对这种奇怪的行为有任何想法吗?

调试显示在 "pixels" 内存中,有一些我不希望看到的字节点,我希望看到一个字节设置为 "FF" 但我有随机的数据部分.

mask_image_test.bmp:

mask_bool_test70.bmp:

Stride and Width 之间存在差异,您需要在算法中考虑。

The stride is the width of a single row of pixels (a scan line), rounded up to a four-byte boundary.

由于70不能被4整除,所以有一些空余像素,可能是随机填充的。

你需要这样的东西

int count = 0;
int stride = bmpData.Stride;

for (int column = 0; column < bmpData.Height; column++)
{
    for (int row = 0; row < bmpData.Width; row++)
    {
        red = pixels[(column * stride) + (row * 3) + 2]);
    }
}

正如 Thomas 所指出的,我没有考虑步幅!一秒钟也没有想到C#会把位图中多余的字节打包出来。但在这种情况下确实足够 width != stride (宽度为 111 但步幅为 112)。我决定写一个更简单的版本,完全避免原始缓冲区以避免粘性问题(而且我不能再为原始缓冲区烦恼)

 public static bool[] ConvertImageToBoolAray(Bitmap maskbuffer)
    {
        bool[] mask = null;
        int w = maskbuffer.Width;
        int h = maskbuffer.Height;

        #region unit_test
        //maskbuffer.Save("mask_image_test.bmp");
        #endregion

        lock (maskbuffer)
        {
            int numPixels = w * h;
            mask = new bool[numPixels];

            for (int y = 0; y < maskbuffer.Height; ++y)
            {
                for (int x = 0; x < maskbuffer.Width; ++x)
                {
                    Color color = maskbuffer.GetPixel(x, y);
                    int index = x + (y*maskbuffer.Width);
                    mask[index] = color.A != 0;
                }
            }
        }

        #region unit_test
        //byte[] boolAsByte = Array.ConvertAll(mask, b => b ? (byte)1 : (byte)0);
       // Bitmap maskBitmap = GLImageConvertor.ConvertByteBufferToBitmap(boolAsByte, w, h, PixelFormat.Format8bppIndexed);
       // int numberOfMasked = mask.Count(b => b);
       // maskBitmap.Save("mask_bool_test" + numberOfMasked + ".bmp");
        #endregion


        return mask;
    }

如果有人想要 post 正确的原始缓冲区版本,请继续,我会将您的答案标记为最佳答案。

---更新--- 添加了返工版本以直接使用缓冲区

   private static bool[] ConvertImageToBoolArayUnsafe(Bitmap maskbuffer)
    {
        bool[] mask = null;
        int w = maskbuffer.Width;
        int h = maskbuffer.Height;

        #region unit_test
            //maskbuffer.Save("mask_image_test.bmp");
        #endregion

        lock (maskbuffer)
        {
            BitmapData bmpData = maskbuffer.LockBits(new Rectangle(0, 0, w, h),
                ImageLockMode.ReadOnly,
                PixelFormat.Format8bppIndexed);


            int stride = bmpData.Stride;
            unsafe
            {

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


                mask = new bool[w * h];

                for (int y = 0; y < h; ++y)
                {
                    for (int x = 0; x < w; ++x)
                    {
                        int imageIndex = x + (y * stride);
                        byte color = pixels[imageIndex];
                        int maskIndex = x + (y * w);
                        mask[maskIndex] = color != 0;
                    }
                }

            }
            maskbuffer.UnlockBits(bmpData);
        }

        #region unit_test
           // byte[] boolAsByte = Array.ConvertAll(mask, b => b ? (byte)1 : (byte)0);
          //  Bitmap maskBitmap = GLImageConvertor.ConvertByteBufferToBitmap(boolAsByte, w, h, PixelFormat.Format8bppIndexed);
          //  int numberOfMasked = mask.Count(b => b);
          //  maskBitmap.Save("mask_bool_test" + numberOfMasked + ".bmp");
        //check equality to safe version (as that is correct)
        #endregion


        return mask;
    }