图形图像基础 之 jpeg介绍 您所在的位置:网站首页 1129938876_16982521855061n.JPG 图形图像基础 之 jpeg介绍

图形图像基础 之 jpeg介绍

2023-12-31 09:24| 来源: 网络整理| 查看: 265

一、【概念】jpeg相关概念简介 jpeg—一种影像有损压缩标准方法 后缀jpg/jpeg

JPEG(Joint Photographic Experts Group 联合图像专家小组)是一种针对照片影像而广泛使用的有损压缩标准方法,面向连续色调静止图像的一种压缩标准。1992年发布了JPEG的标准而在1994年获得了ISO 10918-1的认定。和相同图象质量的其它常用文件格式(如GIF,TIFF,PCX)相比,JPEG是目前静态图象中压缩比最高的。JPEG格式是最常用的图像文件格式,后缀名为.jpg或.jpeg。

JFIF—JPEG文件交换格式,文件格式标准描述

JFIF:JPEG File Interchange Format JPEG文件交换格式。JPEG本身只有描述如何将一个图像转换为字节的数据串流,但并没有说明这些字节如何在任何特定的储存媒体上存储展现。一个由独立JPEG小组(Independent JPEG Group)所建立的额外标准,称为JFIF(JPEG File Interchange Format,JPEG档案交换格式),详细说明如何从一个JPEG串流,产出一个适合于电脑储存和传输的文件。可以见 后文 文件格式介绍部分。

渐进Progressive / 线性Baseline—两种典型jpeg压缩格式

