JPEG编码之颜色空间变换
BMP图转换为JPEG图,首先要进行颜色空间的转换。即把RGB转换为YUV或YIQ,并进行二次采样。因为亮度分量(Y)和色度分量(U、V或I、Q)用不同的比率。这是因为人眼对亮度比较敏感而对色度相对较弱。这些是我从书上看来的,但是,它没讲到底怎么二次采样,我只能以自己的理解来。
读取BMP图的像素数据(24色),用unsigned char * 来存储。然后,是分块:Y用公式直接计算,与每个像素一一对应;而U、V则采用4*4的小块计算出U的平均、V的平均(我采用的是4:2:0)。BMP每行都是偶数(4n),但是并不是每个都存储满的,所以还是要用长、宽来处理颜色空间的色度转换。如果行/列不是偶数,那么就要进行边角料处理了。这是个很麻烦的东西。我想到是先偶数部分,然后对边角料处理:假设长宽都是奇数,那么则最后一列(2*1)进行处理(/2来求平均),同理对最后一行进行处理。最后,剩下一个像素(右下角的,BMP的存储坐标来看)。所以,如果行为奇数,则只要对最后一列进行处理,而列为奇数则对行进行处理,行列都为奇数则行、列处理完之后还有右下角的一个像素点要处理。
那么,Y、U、V是绑定存储还是分开存储呢?我看书上说是绑定存储的,比如4:2:0则存储为4个Y一个U和一个V,如此反复。
但是,我个人觉得是之后的JPEG的处理,是把Y和U、V分开的,可以简化操作。但是没有深入的思考过。这里,大家可以来讨论一下。
附注:
这里,还有写注意点。之前学习了一些处理BMP图的方法,用惯了BYTE* 来存储像素值。在这里进行彩色空间转换的时候,许多都是0,后来才想起来,我进行的是float计算,用BYTE会使得精度损失比较大。另外,我试了一下,从RGB—>YUV—>RGB,发现有些失真(跟原来的颜色不大一样,绿色向黄色偏移)。
还发现一个东西,就是用fstream来写文件,写BYTE* 的时候,如果多个数据中间有0,则会写到0后就不再写了。比如BYTE* p={1,2,3,0,4,5,6};则写了1,2,3,0,就结束了。而4,5,6则没有写。
JPEG编码之DCT变换
在DCT变换之前,先要进行分块处理。还是老问题:8*8之后像素矩阵的边角料处理。处理跟上面差不多,但是之前都是进行的8*8的DCT变换,所以要把它扩充为8*8的矩阵。那么,用什么数据来进行填充呢?我发现用0来填充是对原来的数据没有影响的。因为把0代入就是 * f ( i ,j )就是 *0 ;
之后就是DCT变换了。代入公式,是最适合计算机做的,问题是有没有更高效的方法。我看到一个,就是转换为一维的DCT变换——先行变换,再列变换。我还发现,各个cos的值,在行系数(或列系数)确定的时候,就只跟 i 相关(该行所在位置)。即每一行有8个cos值。那么,8行就是8*8的矩阵。
1 1 1 1 1 1 1 1
0.98 0.83 0.55 0.19 -0.19 -0.55 -0.83 -0.98
0.92 0.38 -0.38 -0.92 -0.92 -0.38 0.38 0.92
0.83 -0.19 -0.98 -0.5 0.55 0.98 0.19 -0.83
0.70 -0.70 -0.70 0.70 0.70 -0.70 -0.70 0.70
0.55 -0.98 0.19 0.83 -0.83 -0.19 0.98 -0.55
0.38 -0.92 0.92 -0.38 -0.38 0.92 -0.9 0.38
0.19 -0.55 0.83 -0.98 0.98 -0.83 0.55 -0.19
(以上的数据是我省略了一些位,为了清楚)
而各个像素值也是8*8的矩阵,所以DCT的行变换正好是两个8*8的矩阵相乘。
附注:
书上用的是蝶形变换,有些是直接代入傅里叶变换中进行,有些是列了很多的变量解决的,完全看不清楚。后来我查到了一些资料,见图33,34,35;