我将接收一些将存储在字节数组中的原始数据,其中每2个字节是一个像素值(16位/ px)。首先,该阵列将包含100x100 * 2字节(足以容纳100x100像素的图像)。我想在窗体窗口中显示此数据。最后,我想用新数据刷新图像,使其看起来像一个视频流。不需要严格的帧速率。如何才能做到这一点? C#中的任何代码示例?
修改 经过对一些类似问题的一些建议和评论后,我仍然无法做到这一点。这是我想要做的一般想法,但图像不会显示在表单上的图片框中。 我的实施有什么特别的错误以及如何解决??
// array of data I collected
byte[] dataArray = new byte[100 * 100 * 2];
//create a pointer to the data
IntPtr hglobal = Marshal.AllocHGlobal(100 * 100 * 2);
// copy my array to global
Marshal.Copy(dataArray, 0, hglobal, dataArray.Length);
// create a bitmap: 100x100 pixels, 2bytes/pixel, 16bitgrayscale
Bitmap newBitmap = new Bitmap(100, 100, 2 * 100, PixelFormat.Format16bppGrayScale, hglobal);
// display bitmap
pictureBox1.Image = newBitmap;
// free the memory
Marshal.FreeHGlobal(hglobal);
答案 0 :(得分:8)
主要问题是不支持PixelFormat.Format16bppGrayScale(至少在我的Win 8.1 x64系统上)。因此,您必须在显示之前将图像转换为rgb:
private void Form1_Load(object sender, EventArgs e)
{
//Create pixel data to put in image, use 2 since it is 16bpp
Random r = new Random();
int width = 100;
int height = 100;
byte[] pixelValues = new byte[width * height * 2];
for (int i = 0; i < pixelValues.Length; ++i)
{
// Just creating random pixel values for test
pixelValues[i] = (byte)r.Next(0, 256);
}
var rgbData = Convert16BitGrayScaleToRgb48(pixelValues, width, height);
var bmp = CreateBitmapFromBytes(rgbData, width, height);
// display bitmap
pictureBox1.Image = bmp;
}
private static byte[] Convert16BitGrayScaleToRgb48(byte[] inBuffer, int width, int height)
{
int inBytesPerPixel = 2;
int outBytesPerPixel = 6;
byte[] outBuffer = new byte[width * height * outBytesPerPixel];
int inStride = width * inBytesPerPixel;
int outStride = width * outBytesPerPixel;
// Step through the image by row
for (int y = 0; y < height; y++)
{
// Step through the image by column
for (int x = 0; x < width; x++)
{
// Get inbuffer index and outbuffer index
int inIndex = (y * inStride) + (x * inBytesPerPixel);
int outIndex = (y * outStride) + (x * outBytesPerPixel);
byte hibyte = inBuffer[inIndex + 1];
byte lobyte = inBuffer[inIndex];
//R
outBuffer[outIndex] = lobyte;
outBuffer[outIndex + 1] = hibyte;
//G
outBuffer[outIndex + 2] = lobyte;
outBuffer[outIndex + 3] = hibyte;
//B
outBuffer[outIndex + 4] = lobyte;
outBuffer[outIndex + 5] = hibyte;
}
}
return outBuffer;
}
private static Bitmap CreateBitmapFromBytes(byte[] pixelValues, int width, int height)
{
//Create an image that will hold the image data
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format48bppRgb);
//Get a reference to the images pixel data
Rectangle dimension = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData picData = bmp.LockBits(dimension, ImageLockMode.ReadWrite, bmp.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
//Copy the pixel data into the bitmap structure
System.Runtime.InteropServices.Marshal.Copy(pixelValues, 0, pixelStartAddress, pixelValues.Length);
bmp.UnlockBits(picData);
return bmp;
}
想法取自this thread。
答案 1 :(得分:5)
使用此Bitmap
构造函数:
public Bitmap(
int width,
int height,
int stride,
PixelFormat format,
IntPtr scan0
)
您将位图的形状,步幅(每行包含多少字节,包括填充),像素格式和像素数据作为void *
指针传递给它。您可以使用Marshal.AllocHGlobal
创建后者,并使用指针操作正常填充它。在创建位图后,不要忘记释放内存。
编辑以解决更新的问题:
只需调用IntPtr.ToPointer()
即可获取指针。如果您熟悉C,其余的应该是蛋糕:
var p=(char *)hglobal.ToPointer(); // bad name by the way, it's not a handle, it's a pointer
p[0]=0; // access it like any normal pointer
但是,您可以使用Marshaller将内存从托管复制到非托管(在C#中通常不赞同你的手):
Marshal.Copy(dataArray, 0, hglobal, dataArray.Length); // again, terrible name
Bitmap
是Image
(因为它来自它),但是您使用Graphics.DrawImage()
错误。正如错误所说,它不是静态方法,您将其绘制到特定的图形上下文。现在,图形背景是什么,这取决于你:
WM_PAINT
绘制它,请使用Paint
事件 - 它会为您提供一个特殊的Graphics
对象设置剪辑和一切按照窗口系统。Graphics.FromImage()
,然后在其上绘制位图。一旦从Bitmap
构造函数返回结果,就可以(并且应该)删除虚拟内存缓冲区。不要泄漏内存,使用try..finally
构造。