在C#中将int数组转换为BMP
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在C#中将int数组转换为BMP相关的知识,希望对你有一定的参考价值。
我在将一个灰度数组的int(int32 [,])转换为C#中的BMP格式时遇到了问题。我尝试在阵列中循环以在BMP中设置像素颜色,它确实有效,但最终变得非常慢并且实际上无法使用。我做了很多谷歌搜索,但我找不到我的问题的答案。我需要实时将该图像放在PictureBox中,因此该方法需要快速。
相关讨论here
编辑:数组的深度为8位,但存储为int32
Edit2:刚刚找到这段代码
private unsafe Task<Bitmap> BitmapFromArray(Int32[,] pixels, int width, int height)
{
return Task.Run(() =>
{
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
for (int y = 0; y < height; y++)
{
byte* row = (byte*)bitmapData.Scan0 + bitmapData.Stride * y;
for (int x = 0; x < width; x++)
{
byte grayShade8bit = (byte)(pixels[x, y] >> 4);
row[x * 3 + 0] = grayShade8bit;
row[x * 3 + 1] = grayShade8bit;
row[x * 3 + 2] = grayShade8bit;
}
}
bitmap.UnlockBits(bitmapData);
return bitmap;
});
}
似乎工作得足够快但图像几乎是黑色的。如果我移除相机的顶部,图像应该是完全白色的,但它只显示一个非常深的灰色。我猜它将像素值解释为32位,而不是8位。然后尝试投射(ushort)像素[x,y]但不起作用
我实际上是wrote a universally usable BuildImage
function here on SO to build an image out of a byte array,但当然,你不是从一个字节数组开始,你是从一个二维Int32
数组开始的。解决它的简单方法就是提前改造它。
你的byte-as-integer数组是一个相当奇怪的事情。如果从灰度图像中读取这个,我宁愿假设这是32位ARGB数据,而你只是使用每个值的最低分量(这将是蓝色的),但是如果将值降低4位产生了一致的黑暗价值观我倾向于接受你的话;否则下一个颜色成分(绿色)的位会渗入,也会产生明亮的颜色。
无论如何,除了沉思和猜测之外,这是我的实际答案。
您可能会认为每个值在倾注到8位图像时只是亮度,但这实际上是错误的。 System.Drawing
像素格式中没有特定类型来指示8位灰度,而8位图像是调色板,这意味着图像上的每个值都指的是调色板上的颜色。因此,要实际制作一个8位灰度图像,其中您的字节值指示像素的亮度,您需要明确定义一个调色板,其中调色板上0到255的索引包含来自(0,0的灰色) 0)到(255,255,255)。当然,这很容易生成。
此代码将您的数组转换为8位图像。它使用前面提到的BuildImage
函数。请注意,该函数不使用不安全的代码。使用Marshal.Copy
意味着永远不会直接处理原始指针,从而使代码得到完全管理。
public static Bitmap FromTwoDimIntArrayGray(Int32[,] data)
{
// Transform 2-dimensional Int32 array to 1-byte-per-pixel byte array
Int32 width = data.GetLength(0);
Int32 height = data.GetLength(1);
Int32 byteIndex = 0;
Byte[] dataBytes = new Byte[height * width];
for (Int32 y = 0; y < height; y++)
{
for (Int32 x = 0; x < width; x++)
{
// logical AND to be 100% sure the int32 value fits inside
// the byte even if it contains more data (like, full ARGB).
dataBytes[byteIndex] = (Byte)(((UInt32)data[x, y]) & 0xFF);
// More efficient than multiplying
byteIndex++;
}
}
// generate palette
Color[] palette = new Color[256];
for (Int32 b = 0; i < 256; b++)
palette[b] = Color.FromArgb(b, b, b);
// Build image
return BuildImage(dataBytes, width, height, width, PixelFormat.Format8bppIndexed, palette, null);
}
注意,即使整数是完整的ARGB值,上面的代码仍然可以完全相同;如果你只使用整数内四个字节中的最低字节,就像我说的那样,那只是整个ARGB整数的蓝色分量。如果图像是灰度图像,则所有三个颜色分量应该相同,因此您仍将获得相同的结果。
假设您发现自己使用相同类型的字节数组,其中整数实际上包含完整的32bpp ARGB数据,您必须移出所有四个字节值,并且不会生成灰色调色板,但除此之外,代码会非常相似。只是,每X次迭代处理4个字节。
public static Bitmap fromTwoDimIntArrayGray(Int32[,] data)
{
Int32 width = data.GetLength(0);
Int32 height = data.GetLength(1);
Int32 stride = width * 4;
Int32 byteIndex = 0;
Byte[] dataBytes = new Byte[height * stride];
for (Int32 y = 0; y < height; y++)
{
for (Int32 x = 0; x < width; x++)
{
// UInt32 0xAARRGGBB = Byte[] { BB, GG, RR, AA }
UInt32 val = (UInt32)data[x, y];
// This code clears out everything but a specific part of the value
// and then shifts the remaining piece down to the lowest byte
dataBytes[byteIndex + 0] = (Byte)(val & 0x000000FF); // B
dataBytes[byteIndex + 1] = (Byte)((val & 0x0000FF00) >> 08); // G
dataBytes[byteIndex + 2] = (Byte)((val & 0x00FF0000) >> 16); // R
dataBytes[byteIndex + 3] = (Byte)((val & 0xFF000000) >> 24); // A
// More efficient than multiplying
byteIndex+=4;
}
}
return BuildImage(dataBytes, width, height, stride, PixelFormat.Format32bppArgb, null, null);
}
当然,如果你想要这个没有透明度,你可以像你一样使用三个字节,或者只是在最后调用PixelFormat.Format32bppArgb
时更改PixelFormat.Format32bppRgb
,这会将第四个字节的含义从alpha改为仅仅填充。
解决了(不得不删除四位移位):
private unsafe Task<Bitmap> BitmapFromArray(Int32[,] pixels, int width, int height)
{
return Task.Run(() =>
{
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
for (int y = 0; y < height; y++)
{
byte* row = (byte*)bitmapData.Scan0 + bitmapData.Stride * y;
for (int x = 0; x < width; x++)
{
byte grayShade8bit = (byte)(pixels[x, y]);
row[x * 3 + 0] = grayShade8bit;
row[x * 3 + 1] = grayShade8bit;
row[x * 3 + 2] = grayShade8bit;
}
}
bitmap.UnlockBits(bitmapData);
return bitmap;
});
}
仍然不确定为什么用Format24bppRgb
替换Format8bppIndexed
不起作用。任何线索?
以上是关于在C#中将int数组转换为BMP的主要内容,如果未能解决你的问题,请参考以下文章