Baseline:基于DCT的连续模式(Sequential DCT-based mode of operation,基本线性格式,一次将图像由左到右、由上到下顺序处理(一次扫描处理)。Progressive:基于DCT的渐进模式(Progressive DCT-based mode of operation),渐进JPEG,当图像传输的时间较长时,将图像分数次处理,以从模糊到清晰的方式来传送和显示图像(多次扫描处理)。

libjpeg/libjpeg-turbo—一个C语言编写处理JPEG图像数据格式的开源库

libjpeg-turbo是libjpeg的一个复刻,它采用单指令流多数据流(SIMD)指令来加速JPEG编码和解码基础效率。许多项目现在使用libjpeg-turbo而不是libjpeg,包括流行的GNU/Linux发行版(Fedora、Debian、Mageia、OpenSUSE等)、Mozilla和Chrome。除了性能方面,部分项目也因它允许向后保留与旧的libjpeg v6b版本的ABI(application binary interface)兼容性而选择使用libjpeg-turbo。libjpeg v7、v8和v9已打破与早期版本的ABI兼容性。

mjpeg—一种视频压缩格式(每帧独立按jpeg编码)后缀mjpeg

MJPEG(Motion Joint Photographic Experts Group )是一种视频压缩格式,技术即运动静止图像(或逐帧)压缩技术,其中每一帧图像都分别使用JPEG编码,不使用帧间编码,压缩率通常在20:1-50:1范围内(相比之下 MPEG4可以到200:1-500:1,帧间压缩)。广泛应用于非线性编辑领域可精确到帧编辑和多层图像处理,把运动的视频序列作为连续的静止图像来处理,这种压缩方式单独完整地压缩每一帧,在编辑过程中可随机存储每一帧,可进行精确到帧的编辑。

jpeg2000—兼容jpeg下代压缩技术 (更高压缩率 支持无损)后缀jp2

JPEG 2000是基于小波变换的图像压缩标准,由联合图像专家小组创建和维护。JPEG 2000通常被认为是未来取代JPEG(基于离散余弦变换)的下一代图像压缩标准,文件后缀.jp2。JPEG 2000的压缩比更高,而且不会产生原先的基于离散余弦变换的JPEG标准产生的块状模糊瑕疵。 JPEG 2000同时支持有损数据压缩和无损数据压缩。另外,JPEG 2000也支持更复杂的渐进式显示和下载。

二、【原理】jpeg编解码码原理简介

编码流程简介:转YUV-下采样-分块-DCT-量化-hufman编码,用一张图简单描述编解码流程: 在这里插入图片描述

1、RGB转YUV并进行色度下采样—减少色度数据(有损)

将RGB的数据转换到YUV色彩空间,基于人眼对亮度相对色度更敏感特性,方便亮色分离后对色度下采样降低数据量。举例的典型转换公式如下。转换后一个RGB像素点,对应一个YUV像素点(包含一个Y数据、U数据、V数据)。 在这里插入图片描述 接下来,下采样主要是对色度数据采样率降低 来降低数据大小,从YUV444(每个亮度Y对应一个U和一个V)下采样到YUV420(四个Y对应一个UV组合)或者YUV422(两个Y对应一个UV组合)。如果降低到420,数据量就只有444的一半大小。一般是按8bit存储YUV数据每个分量。 可以参考博文:https://blog.csdn.net/runafterhit/article/details/82917345

2、分块进行DCT离散余弦变换—分离低频和高频数据

先按像素区域划分方便后续处理,先要把YUV这3个分量分开,存放到3张表中去(分别存储 Y数据表 U数据表 V数据表)。然后由左及右,由上到下依次读取8x8的子块,存放在长度为64的表中。先进行数据偏移得到-128~128的对称区域数据,然后进行DCT离散余弦变换 Discrete cosine transform, 将图像的低频(人眼敏感)和高频(人眼不敏感)部分进行分离。这样得到的结果是每个 88 小块得到 88 的系数矩阵。 在这里插入图片描述 在这里插入图片描述

3、量化处理—频率系数量化取整减少高频数据(有损)

将DCT后得到的每个系数都除以量化矩阵中对应的值,然后进行取整。通常来说频率较高的部分对应的量化参数比较大,这样一来能够在较好地保留图像的低频部分并去除一些高频部分。JPEG中压缩率的调整是在这一步中,量化参数越大,压缩后的大小就会越小,但信息的损失也就越多失真更严重,这一步是有损的。 在这里插入图片描述

4、熵编码压缩数据—Huffman编码或算术编码压缩(无损)

熵编码是无损资料压缩的一个特别形式。它影像成分以Z字体(zigzag)排列,把相似频率组群在一起(矩阵中往左上方向是越低频率之系数,往右下较方向是较高频率之系数),插入长度编码的零结束标志,且接着对剩下的使用霍夫曼编码。 JPEG标准也允许在数学上优于霍夫曼编码的算术编码之使用(一般让文件更小约5%),然而由于专利原因很少使用,且相较霍夫曼编码在编解码上更慢。 哈夫曼编码一句话描述:先统计使用到全部字符独自的数量,构建一个哈夫曼树使每个字符都对应唯一的二进制编码,然后就用这些二进制翻译原先的数据 得到编码后data,反向解码也就是把 data通过哈夫曼树反向得到字符。从树介绍到哈夫曼树 可以了解:https://blog.csdn.net/runafterhit/article/details/96769885 在这里插入图片描述

三、【协议】jpeg协议标准文件格式(JFIF)介绍 jpeg文件基本构成元素—分段存储与段标识

JPEG文件的格式是分段来存储的,每个段都一定包含两部分一个是段的标识,由两个字节构成:第一个字节是0xFF,第二个字节标识段类型, 部分段紧接着的两个字节存放的是这个段的长度。标识表格如下: 在这里插入图片描述 备注:由于jpeg用FFxx作为标识,为了避免真正压缩数据FF冲突,数据段的FF使用FF00表示,相当于只要FF后面不是00就是一个真正的标号 而非 FF数据。

jpeg文件格典型组成详细描述(配合示例)

jpeg文件格典型组成描述:SOI(文件开始标识-FFD8,是不是一个jpeg文件用这个判断)+APP0(应用程序保留标记0xFFE0-如分辨率/采样率等,方便应用查看)+ DQT(定义量化表FFDB)+ SOF0(图像基本信息)+ DHT(定义Huffman表FFC4) + DRI(定义重新开始间隔FFDD-可选的)+ SOS(扫描行开始FFDA)+ EOI(文件结束标识FFD9)。 在这里插入图片描述 从最顶层看:文件起始SOI FFD8和结束EOI FFD9之间,就是jpeg的文件数据frame部分;把jpeg文件数据展开,先有一个APPn用作应用保留信息 方便应用软件读取,和 各类表格(如DQT定义量化表FFDB) 放在Tables/misc部分,然后是真正文件头frame header,接着就是扫描段scan组合,扫描段内部可以通过RST标识 FFD0~FFD7 表示 不同熵编码段 之间的 复位标志。如下我们打开一个图片。二进制通过UltraEdit、解析工具JPEGsnoop 见 第四部分查看工具。 请添加图片描述 1、开始标志SOI FFD8。 png) 2、APPn标记,APP0 应用保留信息信息常见格式(包含9个字段):固定值0xFFE0,1 数据长度(2 bytes,1~9段字节总长度),2标识符(5 bytes,内容0x4A46494600,即字符串JFIF0),3 版本号码(2 bytes,比如0x0102表示JFIF的版本号1.2),4 X和Y的密度单位(1 bytes,0:无单位;1:点数/英寸;2:点数/厘米),5 X方向像素密度(2 bytes),6 Y方向像素密度(2 bytes),7 缩略图水平像素数目(1 bytes),8 缩略图垂直像素数目(1 bytes),9缩略图RGB位图(rgb数据,任意长度)。 在这里插入图片描述 3、APP0~APPn之后是各类表项。一个或者多个量化表DQT:固定0xFFDB,1 量化表长度(2 bytes), P/T(1 bypte,高四位:精度 0 表8bit 1表16bit,低四位:表ID),表项内容; 在这里插入图片描述 3、展开文件头frame header,里面通过SOFn(FFC0~FFCF) 开始表示start of frame market帧标识起始,里面n用来描述是哪种基础编码格式,然后是头长度Lf(头长度),P采样精度(常见8-表示8bit位深),Y帧的行数即图片高度,X图片宽度,Nf 分量的个数(如 Y、U、V 3个分量,或者纯灰度图1)。接下来 每个分量的描述参数,主要包含 (Ci分量类型标识ID,Hi分量水平采样,Vi分量垂直采样,Tqi当前分量量化表ID)。 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 3、文件头之后,是一个或者多个霍夫曼表DHT:固定FFC4,1 长度(2 bytes),2 每个霍夫曼表内容(A ID号;B类型 0:DC表,1:AC表;C 长16个字节的编码,其代码代数和为接下来的编码的长度;D 内容编码) 在这里插入图片描述 4、DRI (Define Restart Interval) 复位段,可选:固定FFDD,1 长度(2 bytes),2 MCU 块的单元中的重新开始间隔(设为n,则意思是说,每n个MCU块就有一个RSTn标记。第一个标记是RST0,然后是RST1等,RST7后再从RST0重复)。 5、SOS(Start of Scan) 扫描数据段:固定FFDA,1 长度(2 bytes),2 分量数(1 bytes,灰度1分量,YUV3分量),3 每个颜色分量(A ID,B 交流系数AC表号,C 直流系数表DC表号)4 压缩数据(A 谱选择开始 bh 0x00,B.谱选择结束 ch 0x3F,C. 两个4位字段,高位和低位的谱选择 dh 1字节在基本JPEG中总为0x00, D 数据) 在这里插入图片描述 6、图像结束EOI:FFD9。 在这里插入图片描述

