Advertisement

图像压缩(1)RGB888与RGB565图像

阅读量:

图像压缩(1)RGB888与RGB565图像

  • 前言

  • 一. 图像数据格式

    • 1.1 不同RGB格式

      • (1)RGB16
      • (2)RGB24
      • (3)RGB32
      • (4)ARGB32
    • 1.2 RGB565与RGB888对比

      • (1)区别
      • (2)各自优缺点
  • 二. 图像格式转换

    • 2.1 取位与补位
    • 2.2 其他转换方法
  • 三. 图像编码

      • 3.1 高精度彩色数据压缩方案(采用16位真彩色数据压缩算法)
      • 3.2 通过FPGA硬件实现的RGB565格式画面展示技术
    • 四. RGB888和RGB565颜色对照表

前言

近期参与了图像压缩相关项目的实践工作。此前对图像压缩的相关技术了解较为有限,在此方面个人积累的经验则略显丰富一些。因此有必要进一步加强这方面的学习。通过查阅相关技术文档并进行了初步的数据收集和整理工作,在此基础上制作了一份简要的学习笔记,并在此基础上分享出来以便大家共同交流学习与进步。

一. 图像数据格式

RGB色彩模式 是一种工业界广泛采用的标准,在红、绿、蓝三种基本色的基础上通过混合调配生成不同的颜色。这也源自于初中物理课程的知识。在RGB三通道体系下能够覆盖人类视觉系统能够感知到的各种颜色,并因此成为当今应用最为广泛的色彩编码标准之一。然而,在实际应用中,人们所感受到的真实世界的视觉效果与各类硬件平台生成的图像之间存在显著差异。不同类型的现实设备受限于自身的硬件配置和具体应用场景的需求,并不需要追求极高的显示质量;即使能够在清晰度上达到基本可辨识的程度,在数据传输和存储方面仍需满足更高的技术标准。

1.1 不同RGB格式

(1)RGB16

复制代码
    RGB16数据格式主要有二种:RGB565(或者是R5G6B5)和RGB555(或者是R5G5B5)。
    RGB565:每个像素用16比特位表示,占2个字节,RGB分量分别使用5位、6位、5位。
    RGB555:每个像素用16比特位表示,占2个字节,RGB分量都使用5位(最高位空闲)。

(2)RGB24

复制代码
    一般RGB24即指RGB888(或者是R8G8B8)
    RGB888:每个像素用24比特位表示,占3个字节,注意:在内存中RGB各分量的排列顺序为:BGR BGR BGR 

(3)RGB32

复制代码
    RGB32:图像每个像素用32比特位表示,占4个字节,R,G,B分量分别用8个bit表示,存储顺序为B,G,R,最后8个字节保留。注意:在内存中RGB各分量的排列顺序为:BGRA BGRA BGRA 。

(4)ARGB32

复制代码
    就是带alpha通道的RGB24,与RGB32的区别在于,保留的8个bit用来表示透明,也就是alpha的值
    ARGB8888:每个像素用24比特位表示,占3个字节,注意:在内存中ARGB各分量的排列顺序为:BGRA BGRA BGRA 

1.2 RGB565与RGB888对比

(1)区别

通常情况下

RGB格式中的值为 R G B分量分别对应不同位数的编码。
RGB格式中使用不同位数编码时,
其中每个通道( R G B)均采用相同数量的比特位编码;
实际上这等价于标准的24位颜色表示法。

RGB565,16bit
RGB888,24bit

常见的是RGB像素数据中添加位图头信息所形成的图像文件格式。

一般BMP是BGR888,jpeg是YUV的,其他的要看各自格式的标准描述。

(2)各自优缺点

就屏幕而言,在应用该标准之前,则必须确保控制器具备相应的功能。与之相比,在存储需求上需增加约一半的空间。其色彩深度明显高于前一代,在成本方面则会更高。一方面可降低开发成本,并从而减少数据传输量。能够支持2^16种颜色的主要用途是用于一般性场景的应用。主要缺陷在于其色域覆盖范围不如前一代宽广。

二. 图像格式转换

在这里学习一下RGB888与RGB565的转换,即24位色彩与16位色彩的转换:

2.1 取位与补位

24bit RGB[23:0]转16bit RGB[15:0]的操作是提取高位完成的。选取RGB[23:19], RGB[15:10], RGB[7:3]作为三原色值。
而对16bit RGB[23:0]转24bit RGB[15:0]的操作则是采用补零的方式进行处理。具体来说,就是利用位操作将低位扩展至8bit长度。

