Как вывести сырое изображение (2 картинки) из массива байт формата RGB565 в PictureBox или сохранить в одном из форматов
Проблема в функции ReCalcArrDoubleImg, код которой приведен ниже (параметры width и height указаны в пикселях (2 байта на пиксель)). Прошу прощения за длинный вопрос. Существует физическое устройство на ПЛИС с оперативной памятью и камерой. Камера передает данные в формате RGB565 - 2 байта на пиксель. Разрешение камеры 640 * 480. Размер одного кадра = 614400 байт. Во время съемки 1-й кадр с камеры помещается в оперативную память последовательно побайтово по адресу 0-614399, далее 2-й кадр помещается следом по адресу 614400-1228799. После того как оба кадра записаны в память, эти данные отправляются по UART на ПК с 0-го адреса по 1228799-й. На ПК (ПО C# Windows Forms) все принятые данные записываются в байтовый массив размером 1228800 байт, после чего должны быть выведены в PictureBox и сохранены в виде изображения в формате PNG. Массив байт содержит 2 последовательных изображения и я хочу вывести эту картинку в виде одного склеенного изображения размером 1280 * 480 пикселей. В этом заключается проблема. Не получается преобразовать массив таким образом. С выводом одного кадра 640 * 480 нет проблем. Функция создания изображения из массива байт:
/// <summary>
/// Преобразование Image из байтового массива
/// </summary>
/// <param name="bytes">Массив байтов</param>
/// <param name="bytesLen">Количество используемых байтов в массиве</param>
/// <param name="width">Ширина</param>
/// <param name="height">Высота</param>
/// <param name="pf">Формат пикселей</param>
/// <returns>Image</returns>
public static Image ImageFromRawBGRAArray(byte[] bytes, int bytesLen, int width, int height, PixelFormat pf)
{
var output = new Bitmap(width, height, pf);
var rect = new Rectangle(0, 0, width, height);
var bmpData = output.LockBits(rect, ImageLockMode.ReadWrite, output.PixelFormat);
var ptr = bmpData.Scan0;
Marshal.Copy(bytes, 0, ptr, bytesLen);
output.UnlockBits(bmpData);
return output;
}
Вывод изображения в PictureBox:
PictureBox.Image = ImageFromRawBGRAArray(array_DataBytes, array_DataBytes.Length, ImgWidth, ImgHeight, PixelFormat.Format16bppRgb565);
Сохранение изображения в PNG:
PictureBox.Image.Save(ImgName + ".png", ImageFormat.Png);
Проблемная функция, которая должна трансформировать входной массив байт под данную задачу:
/// <summary>
/// Трансформация массива для 2х склеенных кадров (2 камеры. адресация кадров -> сначала 1й кадр целиком, потом 2й)
/// </summary>
/// <param name="inputArr">Входной массив</param>
/// <param name="width">Ширина кадра в пикселях (2 байта на пиксель)</param>
/// <param name="height">Высота кадра в пикселях (2 байта на пиксель)</param>
/// <returns>Выходной массив</returns>
public byte[] ReCalcArrDoubleImg(byte[] inputArr, int width, int height)
{
byte[] outArr = new byte[inputArr.Length];
byte[] inputArr1 = new byte[inputArr.Length / 2];
byte[] inputArr2 = new byte[inputArr.Length / 2];
//разделение входного массива пополам
for (int i = 0; i < (inputArr.Length / 2); i++)
{
inputArr1[i] = inputArr[i];
inputArr2[i] = inputArr[i + (inputArr.Length / 2)];
}
/*
* Преобразование массива по типу:
* Допустим ширина 6, высота 4.
* Входной: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23
* 1) 1/2 входного: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
* 2) 1/2 входного: 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23
* Выходной: 0, 1, 2, 12 13, 14, 3, 4, 5, 15, 16, 17, 6, 7, 8, 18, 19, 20, 9, 10, 11, 21, 22, 23
* !!! В одном пикселе 2 байта, соответственно трансформируем по 2 байта сразу.
*/
for (int j = 0; j < height; j++)
{
for (int i = 0; i < width; i += 2)
{
//Для пары байт (каждый пиксель) (i += 2)
if (i < (width / 2))
{
outArr[i + j * width] = inputArr1[i + (width / 2) * j];
outArr[i + 1 + j * width] = inputArr1[i + 1 + (width / 2) * j];
}
else
{
outArr[i + j * width] = inputArr2[i - (width / 2) + (width / 2) * j];
outArr[i + 1 + j * width] = inputArr2[(i + 1) - (width / 2) + (width / 2) * j];
}
}
}
return outArr;
}
В данной функции я пытаюсь разделить массив из 2-х изображений на 2 массива, после чего оперировать ими так, что бы получить картинку, которую хочу, но я вижу совсем не то. Подскажите где ошибка или как это можно сделать лучше, как исправить функцию "ReCalcArrDoubleImg"? 
Дополню примером изображения 1-го кадра из массива как есть (614400 байт), здесь все работает как надо, функция "ReCalcArrDoubleImg" не используется:
На следующем изображении 2 кадра из массива (1228800 байт) с использованием функции "ReCalcArrDoubleImg". Здесь картинка не корректная, будто обрезана снизу:
Здесь примерное изображение, которое я бы хотел получить, это склеенное изображение из 2-х разных кадров (создано в фоторедакторе, белой разделительной линии, конечно, быть не должно - она отображает место склейки, 2 изображения могут быть разными:
Если код функции "ReCalcArrDoubleImg" меняю на такой:
/// <summary>
/// Трансформация массива для 2х склеенных кадров (2 камеры. адресация кадров -> сначала 1й кадр целиком, потом 2й)
/// </summary>
/// <param name="inputArr">Входной массив</param>
/// <param name="width">Ширина кадра в пикселях (2 байта на пиксель)</param>
/// <param name="height">Высота кадра в пикселях (2 байта на пиксель)</param>
/// <returns>Выходной массив</returns>
public byte[] ReCalcArrDoubleImg(byte[] inputArr, int width, int height)
{
byte[] outArr = new byte[inputArr.Length];
byte[] inputArr1 = new byte[inputArr.Length / 2];
byte[] inputArr2 = new byte[inputArr.Length / 2];
//разделение входного массива пополам
for (int i = 0; i < (inputArr.Length / 2); i++)
{
inputArr1[i] = inputArr[i];
inputArr2[i] = inputArr[i + (inputArr.Length / 2)];
}
/*
* Преобразование массива по типу:
* Допустим ширина 6, высота 4.
* Входной: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23
* 1) 1/2 входного: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
* 2) 1/2 входного: 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23
* Выходной: 0, 1, 2, 12 13, 14, 3, 4, 5, 15, 16, 17, 6, 7, 8, 18, 19, 20, 9, 10, 11, 21, 22, 23
* !!! В одном пикселе 2 байта, соответственно трансформируем по 2 байта сразу.
*/
for (int j = 0; j < height; j++)
{
for (int i = 0; i < width; i += 2)
{
//Для пары байт (каждый пиксель) (i += 2)
if (i < (width / 2))
{
outArr[i + j * width * 2] = inputArr1[i + (width) * j];
outArr[i + 1 + j * width * 2] = inputArr1[i + 1 + (width) * j];
}
else
{
outArr[i + j * width * 2] = inputArr2[i + (width) * j];
outArr[i + 1 + j * width * 2] = inputArr2[i + 1 + (width) * j];
}
}
}
return outArr;
}
То получается лучше, оба изображения уже отображаются корректно, но они как бы сплющены в 2 раза и занимают левую часть окна по ширине в 1 кадр, а не в 2 как должно быть. Правая часть при этом заполнена черным, чего быть не должно. Исходный массив изображений сделал здесь таким, что бы изображения отличались по цвету для того что бы лучше понимать где одна картинка, а где вторая. Черного цвета в обеих изображениях нет. Итоговое изображение получилось таким с учетом измененной функции "ReCalcArrDoubleImg":
Ответы (2 шт):
Давайте попробуем без массивов-половинок. Здесь width, height - единичные размеры, т.е. 640, 480 в вашем случае
for (row=0; row < height; row++) {
for (col=0; col < width; col++) {
out[(row*width*2+col)*2] = in[(row*width+col)*2]
out[(row*width*2+col)*2+1] = in[(row*width+col)*2+1]
}
for (col=0; col < width; col++) {
out[(row*width*2+width+col)*2] = in[((row+height)*width+col)*2]
out[(row*width*2+width+col)*2+1] = in[((row+height)*width+col)*2+1]
}
}
Выкладываю здесь в качестве второго ответа полный рабочий код функции. Благодарю @MBo за решение проблемы, описанное выше.
/// <summary>
/// Трансформация массива для 2х склеенных кадров (2 камеры. адресация кадров -> сначала 1й кадр целиком, потом 2й)
/// </summary>
/// <param name="inputArr">Входной массив</param>
/// <param name="width">Ширина кадра в пикселях (2 байта на пиксель)</param>
/// <param name="height">Высота кадра в пикселях (2 байта на пиксель)</param>
/// <returns>Выходной массив</returns>
public byte[] ReCalcArrDoubleImg(byte[] inputArr, int width, int height)
{
width /= 2;
byte[] outArr = new byte[inputArr.Length];
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
outArr[(row * width * 2 + col) * 2] = inputArr[(row * width + col) * 2];
outArr[(row * width * 2 + col) * 2 + 1] = inputArr[(row * width + col) * 2 + 1];
}
for (int col = 0; col < width; col++)
{
outArr[(row * width * 2 + width + col) * 2] = inputArr[((row + height) * width + col) * 2];
outArr[(row * width * 2 + width + col) * 2 + 1] = inputArr[((row + height) * width + col) * 2 + 1];
}
}
return outArr;
}
Полученное изображение (из массива 1228800 байт в формате RGB565 (2 байта на пиксель) разрешением 1280 * 480 пикселей) теперь выводится корректно:
