Как отображать необработанные данные в виде изображения (Visual Studio С#)
Я получаю некоторые необработанные данные, которые будут храниться в байтовом массиве, где каждый 2 байта является значением пикселя (16 бит /px ). Для начала, массив будет содержать 100x100 * 2 байта (достаточно для изображения размером 100x100 пикселей). Я хотел бы отобразить эти данные в окне формы. В конце концов, я хотел бы обновить изображение с новыми данными, чтобы он выглядел как видеопоток. Не требуется строгая частота кадров. Как это может быть сделано? Любые примеры кода на С#?
EDIT:
После некоторых предложений и обзоров десятков подобных вопросов я до сих пор не могу этого добиться. Вот общая идея того, что я пытаюсь сделать, но изображение не отображается в окне изображения на форме. Что конкретно неправильно с моей реализацией и как ее исправить?
// 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);
Ответы
Ответ 1
Основная проблема заключается в том, что 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;
}
Идея была взята из этот поток.
Ответ 2
Используйте этот 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 not a handle, it a pointer
p[0]=0; // access it like any normal pointer
Однако вы можете использовать Marshaller для копирования памяти для вас из управляемого в неуправляемый (получение грязных рук обычно нахмуривается на С#):
Marshal.Copy(dataArray, 0, hglobal, dataArray.Length); // again, terrible name
A Bitmap
- это Image
(как и в нем, он вытекает из него), однако вы используете Graphics.DrawImage()
неправильно. Как говорит ошибка, это не статический метод, вы рисуете его в определенном графическом контексте. Теперь, каков этот графический контекст, что до вас:
- Если вы хотите нарисовать его в ответ на
WM_PAINT
, используйте событие Paint
- он предоставляет вам специальный объект Graphics
, настроенный с отсечением, и все, как указано в системе окон.
- Если вы хотите покрасить его в растровое изображение, которое впоследствии будет отображаться (обычное использование, также называемое двойной буферизацией), используйте
Graphics.FromImage()
в исходном растровом изображении, затем нарисуйте его растровое изображение.
Вы можете (и должны) удалить буфер виртуальной памяти, как только получите результат от конструктора Bitmap
. Не просачивайте память, используйте конструкцию try..finally
.