2.2 其他转换方法

复制代码
    R8 = ( R5 * 527 + 23 ) >> 6;
    G8 = ( G6 * 259 + 33 ) >> 6;
    B8 = ( B5 * 527 + 23 ) >> 6;
    
    R5 = ( R8 * 249 + 1014 ) >> 11;
    G6 = ( G8 * 253 +  505 ) >> 10;
    B5 = ( B8 * 249 + 1014 ) >> 11;
复制代码
    Apple ( iOS Accelerate Framework ):
    Pixel8 red = (5bitRedChannel * 255 + 15) / 31 
    Pixel8 green = (6bitGreenChannel * 255 + 31) / 63 
    Pixel8 blue = (5bitBlueChannel * 255 + 15) / 31

三. 图像压缩

3.1 G6压缩法(16位真彩色数据压缩算法)

此方法不同于常见的PNG、JPG压缩算法。它主要应用于单片机刷新RGB565格式的图片,在进行数据压缩与解压时所需运算量并不算大。对单片机运行较为友好。然而其压缩效率相对较低;由于图片中存在多处相同的数据(这是因为图片的颜色变化不大)。许多图像都具有这一特征:颜色变化不大。我们可以通过观察颜色连续性来优化图像数据存储空间。如果相邻像素或区域的颜色相同,则可以仅保留一个数据点,从而节省2Byte的空间。

G6字段用于标识一个状态标记。该字段的意义在于指示相邻像素是否相同——当它们相等时设为1,在不同时设为0。无需额外占用内存来保存此标记信息。举例如下:假设第一个像素值为2,则通过算法判断其与下一个像素是否一致后会决定是否对该位置进行标记设置(置1)或直接隐藏其数值(置0)。在具体实施过程中可遵循以下逻辑步骤:首先检查当前像素与前一个像素的关系;如果两者数值一致,则对该位置进行标记设置;否则直接将其数值隐藏而不作任何修改。对于后续的每个新像素处理步骤均需遵循这一规则——若当前处理对象与其前驱对象数值一致则为其设置标记位(置1),否则将其数值遮挡(置0)。按照这一流程依次完成所有相关操作即可完成整个算法过程

压缩前的数据为2,2,2,2,2,3,4,2

压缩后的数据为2(G6=1),2(G6=1),2,3,4,2

在解压过程中第一个数值设置为2;我们在处理过程中首先检查G6位;此处被设置为1;因此该数值会重复出现两次;接着另一个数值也遵循相同的逻辑;第三个数值由于G6未被设置而只出现一次;因此只有一个。

压缩率计算基于以下假设:一张图片具有宽度W像素和高度H像素,并采用RGB565格式,则存储该图片所需的空间为W \times H \times 2\ \mathrm{Byte}

当使用该压缩算法时,在最佳压缩状态下(即相邻像素相同的情况下),最终的容量为W×H Byte)。若图像中无相邻像素相同的情况,则不会发生数据压缩操作。因此该方法的实际压缩率在50%至100%之间(忽略边缘情况)。综上所述:该方法通过利用RGB565中的G6位来标识相邻像素是否相同的标志,并对相同区域进行编码实现 compression效果。编程实现时需在编码前记录必要的元数据如编码长度、原始数据长度以及原始图像尺寸等信息。

复制代码
    void YS1_G6(unsigned char PIC[])
    {
    	int i;//循环变量
    	int PIC_Addr;//运行到输入图像的位置,不可到达
    	//PIC_SAVE[0]=;长度高位
    	//PIC_SAVE[1]=;长度低位
    	//第一步,遍历原来的数据
    	PIC_SAVE[2]=PIC[0];//第一个数据
    	PIC_SAVE[3]=PIC[1];//第二个数据
    	//PIC_SAVE[3]&=~0X20;
    	//PIC_SAVE[4]=;//存储循环长度
    	PIC_Addr=4;//不可到达位置,长度
    	i=0;
    	while(i<16383)//两个一组进行分组,循环16382次
    	{
    		i++;
    		//后边和前边一样,将第六位置1
    		if((PIC_SAVE[PIC_Addr-2]==PIC[i*2])&&(PIC_SAVE[PIC_Addr-1]==PIC[i*2+1]))
    		{
    			PIC_SAVE[PIC_Addr-1]|=0X20;
    			i++;
    		}
    		else//写入当前数据,并将第六位置零
    		{
    			PIC_SAVE[PIC_Addr-1]&=~0X20;
    		}
    		if(i<16384)//防止存储非法数据
    		{
    			//每次处理后必定写入一个数据
    			PIC_SAVE[PIC_Addr]=PIC[i*2];//第一个数据
    			PIC_SAVE[PIC_Addr+1]=PIC[i*2+1];//第二个数据
    			//PIC_SAVE[PIC_Addr+1]&=~0X10;
    			PIC_Addr+=2;//到达下个存储长度位置
    		}
    
    		//i++;//每次循环后必定处理数据,i向后移动
    	}
    	PIC_SAVE[0]=PIC_Addr/256;//长度高位
    	PIC_SAVE[1]=PIC_Addr%256;//长度低位
    	printf("压缩后的长度为%d",PIC_Addr);
    } 

