将 RGB 444 转换为可显示的图像

Posted

技术标签:

【中文标题】将 RGB 444 转换为可显示的图像【英文标题】:Convert RGB 444 to a displayable image 【发布时间】:2014-02-26 09:50:13 【问题描述】:

我在嵌入式设备中使用了一种奇怪的图像格式,并且像往常一样,文档没有帮助......

下面是两行doc:

图片是:

全彩色位图(每像素 4+4+4bit),3 字节 每两个像素(像素顺序从左到右)。

所以,为了能够得到一个字节序列的图像,我编写了以下代码:

// size_x and size_y are known at this point
QImage img(size_x, size_y, QImage::Format_RGB444); 
int xctr = 0;
int yctr = 0;
for(int c1 = 0; c1 < bsize; ) // bsize is the size of image bytes

    static const int M = 1; // a multiplier to make the image brighter
    uint8_t b1 = *(p_pos + c1++); // p_pos points to the first image byte
    uint8_t b2 = *(p_pos + c1++);
    uint8_t b3 = *(p_pos + c1++);

    uint8_t pix1_r =( (b1 & 0xF0) >> 4 ) * M;
    uint8_t pix1_g =( (b1 & 0xF) ) * M;
    uint8_t pix1_b =( (b2 & 0xF0) >> 4 ) * M;

    uint8_t pix2_r =( (b2 & 0xF) ) * M;
    uint8_t pix2_g =( (b3 & 0xF0) >> 4 ) * M;
    uint8_t pix2_b =( (b3 & 0xF) ) * M;

    // old code
    //unsigned int pix1 = pix1_r << 8 | pix1_g << 4 | pix1_b;
    //unsigned int pix2 = pix2_r << 8 | pix2_g << 4 | pix2_b;

    // new code
    unsigned int pix1 = ((unsigned int)pix1_r) << 8 | pix1_g << 4 | pix1_b;
    unsigned int pix2 = ((unsigned int)pix2_r) << 8 | pix2_g << 4 | pix2_b;

    // set the first pixel
    img.setPixel(xctr, yctr, pix1);
    xctr ++;
    if(xctr == size_x) 
        xctr = 0;
        yctr ++;
    

    // set the second pixel
    img.setPixel(xctr, yctr, pix2);
    xctr ++;
    if(xctr == size_x) 
        xctr = 0;
        yctr ++;
    

这是我由此得到的图像:

这是我应该得到的图像(非常相似)(只有蓝色/洋红色部分,请不要考虑黑色背景和雪 :)):

对于勇敢的人,图像数据:http://pastebin.com/m4y8qamB,请随时使用此Python creating an array from hex bytes 将原始数据转换为“真实”十六进制字节。

还有一个问题:现在,忽略嵌入在设备本身中的图像调色板,我做错了什么,我没有获得与预期相似的图像?我应该怎么做才能获得相似的图像?

编辑在添加一些解码值(和打印字符)的打印输出后,我得到如下内容:

PPPPPPPPPPPPPPPPPPPAPPPPPPPPPPPP
PPPPPPPAPPPPPPPPPPPPPPPPPPPAPPPP
PPPPPPPPPPPPPPPAPPPPPPPPPPPPPPPP
PPPAPPPPPPPPPPPPPPPPPPPAPPPPPPPP
PPPPPPPPPPPAPPPPPPPPPPPPPPPPPPPA
PPPPPPPPPPPPPPPPPPPAPPPPPPPPPPPP
PPPPPPPAPPPPPPPPPPPPPPPPPPPAPPPP
PGPPPPPPPPPPPPPAPPPPPCANMMMMMMMM
MMMMAMMMMOPCALDDDDDDDDDDDADDDMPC
ANODDDDDDDDDDDDADDKPANPCAGMMMMMM
MMMMMMAMMNPCANPCANPCANPCANPCANPC
ANPCANPPPAPPANPCANPCANPCANPCANPC
ANPCANPCADDADDPCANPCANPCANPCANPC
ANPCANPCANPCMMAMMMMMMMMMMMMMMMMM
MMAMMMMMMMMMMMMMMMMMMMAMMMMMMMMM
MMMMMMMMMMAMMMMMMMMMMMMMMMMMMMAM
MMMMMMMMMMMMMMMMMMAMMMMMMMMMMMMM
MMMMMMAMMMMMMMMMMMMMMMMMMMAMMMMM
MMMMMMMMMMMMMMAMMMMMMMMMMMMMMMMM
MMAMMMMMMMMMMMMMMMMMMMAMMMMMMMMM

【问题讨论】:

