JPG学习笔记4(附完整代码)
JPG编码的第3步是量化。对于经过离散余弦变化后的8*8block的数据,我们要对这8*8的数据进行量化。在JPEG中量化就是对数据V除以某个数Q,得到round(V/Q)代替原来的数据。然后在JPG解码的时候再乘以M得到V。
需要注意的是,量化会丢失图片精度,而且是不可逆的。
M的大小同时也影响压缩的效果。M越大压缩效果越好,但是图片精度损失越大。
图片引用自"Compressed Image File Formats JPEG, PNG, GIF, XBM, BMP - John Miano"[1]
1.JPEG的量化过程
首先我们会有一个8*8的量化表,这个表可以自定义,也可以用JPEG标准提供的量化表。
直接用我们的8*8的Block数据,除以对应的QuantizationTable的数据即可。
const Block QUANTIZATION_TABLE_Y = {16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99 }; const Block QUANTIZATION_TABLE_CBCR = { 17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 };
假设我们的数据如左下,量化表如右下.
则round(V / A)就是我们需要的值了
2.代码
void JPG::quantization() { for (uint i = 0; i < mcuHeight; i++) { for (uint j = 0; j < mcuWidth; j++) { MCU& currentMCU = data[i * mcuWidth + j]; //iterate over 每一个component Y, cb cr for (uint componentID = 1; componentID <= 3; componentID++) { //遍历block for(uint ii = 0; ii < getVerticalSamplingFrequency(componentID); ii++) { for(uint jj = 0; jj < getHorizontalSamplingFrequency(componentID); jj++) { Block& currentBlock = currentMCU[componentID][ii * getHorizontalSamplingFrequency(componentID) + jj]; const Block& quantizationTable = getQuantizationTableByID(componentID); //这一步就是对64个像素进行量化 for(uint index = 0; index < 64; index++) { currentBlock[index] = currentBlock[index] / quantizationTable[index]; } } } } } } }
以上全部的代码在https://github.com/Cheemion/JPEG_COMPRESS/tree/main/Day4
完结
Thanks for reading,
Wish you have a good day.
>>>> JPG学习笔记5