算法说明:在压缩编码结果中包含统一的数据标识字段(即前两位字段编码了原始数据的位数),这一设计至关重要,它决定了解压所需的迭代次数依据。此处略过细节讨论算法中的循环次数与边界条件问题。该方案不具备普适性特征,在后续阶段计划补充图像尺寸信息(输入数据包括图像参数),因此生成的压缩编码结果中包含统一的数据标识字段这样的设计将使方案更具通用性。避免因压缩对象的变化而需手动调整编码参数的情况发生。本次仅作初步流程概述

第一步:首先我们需要查询并创建一个C文件(txt文件也可以用)。

第二步:然后我们处理一下输入的数据,清除G6位。

第三步:进行压缩,然后计算出压缩后的长度。

第四步:将压缩后的数据保存,然后传输,即可完成压缩编码部分。

该G6压缩方法虽能实现数据压缩, 其压缩效率未能达到最佳水平, 未能达到最高效率的原因是由于图像像素之间存在差异性. 此时无需解压即可恢复原始数据.

解压的过程与压缩互为反向操作。
该算法在解压环节上更为简便。
运算量相对较小。

解压的过程与压缩互为反向操作。
该算法在解压环节上更为简便。
运算量相对较小。

用于解压的方法主要涉及对压缩后数据长度进行估算,在程序运行中会进入持续不断的数据解压过程。首先将编码后的像素值发送到主控制器,并根据接收的数据状态决定是否继续后续处理步骤。当G6位被设置为高电平时,则会触发重新刷新屏幕以显示最新图像;否则系统将直接进入下一个循环以完成剩余部分的处理。在这个过程中会频繁地进行相邻像素值比较(在编码过程中需要频繁地进行相邻像素值比较),而解码过程中则主要执行按位与运算来恢复原始像素值。该编码方案能够有效地减少图像存储所需的比特数,并且由于该方法未采用动态预测或其他高级编码技术(如MPEG-4 HEVC),其空间利用率仍有一定提升余地。

3.2 基于FPGA片上ROM的RGB565屏幕图像显示

为实现16位彩图显示受片上ROM容量限制示例采用分辨率60×100像素(共72,000 bits)即显示时需将图像放大至480×800像素同样可利用Img2Lcd软件将图片转换为所需的.bmp格式(通常采用水平扫描模式并采用16位真彩色)随后在Matlab中将.bmp文件信息转换为RGB565格式的MIF文件

复制代码
    clear;
    clc;
    n=6000;%100*60--16bit
    myImg = imread('example.bmp');%读取.bmp文件
    fid=fopen('example.mif','w');%打开待写入的.mif文件
    fprintf(fid,'WIDTH=16;\n');%写入存储位宽16位
    fprintf(fid,'DEPTH=6000;\n');%写入存储深度6000
    fprintf(fid,'ADDRESS_RADIX=UNS;\n');%写入地址类型为无符号整型
    fprintf(fid,'DATA_RADIX=HEX;');%写入数据类型为16进制
    fprintf(fid,'CONTENT BEGIN\n');%起始内容
    image(myImg);
    for i=0:n-1         %RGB888 -> RGB565
    x = mod(i,100)+1;
    y = fix(i/100)+1;
    r = uint16(myImg(y,x,1)/8);
    g = uint16(myImg(y,x,2)/4);
    b = uint16(myImg(y,x,3)/8);%由于输出的图像为uint8,所以等式左边的变量无法赋值为其他类型
    r = dec2bin(r);
    g = dec2bin(g);
    b = dec2bin(b);%十进制转二进制
    r = num2str(r);
    g = num2str(g);
    b = num2str(b);%数字转字符串
    
    if length(r)==1 %根据字符串长度补零--r
        r = strcat('0000',r);
    elseif length(r)==2
        r = strcat('000',r);
    elseif length(r)==3
        r = strcat('00',r);
    elseif length(r)==4
        r = strcat('0',r); 
    else
        r = r;
    end
    if length(g)==1 %根据字符串长度补零--g
        g = strcat('00000',g);
    elseif length(g)==2
        g = strcat('0000',g);
    elseif length(g)==3
        g = strcat('000',g);
    elseif length(g)==4
        g = strcat('00',g); 
    elseif length(g)==5
        g = strcat('0',g);
    else
        g = g;
    end    
    if length(b)==1 %根据字符串长度补零--b
        b = strcat('0000',b);
    elseif length(b)==2
        b = strcat('000',b);
    elseif length(b)==3
        b = strcat('00',b);
    elseif length(b)==4
        b = strcat('0',b); 
    else
        b = b;
    end    
        
    k = strcat(r,g,b);
    %k = str2num(k);
    k = bin2dec(k);
    fprintf(fid,'\t%d:%x;\n',i,k);
    %disp(k);
    end
    fprintf(fid,'END;\n');
    fclose(fid);%关闭文件

