为什么这个黑白位图到 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;
}
这是一个获取透明和白色图像并尝试将其转换为 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;
}