TinyJPEG源码剖析

源代码仓库:https://github.com/leelitian/TinyJPEGDecoder

说明:轻量级JPEG解码器,代码的远古版本可以通过Google找到。


项目架构

主要分为以下三个部分,在main函数中对应三个调用函数:

  • tinyjpeg_parse_header:解析jpeg文件头
  • tinyjpeg_decode:解码jpeg的图像数据
  • write_yuv:将解码后的数据写入文件

tinyjpeg_parse_header

这部分的解析,可以参考以下两篇文章,写的很详细:

https://blog.csdn.net/weixin_41926958/article/details/106308576

https://segmentfault.com/a/1190000021169775


tinyjpeg_decode

在看这部分代码之前,我们需要知道在JPEG中图像数据是如何存储的,首先需要了解MCU和DU的概念。

DU是数据单元,一个DU就是DCT变换块,在JPEG中固定为8X8;

MCU是最小编码单元,也被称作宏块,它取决于JPEG图像中Y、U、V三个分量的采样因子的比例。

一般来说,U和V的采样因子为1x1,当Y的采样因子也为1x1时,图像格式称为YUV444,即各个分量都是逐点采样。此时MCU的大小为8x8,即我们将图片分割为许多8x8的MCU进行编码。在一个MCU中包含3个分量,也就是MCU = YDU + UDU + VDU

当Y的采样因子为2x2时,也就是Y逐点采样,而U和V在2x2像素上采一次样,图像格式称为YUV420,此时8x8的图像中Y、U分量的大小仅为4x4,不够一个DU的大小,因此我们把MCU设置为16x16。此时MCU = 4 * YDU + UDU + VDU

参考1:https://blog.csdn.net/hudaliquan/article/details/38589747

参考2:https://blog.csdn.net/xjhhjx/article/details/80291465

而我们说,JPEG在存储图像数据时,是以一个MCU一个MCU存储的。
MCU = 4 * YDU + UDU + VDU为例,我们依次从码流中读入4个YDU、1个UDU、1个VDU,经过反DCT变换、反量化,就能够得到16*16大小宏块的信息。

	// MCU: Minimum Coding Unit
	xstride_by_mcu = ystride_by_mcu = 8;
	
	// DU都是8x8的
	// Y.H x Y.V = 1 x 1,此时 MCU(8x8) = YDU + UDU + VDU,相当于YUV444
	if ((priv->component_infos[cY].Hfactor | priv->component_infos[cY].Vfactor) == 1) {
		decode_MCU = decode_mcu_table[0];
		convert_to_pixfmt = colorspace_array_conv[0];
		trace("Use decode 1x1 sampling\n");
	// Y.H x Y.V = 1 x 2,不支持
	} else if (priv->component_infos[cY].Hfactor == 1) {
		decode_MCU = decode_mcu_table[1];
		convert_to_pixfmt = colorspace_array_conv[1];
		ystride_by_mcu = 16;
		trace("Use decode 1x2 sampling (not supported)\n");
	// Y.H x Y.V = 2 x 2,此时 MCU(16x16) = 4*YDU + UDU + VDU,相当于YUV420
	} else if (priv->component_infos[cY].Vfactor == 2) {
		decode_MCU = decode_mcu_table[3];
		convert_to_pixfmt = colorspace_array_conv[3];
		xstride_by_mcu = 16;
		ystride_by_mcu = 16;
		trace("Use decode 2x2 sampling\n");
	// Y.H x Y.V = 2 x 1,此时 MCU(16x8) = 2*YDU + UDU + VDU,相当于YUV422
	} else {
		decode_MCU = decode_mcu_table[2];
		convert_to_pixfmt = colorspace_array_conv[2];
		xstride_by_mcu = 16;
		trace("Use decode 2x1 sampling\n");
	}

在获得各个DU之后,依据采样方式和输出格式的不同,采取不同的措施,在这里举一个例子:

若JPG文件采样方式为1x1,即文件格式为yuv444,而我们要求的输出格式yuv420p。对于Y分量,我们需要全部写入文件,而对于UV分量,我们需要先进行2x2的采样,才能写入文件。详见:YCrCB_to_YUV420P_1x1函数


其它

其它注解见仓库源代码,如果博主有说的不对或者疑惑的地方,欢迎评论留言。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值