查阅Matlab中文手册得知,软件读取8位的bmp图像时转化为单个矩阵。而读取16位bmp图像时,将其转化为3个8位矩阵,所以提取图像数据转为RGB565时需要除以特定的数。因为Matlab会把数据自动转化为uint8类型,所以需要将数据强制类型转换为二进制。在数据的高位补零后,再用字符串连接函数,将其连接为16位的字符串。最后用二进制转十进制函数,把字符串直接转为数字,存与指定的mif文件中。定制好ROM为16位,6000深度,例化下载后,其显示结果如下(其实,由于matlab读取16位bmp图像时,将其转化为3个8位矩阵。故可不需要用软件转换为16位的bmp文件再用matlab提取数据)。显示的Verilog主要程序如下:

复制代码
    /*color_rom--彩色-16位*/        
    localparam
        BMP_W            =    11'd100,   //显示区域宽度 = 图片宽
        BMP_H            =    11'd60;   //显示区域高度 = 图片高
    
    wire [12:0]  pic_addr;    
    wire [15:0] q;
    assign pic_addr = hcount[10:3] + vcount[10:3]*BMP_W;
    
    color16_rom color16_rom(
        .address(pic_addr),
        .clock(clk),
        .q(q)
    );
    assign disp_data = q;
    /*color_rom-end*/

在计算机领域中, 图像通常采用RGB 24位制进行显示。每个像素占用32位的空间存储数据, 其中包含红绿蓝通道及透明度信息(Alpha通道)。值得注意的是,在16位深度下(如Truevision RGB),人眼几乎无法察觉颜色差异。为了优化资源消耗并减少数据量, 我们可以通过颜色空间转换技术将标准的RGB 24位制(Red-Green-Blue 24-bit)压缩至嵌入式视觉专用格式(如RGB 565)。当需要恢复原始显示格式时, 我们会将压缩后的RGB 565格式解码并还原回标准的红绿蓝通道(Red-Green-Blue)以及透明度信息(Alpha通道)。

RGB888->RGB565->RGB888

然而,在将RGB值从8位扩展到5位(即RGB888->RGB565)的过程中

(1)RGB888-RGB565

24位RGB RRRRRRRRGGGGGGGGBBB色深转换至16位RGB RRRRRRRGGGGBBBBBB色深的编码方案中可以看出

(2)RGB565-RGB888
将16位RGB 656编码转换为24位RGB 888编码
将16位RGB 656码{R4 R3 R2 R1 R0}、{G5 G4 G3 G2 G1 G0}、{B4 B3 B2 B1 B0}分别扩展为
24位RGB 888码{R4 R3 R2 R1 R0 0 0 0}、{G5 G4 G3 G2 G1 G0 0 0}、{B4 B3 B2 B1 B0 0 0 0}
或采用重复低位方式进行扩展得到
24位RGB 889码{R4 R3 R2 R1 R0 R₂R₁R₀}、{G₅G₄G₃G₂G₁G₀G₁G₀}、{B₄B₃B₂B₁B₀B₂B₁B₀}]

基于此,在量化压缩策略中,我们采用以下两种方法:首先,在RGB分量中提取其高位数值;其次,在最低位上实施"四舍五入"操作以减少误差;此外,我们还设计了一种有效的补偿机制。

  1. 将原数据填充至高位
  2. 对于低位,用原始数据的低位进行补偿

四. RGB888和RGB565颜色对照表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

全部评论 (0)

还没有任何评论哟~