代码之家  ›  专栏  ›  技术社区  ›  Klinger

如何将大小为19200字节的字节数组(其中每个字节代表4个像素(每像素2位)转换为排列为320x240个字符的位图

  •  1
  • Klinger  · 技术社区  · 15 年前

    我正在与一个仪器通信(远程控制它),并且
    我需要做的一件事就是画出仪器屏幕。

    为了得到屏幕,我发出命令和仪器
    用表示屏幕的字节数组答复。

    下面是仪器手册关于将响应转换为实际屏幕的内容:

    该命令检索用于显示的帧缓冲区数据。
    它的大小为19200字节,每像素2位,每字节4个像素,按
    320x240个字符。
    数据以RLE编码的形式发送。
    要将此数据转换为在Windows中使用的BMP,需要
    变成4bpp。还要注意,BMP文件是上下颠倒的相对文件
    对于此数据,即顶部显示行是BMP中的最后一行。

    我设法打开了数据包,但现在我一直在研究如何
    从解包的字节数组转到位图。

    我在这方面的背景非常接近零,我的搜索
    也没有透露多少。

    我在找可以帮助我的方向和/或文章
    不知道该如何完成。

    任何代码甚至伪代码也会有所帮助。-)

    所以,总结一下:

    如何转换19200字节大小的字节数组,其中
    每个字节代表4个像素(每像素2位),
    以320x240个字符排列的位图。

    事先谢谢。

    3 回复  |  直到 7 年前
        1
  •  2
  •   plinth    15 年前

    要执行这样的操作,您需要这样的例行程序:

    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;        
    }
    

    基本上,我从每个字节中提取每组两个位,并将其作为int返回。

    至于转换为颜色,这取决于您如何使返回的4个值的头或尾。 很可能你可以这样做:

    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];
    }
    
        2
  •  1
  •   Juan Pablo Califano    15 年前

    如果每个像素有两个位,则每个像素有4种可能的颜色。可能颜色是索引的或只是硬编码的(即0表示黑色,1表示白色等)。

    不知道这是否有很大帮助(我不知道您正在使用什么位图对象,但可能它有一个规则的RGB或argB方案,每个通道有1个字节),但在伪操作脚本中,我认为您应该这样做。

    //  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
        }
    }
    

    上面的代码,足以说明,只是给你一个想法(希望!)它基于一些假设,比如这四个像素是如何存储在一个字节中的。

    希望这是合理的。

        3
  •  0
  •   Stefan    15 年前

    我不知道这是否有帮助,我将它用于我从一个罕见的旧硬件获得的数据:

    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
    
            }
        }
    }