您检查过您的 size_x 和 size_y 是否正常?该结果看起来与原始的比例不同。 pix1_r &lt;&lt; 8中,pix1_r是8位变量,做8位移位会变0,建议你typecast成16位或者32位。 它们也与图像字节一起被编码在图像数据中(参见粘贴箱)。我感觉原版没有使用方形像素,因为它是 33x18) @MadHatter 现在就这样做了,它没有帮助 :( 问题已更新 你确定你的 size_x 和 size_y,并且 p_pos 是一个char *(或者至少是一个指向 8 位值的指针)吗?您的 pastebin 在开始和结束处具有相同的模式(39 33 93 重复,应转换为 393 393),这意味着,如果您正确读取数据,至少第一行和最后一行应该是相同的。我将 img.setPixel 替换为 printf("%3x", pix1) 和 yctr++ 替换为 putchar('\n') 以查看您的解码器实际解码的内容。 【参考方案1】:

我将 pastebin 数据转换为二进制文件使用

perl -pe 's/([0-9a-f]2)\s+/chr(hex($1))/ige' > tempfile

当我查看该文件的十六进制转储时,这似乎是正确的。

然后我写了一个程序来解码那个文件:

    #include <stdio.h>

    typedef unsigned char uint8_t;
    #define M 1

    int main(void) 
            FILE *fp;

            int sizex, sizey;
            int xctr, yctr;
            int i=0;

            if ((fp=fopen("tempfile", "rb"))==NULL) 
                    perror("tempfile"); exit(1);
            
            sizey=getc(fp);
            sizex=getc(fp);
            int bsize=getc(fp)*256;
            bsize+=getc(fp);

            xctr=yctr=0;
            for (i=0; i<bsize/3; i++) 
                    uint8_t b1=getc(fp);
                    uint8_t b2=getc(fp);
                    uint8_t b3=getc(fp);

                    uint8_t pix1_r =( (b1 & 0xF0) >> 4 ) * M;
                    uint8_t pix1_g =( (b1 & 0xF) ) * M;
                    uint8_t pix1_b =( (b2 & 0xF0) >> 4 ) * M;

                    uint8_t pix2_r =( (b2 & 0xF) ) * M;
                    uint8_t pix2_g =( (b3 & 0xF0) >> 4 ) * M;
                    uint8_t pix2_b =( (b3 & 0xF) ) * M;

                    unsigned int pix1 = ((unsigned int)pix1_r) << 8 | pix1_g << 4 | pix1_b;
                    unsigned int pix2 = ((unsigned int)pix2_r) << 8 | pix2_g << 4 | pix2_b;

                    printf("%3x ", pix1);
                    xctr++;
                    if (xctr==sizex) 
                            putchar('\n');
                            xctr=0;
                    
                    printf("%3x ", pix2);
                    xctr++;
                    if (xctr==sizex) 
                            putchar('\n');
                            xctr=0;
                    
            
    

它或多或少地完成了你的工作,并且有输出:

    393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393
    393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393
    393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393
    393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393
    393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393 393
    393 393 393 393 393 393 393 999 ddd eee bcc 393 393 999 fd0 2fd 02f d02 fd0 2f9 993 933 933 933 939 99f fd0 2fd
    02f d02 fd0 2fd 02f ccc 666 ffd 02f d02 fd0 2fd 02f d02 fd0 2fd 029 993 933 933 93d ddf fd0 2fd 02f d02 fd0 2fd
    02f d02 fd0 2ee eff d02 fd0 2fd 02f d02 fd0 2fd 02f d02 ddd 393 393 393 fd0 2fd 02f d02 bbb 393 bcc ffd 02f d02
    fd0 1ee 393 393 bbb ffd 02f d02 f39 339 339 3fd 02f d02 fd0 239 339 339 3ee efd 02f 999 393 393 393 ffd 02f d02
    f39 339 339 3fd 02f d02 fd0 2aa a39 399 9ff d02 fd0 2fd 01e e39 339 399 9ff d02 fd0 2f3 933 933 93d ddf fd0 2fd
    02f d02 fd0 2fd 02f d02 fd0 2fd 02f d02 fd0 2fd 02f d01 eef d02 fd0 2fd 02d dd3 933 933 938 88f fd0 2fd 02f d02
    fd0 2fd 02f aaa 999 ffd 02f d02 fd0 2fd 02f d02 fd0 2fd 029 993 933 933 933 938 88c ccc cc9 993 933 93a aaf d02
    fd0 2fd 02f d02 f99 939 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339
    339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339
    339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339
    339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339 339

看起来解码过程没问题,但输出看起来还是不像图像。

再次查看您的原始 pastebin 数据,我几乎可以肯定该数据中有一些错误。开始时,序列“39 33 93”重复了几次,很好地转换为 393 393。但后来,你得到了“FD 02 FD 02 FD 02 FD 02”。这将转换为“FD0 2FD 02F D02 FD0 2..”,我无法想象这是正确的。是否有可能缺少某些字节(例如未传输的 \0 字节)并且结尾的 339 实际上是属于下一个图像的数据?

【讨论】:

嗯,谢谢你的努力。我还得出结论,如果图像格式的描述中没有隐藏任何内容(没有找到任何内容),那么数据中肯定存在错误......现在另一端有“有效”图像(取自真实的总线)相同的数据很好地解码成我们看到的......所以我有感觉,解码算法中有一些东西是在总线硬件上实现的,但没有记录在案:(【参考方案2】:

我不能让这个问题休息;)我注意到我的文件有 2F7(十六进制)字节,但标题显示 02A0,所以文件中肯定有一些东西,但必须在输出时忽略。然后,我想到如果从输入中省略所有“F”半字节(并从输出中省略 393)会发生什么,但结果看起来不太好,尤其是 39 33 93 有时会移位解码为 933 933 933。

接下来,我省略了所有 F 和 E 半字节,结果如下:


    start of image









                999 ddd bcc         999 d02 d02 d02 d02 999
            999 d02 d02 d02 d02 d02 ccc 666 d02 d02 d02 d02 d02 d02
    d02 999             ddd d02 d02 d02 d02 d02 d02 d02 d02 d02 d02
    d02 d02 d02 d02 ddd             d02 d02 d02 bbb     bcc d02 d02
    d01         bbb d02 d02             d02 d02 d02             d02
    999             d02 d02             d02 d02 d02 aaa     999 d02
    d02 d01         999 d02 d02             ddd d02 d02 d02 d02 d02
    d02 d02 d02 d02 d02 d02 d01 d02 d02 d02 ddd             888 d02
    d02 d02 d02 d02 aaa 999 d02 d02 d02 d02 d02 d02 d02 999
            888 ccc ccc 999         aaa d02 d02 d02 d02 999









    I'm now at 02df
    end of image

这看起来并不完全正确,但比我们迄今为止所拥有的一切都要好得多。我认为必须省略更多内容,因为代码读取到 0x2df,而不是 0x2f7,但这可能有助于您找到还需要做的事情。这是我的代码;请注意,我先阅读 sizey,然后阅读 sizex,因为这似乎会产生更好的画面:

    #include <stdio.h>

    typedef unsigned char uint8_t;
    #define M 1

    int sizex, sizey;
    int xctr, yctr;

    int main(void) 
            FILE *fp;

            int i=0, c;
            int flag;

            if ((fp=fopen("tempfile", "rb"))==NULL) 
                    perror("tempfile"); exit(1);
            
            sizey=getc(fp);
            sizex=getc(fp);
            int bsize=getc(fp)*256;
            bsize+=getc(fp);

            xctr=yctr=0;
            for (i=0; i<bsize*2;) 
                    c=getc(fp);
                    if ((c&0xf0) != 0xf0 && (c&0xf0) != 0xe0) 
                            push ((c>>4)&0x0f);
                            i++;
                    
                    if ((c&0x0f) != 0x0f && (c&0x0f) != 0x0e) 
                            push (c&0x0f);
                            i++;
                    

            
            printf("I'm now at %04x\n", ftell(fp));
    


    int push(int c) 
            static int pos=0;
            static uint8_t red, green, blue;

            if      (pos==0)  pos=1; red=c; 
            else if (pos==1)  pos=2; green=c; 
            else if (pos==2)  pos=0; blue=c; 

            if (pos==0) 
                    if (red==3 && green==9 && blue==3)
                            printf("    ");
                    else
                            printf("%01x%01x%01x ", red, green, blue);
                    xctr++;
                    if (xctr==sizex) 
                            putchar('\n');
                            xctr=0;
                    
            
    

【讨论】:

嗯,这看起来很有希望 :) 再次感谢您的努力。

以上是关于将 RGB 444 转换为可显示的图像的主要内容,如果未能解决你的问题,请参考以下文章

yuv 422 转换成rgb 的能在arm linux上显示出来的c代码 怎么样了 做出来了吗?想请教你

C: 将数组转换为 RGB 图像

将图像转换为可搜索的 pdf [关闭]

Matlab图像处理彩色图像转换为灰度图像(初学必看)

将numpy数组转换为rgb图像

mono8转换过来显示多个图像