作者:mobiledu2502905727 | 来源:互联网 | 2024-10-14 20:34
最近在看GitHub上的一个很火的项目是:ImageSharp。这是一个纯.netcore的图像处理库,没有使用其他的任何依赖。在看这个项目过程中激发了我对图像文件编码解码的兴趣。
最近在看GitHub上的一个很火的项目是:ImageSharp。这是一个纯.net core的图像处理库,没有使用其他的任何依赖。在看这个项目过程中激发了我对图像文件编码解码的兴趣。于是从最简单的BMP图像开始看,到GIF格式卡了一段时间(主要卡在lzw编码过程和数据块中),到最后的JPEG格式(PNG格式不打算看了),经历了半个月时间才梳理出个大概。趁着这个热乎劲,我想写下关于JPEG格式的系列文章,文章目录暂定如下:
ImageSharp源码详解之JPEG编码原理(1)JPEG介绍
ImageSharp源码详解之JPEG编码原理(2)采样
ImageSharp源码详解之JPEG压缩原理(3)DCT变换
ImageSharp源码详解之JPEG压缩原理(4)量化
ImageSharp源码详解之JPEG压缩原理(5)熵编码
ImageSharp源码详解之JPEG压缩原理(6)C#源码解析及调试技巧
JPEG(Joint Photographic Experts Group)是联合图像专家小组的英文缩写。它由国际电话与电报咨询委员会CCITT(The International Telegraph and Telephone Consultative Committee)与国际标准化组织ISO于1986年联合成立的一个小组,负责制定静态数字图像的编码标准。
小组一直致力于标准化工作,开发研制出连续色调、多级灰度、静止图像的数字图像压缩编码方法,即JPEG算法。JPEG算法被确定为国际通用标准,其适用范围广泛,除用于静态图像编码外,还推广到电视图像序列的帧内图像压缩。而用JPEG算法压缩出来的静态图片文件称为JPEG文件,扩展名通常为*.jpg、*.jpe*.jpeg。
JPEG专家组开发了两种基本的压缩算法、两种数据编码方法、四种编码模式。具体如下:
压缩算法:
1有损的离散余弦变换(Discrete Cosine Transform,DCT);
2 无损的预测技术压缩。
数据编码方法:
1哈夫曼编码;
2算术编码;
编码模式:
1基于DCT顺序模式:编/解码通过一次扫描完成;
2基于DCT递进模式:编/解码需要多次扫描完成,扫描效果从粗糙到精细,逐级递进;
3无损模式:基于DPCM,保证解码后完全精确恢复到原图像采样值;
4层次模式:图像在多个空间多种分辨率进行编码,可以根据需要只对低分辨率数据作解码,放弃高分辨率信息。
在我阅读的源码中,我关注的是离散余弦变换、哈夫曼编码、基于DCT顺序模式的编码,这也是JPRG图像常用的技术。
整体文件的大致结构如下:
SOI(0xFFD8)
APP0(0xFFE0)
[APPn(0xFFEn)]可选
DQT(0xFFDB)
SOF0(0xFFC0)
DHT(0xFFC4)
SOS(0xFFDA)
压缩数据
EOI(0xFFD9)
我们解码的时候大致都是按照上面顺序进行解码,关于上面这些标记,大家可以从文章结尾参考资料中看到他们的详细信息,这里我不对这些标记展开描述,在后面用到的时候会提到。
2.JPEG压缩的大致过程
2.1 编码
对于一副图像,编码器首先需要填充这个图像的一些头信息,量化表,霍夫曼表。我们可以看ImageSharp中JpegEncoderCore这个类里面的的Encode方法如下:
1 ...
2 // Write the Start Of Image marker.
3 this.WriteApplicationHeader(metadata);
4 // Write Exif and ICC profiles
5 this.WriteProfiles(metadata);
6 // Write the quantization tables.
7 this.WriteDefineQuantizationTables();
8 // Write the image dimensions.
9 this.WriteStartOfFrame(image.Width, image.Height, componentCount);
10 // Write the Huffman tables.
11 this.WriteDefineHuffmanTables(componentCount);
12 // Write the image data.
13 this.WriteStartOfScan(image);
14 // Write the End Of Image marker.
15 this.buffer[0] = JpegConstants.Markers.XFF;
16 this.buffer[1] = JpegConstants.Markers.EOI;
17 stream.Write(this.buffer, 0, 2);
18 stream.Flush();
19 }
View Code