四、【查看】jpeg格式查看工具

在网上找了两款,可以用一下。

JPEG分析器—雷霄骅的JPEG分析器开源项目

介绍网页:https://blog.csdn.net/leixiaohua1020/article/details/84520117 SourceForge:https://sourceforge.net/projects/jpeganalysis/ 中文,还配有注释,比较好理解。 在这里插入图片描述

JPEGsnoop—一款开源的国外的jpeg分析工具

官网:https://www.impulseadventure.com/photo/jpeg-snoop-source.html gitbub链接:https://github.com/ImpulseAdventure/JPEGsnoop 百度经验使用指导:https://jingyan.baidu.com/article/37bce2be53f88f1002f3a212.html 在这里插入图片描述

五、【使用】libjpeg使用基础 libjpeg源码下载与编译

libjpeg-turbo介绍主页:https://libjpeg-turbo.org/ libjpeg-turbo下载github链接:https://github.com/libjpeg-turbo/libjpeg-turbo 1、下载源码:在github上找到源码链接,我的调试环境是linux,下载时的版本是2.1.1(见ChangeLog.md)

root@ubuntu:~/libjpeg/code# git clone https://github.com/libjpeg-turbo/libjpeg-turbo.git

2、readme有提示编译看BUILDING.md 文件,基本用法梳理如下(依赖cmake,apt-get install cmake下载)

