利用opencv与python3 JPEG压缩与解压实现
由于内容是从写好的word文件中复制过来,可能排版等会有各种问题,建议直接看github中的pdf
另外由于我写这份作业的时候还不熟悉py3,因此实际上由很多可以优化的地方,比如数组强烈建议使用numpy而不是此处的列表
github地址:https://github.com/c980129/JPEG
【代码有误,参考前请先参考评论】
JPEG压缩实现(Python3)
- RGB转YUV
JPEG会将彩色图像执行YUV或YIQ的颜色空间转换,二次采样JPEG采用4:2:0,所以这里使用YUV420的颜色空间。在JPEG中使用的颜色模型是YCbCr(由YUV调整而来)。对于一个2*2的块,我们会保存4个Y值,1个Cb值(取0行0列的Cb)与1个Cr值(取1行0列的Cr),6个值保存信息,因此Cb与Cr有一定损失。
其中我们用函数rgb2yuv(在RGB2YUV.py)中来实现颜色模型转换与二次采样。并分别用三个二维数组保存采取的Y、U、V值。于是我们能得到三张分别用Y、U、V生成的灰度图(由于U和V损失为原来的1/4,因此其图像的长宽也分别为原来的1/2)(为了减少绝对值将Y值减去128):
- 图像边长填充为8的倍数并等分
用DCT.fill(img)函数对二次采样得到的Y、U、V图像分别用0填充知道其矩阵的height和width都是8的倍数,因为DCT函数的参数是一个8*8的矩阵。同样用DCT.split(img)函数将图像以左到右,上到下的顺序分成多个8*8矩阵,并返回这些矩阵连成的数组。
- 离散余弦变换
用DCT.FDCT(block)函数对一个8*8矩阵进行二维离散余弦变换,保存得到的矩阵。
- 量化
在类Quantization中保存成员变量table0与table1作为亮度和色度的量化表,调用Quantization.quanY(img)与Quantization.quanUV(img)分别用于对Y图像与U、V图像量化。
- AC系数
用AC类中的ZScan(img)对一个图像进行Z型扫描,得到一个长度为63的数组(图像第一个像素并不需要,它将在DC系数中保存)。之后通过RLC(array)对上面得到的数组array进行RLC得到他们的游长编码。
- DC系数
用DC类中的DPCM(blocks)函数对所有图像的DC系数提取并返回它们的DPCM编码数组。
- 熵编码
压缩部分是无损压缩,对DC系数(一个数组)采用可变字长整数编码。将一个DC系数分成size和amplitude两部分,配合VLI(num)和toB(num)函数将一个DC系数转换为一个[size(num), s(string(B))]。
这里的s已经是二进制串了(暂且用字符串存储方便操作),size需要用哈夫曼编码压缩。这里使用JPEG推荐的哈夫曼编码(亮度与色度两个表)。
对于AC系数,我们知道AC系数采用有偿编码,由两个数runlength与value组成。先将value如同DC系数一样采用可变字长整数编码拆分成size与amplitude,然后runlength与size合并为symbol1,smplitude独立为symbol2。对于runlength大于15的数情况,在symbol1添加(15,0)表示(为了解码时能识别,应在前端添加)。然后对symbol1采用哈夫曼编码,对于symbol2则直接用上文可变字长整数编码得到的二进制码。Symbol1采用的哈夫曼编码同样用JPEG推荐的哈夫曼编码表,由于过长不在报告中贴出。最终可用函数AllCompressY与AllcompressUV(未贴出)将一个表格的数据转换为二进制字符串。参数为DC系数(一个)与AC系数(数组)。
在Test.py中将对所有函数测试(实际上每个类的python文件的下方注释部分都是对这个类中函数的单元测试,由于不方便在报告中列出因此采用这种方式)。
这是Y图像的第一个8*8矩阵:
这是该矩阵的DCT变换结果(取整):
量化结果(取整):
Z字形扫描,DC系数,AC系数(太长未列全),二进制字符串,串长:
可以看到此处二进制长217位,意思是已经将一个8*8*8(512)bit的图像压缩为217bit。
对于译码,我们需要事先保存图片的长宽(正如图片位流里会保存一样),以此计算出Y,U,V图像的矩阵数,才能对整个二进制流正确分割(在译码过程中分割)。
在Compress类的encoding函数中,参数是位流(字符串形式)与宽,高(整型)。我们根据宽高得到Y、U、V图像的8*8矩阵的数量(我们知道U和V是一样多的),然后从位流头部开始移动两个指针。我们需要先得到上面使用的四个哈夫曼编码表的反向映射(这里用字典)。我们知道两个指针之间的二进制码的含义必定在几个状态之间转换:读取DC系数的size(通过不断比较两个指针之间的位流是否为字典的key,是的话得到其value(这里指字典的value),即size,不是则移动尾指针);通过size得到新的头尾指针,得到amplitude;然后开始读取AC系数的(runlength,size),如同上面得到DC系数的size一样,获得size后以此得到amplitude;循环读取AC系数直到读取翻译到的(runlength, value)为(0,0)或得到63个AC系数为止,将DC系数与AC系数都加进各自的列表中(这两列表将存储全部Y矩阵的DC系数与AC系数)。U与V同理,最终我们得到Y、U、V的DC系数与AC系数的数据(由于这段代码比较长不贴出,在Compress.py中)
然后通过DC类中的DPCM2(DC)函数,传入各自的DC系数,得到三个其元素是一个矩阵的列表,每个矩阵的[0][0]都通过DC系数还原为编码前
然后对每个矩阵分别通过对应的DC系数还原为63个数(AC.RLE(array))
再Z形填进矩阵中(AC.Z2Tab函数)(这段较长不贴出)
分别逆量化(Quantization.reY(img)与Quantization.reUV(img))(因为亮度与色度量化表不一样因此要分别操作)
再二维逆离散余弦变换(DCT.IDCT(img)。
这段操作每个函数的对象都是单个8*8矩阵
通过对所有矩阵进行同样的操作我们能得到所有当初刚分割完的8*8矩阵(不算损失的话)。
同样通过对这些矩阵进行合拼并将当初填充的0割掉,得到Y、U、V图像(这段代码较长不贴出,在DCT.py的DCT.merge()中,注意需要图像的长宽作为参数)
经此我们能得到YUV420的原图像。
关于测试代码,可以直接运行Test.py,会打印得到对每个函数的测试,或者运行Main.py,将执行对图像从压缩到解压的全部操作。Main.py会打印压缩得到的位流长,并在代码的上一层目录中创建一个txt.txt文件用字节形式保存每个位的数据(主要是用于测试解压代码时可以免去压缩过程迅速开始测试)。
运行Main.py会打印位流的长度,图片的高度与宽度(因此推荐在终端窗口中运行而不是直接运行py文件),并显示解压后的图片
我们直到682*1024个像素,如果直接用RGB的24位保存将要16760832bit
而这里压缩后得到的位流是1388439bit(虽然这里是字符串形式而非位流形式,但为了方便省去了以位流写文件的步骤,我们直接通过字符串的长度来对比)。压缩率是8.3%。
然而压缩的代价也十分明显(如图)。其中黑色变为其他颜色的点主要是YUV420中大量色度损失导致的(直接将图片从RGB转为YUV420再直接转为RGB就会有这些点的损失,因此如此推断),颜色也同样有不少损失,远处的云能看到明显的格子化。
解压后:
原图:
③ 结果对比:
压缩率约为8.3%的JPEG图(忽略文件信息等真正JPEG文件可能会造成的误差)
体积大约为170KB。与原文件接近,或许是我二度采样哪里理解错了或者写错了导致失真严重。
用PS使压缩的GIF也接近170KB(调整损失),得到的图像明显颜色(只有256)不足导致颜色接近的区块容易模糊融在一起。但整体色彩比起jpeg更接近原图,但是缺少细节。因此像照片这种细节多的图片,在体积相同的情况下还是选择JPEG更合适。或许是我的代码或者算法不够好导致的损失,也可能是我的二度采样是在原JPEG已经压缩过的二度采样的基础上的再次二度采样导致的损失,实际上原图jpg也是170KB体积,但表现效果十分好,比起我的压缩解压以及PS的转换为GIF都是如此。
GIF与JPEG文件保存在根目录下。代码保存在py文件夹中。
补充:色差原因请参考评论的朋友的描述,未测试
利用opencv与python3 JPEG压缩与解压实现相关推荐
- Java利用Gzip对字符串进行压缩与解压
在某些业务场景下,可能需要对字符串进行压缩与解压,压缩字符串可以使用 GZIPOutputStream 输出流来实现,而解压可以使用 GZIPInputStream 输入流来实现,下面先给出具体的参考 ...
- 利用huffman编码对文本文件进行压缩与解压(java实现)
利用huffman编码对文本文件进行压缩与解压 输入:一个文本文件 输出:压缩后的文件 算法过程: (1)统计文本文件中每个字符的使用频度 (2)构造huffman编码 (3)以二进制流形式压缩文件 ...
- python3 zlib 实现压缩与解压字符串与文件数据流
关于python3 zlib 压缩解压情况总结如下: 字符串:使用zlib.compress方法压缩字符串,使用zlib.decompress方法解压字符串. 数据流:压缩:zlib.compress ...
- linux 压缩文件夹格式,Linux下常见文件格式的压缩、解压小结
Linux下常见文件格式的压缩.解压小结 .tar 解包: tar xvf FileName.tar 打包:tar cvf FileName.tar DirName (注:tar是打包,不是压缩!) ...
- Linux操作系统中,*.zip、*.tar、*.tar.gz、*.tar.bz2、*.tar.xz、*.jar、*.7z等格式的压缩与解压...
转:http://www.cnblogs.com/yejianfei/archive/2013/10/04/3351626.html zip格式 压缩: zip -r [目标文件名].zip [原文件 ...
- 按压缩格式整理打包(解包)和压缩(解压)命令
文章目录 一.zip 格式 (一)使用命令 zip 压缩文件 (二)使用命令 unzip 解压 zip 包 二.tar 格式 (一)打包文件 (二)解包 tar 包 三.tar.gz 格式 方式一:利 ...
- 如何在 C# 中用 SharpZipLib 进行 ZIP 压缩与解压(转)
转自:http://www.cftea.com/c/2008/04/A1FQ34RYSYNLFT47.asp SharpZipLib 是一个免费的组件,可以利用它对 ZIP 等多种格式进行压缩与解压. ...
- linux 常用压缩格式,Linux常见压缩格式之压缩与解压
Linux常见压缩格式之压缩与解压 zip格式 压缩:zip -r [目标文件名].zip [原文件/目录名] 解压:unzip [原文件名].zip 注:-r参数代表递归 # Extract arc ...
- linux-应用-压缩与解压小结
linux下的压缩与解压 摘要:主要说明在linux下的常用的压缩和解压程序的用法. 在压缩指令中最早的要算是compress了,但现在它基本上不是预设的压缩指令了. 后来被gzip和bzip代替了, ...
- linux快速解压缩,快速了解linux压缩与解压
旨在快速了解linux文档压缩与解压,如需详细理解某个命令可用man一下,或者google一下. GUN-zip(gz) 压缩:gzip testfile #压缩testfile文件,生成 ...
最新文章
- P1083 借教室(标记永久化线段树/二分+前缀和)难度⭐⭐⭐★
- linux安装系统配置环境变量,Linux系统安装jdk及配置环境变量的方法
- java更新无法正常安装_Java无法安装
- php有个qrcode类,一个PHP的QRcode类与大家分享
- MySQL高级 - 日志 - 错误日志
- MIPS投RISC-V是龙芯新征程的开始
- WidsMob Viewer Pro Mac如何批量调整照片大小及格式
- unity 加载关卡_Unity手游实战:从0开始SLG——本地化篇(四)提取本地化元素
- 台达b3伺服参数设置方法_台达B2系列伺服电机的调试方法和注意事项
- dcdc芯片效率不高的原因_影响DC-DC转换器效率的主要因素
- 显示器偏色测试软件,显示器偏色,如何解决显示器偏色?
- 由于目标计算机积极拒绝,无法连接。 Could not connect to Redis at 127.0.0.1:6379: 由于目标计算机积极拒绝,无法连接
- 字节架构师:来说说 Kafka 的消费者客户端详解,你都搞懂了吗。
- 记一次RATEL脱壳配合Il2CppDumper解密完成的样本分析
- ug中许可证服务器,ug教程:ug服务器许可证(LMTOOLS)设置教程
- RK系列(RK3568) 收音机tef6686芯片驱动,i2c驱动
- [模拟电路]ADI放大器笔记 - 差分放大器单端输入电阻设计
- matplotlib绘制3D图像
- 51单片机距离测试软件,单片机超声波传感器测量距离
- google日历的农历循环提醒