Как преобразовать массив байтов размером 19200 байт, где каждый байт представляет 4 пикселя (2 бита на пиксель), в растровое изображение, организованное в виде символов 320x240.

StackOverflow https://stackoverflow.com/questions/2969208

  •  24-10-2019
  •  | 
  •  

Вопрос

Я общаюсь с инструментом (дистанционно управляю им) и
одна из вещей, которые мне нужно сделать, это нарисовать экран прибора.

Чтобы получить экран, я даю команду, и прибор
отвечает массивом байтов, представляющим экран.

Ниже указано, что в руководстве к прибору говорится о преобразовании ответа в реальный экран:

Команда извлекает данные кадрового буфера, используемые для отображения.
Его размер составляет 19200 байт, 2 бита на пиксель, 4 пикселя на байт, упорядоченные как
320х240 символов.
Данные отправляются в закодированной форме RLE.
Чтобы преобразовать эти данные в BMP для использования в Windows, их необходимо
превратился в 4БПП.Также обратите внимание, что файлы BMP перевернуты относительно друг друга.
этим данным, т.е.верхняя строка дисплея — это последняя строка в BMP.

Мне удалось распаковать данные, но теперь я застрял в том, как на самом деле
перейти от распакованного массива байтов к растровому изображению.

Мой опыт в этом деле близок к нулю, и мои поиски
тоже многого не выявили.

Я ищу направления и/или статьи, которые могли бы мне помочь.
не понимаю, как это сделать.

Любой код или даже псевдокод также поможет.:-)

Итак, просто подведем итог:

Как преобразовать байтовый массив размером 19200 байт, где
каждый байт представляет 4 пикселя (2 бита на пиксель),
в растровое изображение размером 320x240 символов.

Заранее спасибо.

Это было полезно?

Решение

Чтобы сделать что-то подобное, вам понадобится такая процедура:

Bitmap ConvertToBitmap(byte[] data, int width, int height)
{
    Bitmap bm = new Bitmap(width, height, PixelFormat.Format24bppRgb);
    for (int y=0; y < height; y++) {
        for (int x=0; x < width; x++) {
            int value = ReadPixelValue(data, x, y, width);
            Color c = ConvertValToColor(value);
            bm.SetPixel(x, y, c);
        }
    }
    return bm;
}

отсюда вам нужны ReadPixelValue и ConvertValToColor.

static int ReadPixelValue(byte[] data, int x, int y, width)
{
    int pixelsPerByte = 4;
    // added the % pixelsPerByte to deal with width not being a multiple of pixelsPerByte,
    // which won't happen in your case, but will in the general case
    int bytesPerLine = width / pixelsPerByte + (width % pixelsPerByte != 0 ? 1 : 0);
    int index = y * bytesPerLine + (x / pixelsPerByte);
    byte b = data[index];

    int pixelIndex = (x % pixelsPerByte) * 2;
    // if every 4 pixels are reversed, try this:
    // int pixelIndex = 8 - (x % pixelsPerByte) * 2;
    return ((int b) >> pixelIndex) & 0x3;        
}

По сути, я извлекаю каждый набор из двух битов из каждого байта и возвращаю его как целое число.

Что касается преобразования в цвет, то вам решать, как составить орел или решку из четырех возвращаемых значений.Скорее всего, вы можете сделать что-то вроде этого:

static Color[] _colors = new Color[] { Color.Black, Color.Red, Color.Blue, Color.White };
static Color ConvertValToColor(int val)
{
    if (val < 0 || val > _colors.Length)
        throw new ArgumentOutOfRangeException("val");
    return _colors[val];
}

Другие советы

Если у вас есть два бита на пиксель, для каждого пикселя у вас есть 4 разных цвета.Вероятно, цвета проиндексированы или просто жестко запрограммированы (т.0 означает черный, 1 белый и т. д.).

Не знаю, сильно ли это поможет (я не знаю, какой растровый объект вы используете, но, возможно, он имеет обычную схему RGB или ARGB с 1 байтом на канал), но в псевдо-actionscript, я думаю, вам следует сделай что-нибудь вроде этого.

//  80 -> 320 / 4
for(var x:int = 0; x < 80; x++) {
    for(var y:int = 0; y < 240; y++) {
        var byteVal:int = readByte();

        var px_1:int = (byteVal >> 6) & 0x03;
        var px_2:int = (byteVal >> 4) & 0x03;
        var px_3:int = (byteVal >> 2) & 0x03;
        var px_4:int = (byteVal) & 0x03;

        //  map your pixel value to ARGB 
        px_1 = getPixelValue(px_1);
        px_2 = getPixelValue(px_2);
        px_3 = getPixelValue(px_3);
        px_4 = getPixelValue(px_4);     
        //  assuming setPixel(x,y,pixelValue)
        setPixel((x * 4), y, px_1);
        setPixel((x * 4) + 1, y, px_2);
        setPixel((x * 4) + 2, y, px_3);
        setPixel((x * 4) + 3, y, px_4);


    }
}

function getPixelValue(idx:int):uint {
    //   just an example...
    switch(idx) {
        case 0:     return 0xff000000;  //  black
        case 1:     return 0xffffffff;  //  white
        case 2:     return 0xffff0000;  //  red
        case 3:     return 0xff0000ff;  //  blue
    }
}

Достаточно сказать, что приведенный выше код предназначен просто для того, чтобы дать вам представление (надеюсь!), и основан на некоторых предположениях, например, о том, как эти четыре пикселя хранятся в байте.

Надеюсь, это имеет смысл.

Не знаю, поможет ли это, я использую это для данных, полученных от редкого старого оборудования:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            byte[] imageBytes = new byte[19201];
            //(Fill it with the data from the unit before doing the rest).
            Bitmap bmp_workarea = new Bitmap(320, 240, System.Drawing.Imaging.PixelFormat.Format4bppIndexed);
            Image newImage = Image.FromStream(new MemoryStream(imageBytes));
            using (Graphics gr = Graphics.FromImage(bmp_workarea))
            {
                gr.DrawImage(newImage, new Rectangle(0, 0, bmp_workarea.Width, bmp_workarea.Height));
            }
            //now you can use newImage, for example picturebox1.image=newimage

        }
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top