#创建并进入构建文件夹 mkdir build cd build/ # 用命令创建makefile,格式:cmake -G"Unix Makefiles" [additional CMake flags] {source_directory} # CMake flags这里可以指定编译参数,source_directory是libjpeg源码目录 # 常见编译参数:-DCMAKE_INSTALL_PREFIX=xxx指定安装目录,如是交叉编译-DCMAKE_C_COMPILER指定编译器 cmake -G"Unix Makefiles" -DCMAKE_INSTALL_PREFIX=./ /root/libjpeg/code/libjpeg-turbo/ # 编译,可以带-j多线程加速编译 make -j # 查看编译后内容 root@ubuntu:~/libjpeg/code/libjpeg-turbo/build# ls cjpeg cmake_install.cmake djpeg jcstest libjpeg.map libturbojpeg.a Makefile sharedlib tjbenchtest tjunittest-static cjpeg-static cmake_uninstall.cmake djpeg-static jpegtran libjpeg.so libturbojpeg.so md5 simd tjexample wrjpgcom CMakeCache.txt croptest jconfig.h jpegtran-static libjpeg.so.62 libturbojpeg.so.0 pkgscripts tjbench tjexampletest CMakeFiles CTestTestfile.cmake jconfigint.h libjpeg.a libjpeg.so.62.3.0 libturbojpeg.so.0.2.0 rdjpgcom tjbench-static tjunittest

3、编译后最关注的libjpeg.a静态链接库,libjpeg.so动态链接库 就是我们需要的 库了,还有一些小工具。

libjpeg最简单的解码用例

有了动态链接库了,我们来写一个最简单的解码sample试试(在源码目录下example.txt有很详细的各种用法用例描述) 写一个sample_jpeg.c文件:

#include "stdio.h" #include "jpeglib.h" #include struct my_error_mgr { struct jpeg_error_mgr pub; /* "public" fields */ jmp_buf setjmp_buffer; /* for return to caller */ }; typedef struct my_error_mgr * my_error_ptr; void my_error_exit (j_common_ptr cinfo) { my_error_ptr myerr = (my_error_ptr) cinfo->err; (*cinfo->err->output_message) (cinfo); longjmp(myerr->setjmp_buffer, 1); } int main(int argc, char *argv[]) { if (argc printf("intput file %s open failed!\n", argv[1]); return -1; } // 创建输出文件 FILE *outfile = fopen("./output.bit", "w+"); if (outfile == NULL) { printf("out file open failed!\n"); return -1; } // 定义cinfo数据结构,libjpeg定义的解码器上下文 struct jpeg_decompress_struct cinfo; // 设置报错回调,如果libjpeg解析过程出错,会调用 struct my_error_mgr jerr; cinfo.err = jpeg_std_error(&jerr.pub); if (setjmp(jerr.setjmp_buffer)) { printf("set jmp error failed!\n"); fclose(infile); return -1; } // 创建解码器 jpeg_create_decompress(&cinfo); // 设置解码器输入,绑定文件 jpeg_stdio_src(&cinfo, infile); // 读取文件头,这一步之后cinfo中文件信息才可信 jpeg_read_header(&cinfo, TRUE); // 开始解码,默认输出是RGB,还可以cinfo.out_color_space设置 jpeg_start_decompress(&cinfo); // 计算输出的行stride,jpeg库是按行往外读取,要计算每行读取多少数据 int row_stride = cinfo.output_width * cinfo.output_components; // 调用libjpeg内存申请接口,申请一个一行长的buffer JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1); // 循环从libjpeg读取行 到 buffer,然后将buffer内容写入文件 while (cinfo.output_scanline


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有