JPEG图片压缩的Python实现









注意:比较压缩率是 比较 原图与gpj文件的 大小 而非 解压后的bmp ,不管Q选择多少 解压后的bmp大小都是一样的。

import numpy as np
import os
from PIL import Image, ImageChops
import mathclass KJPEG():def __init__(self,Q=1):# 初始化DCT变换的A矩阵self.__dctA = np.zeros(shape=(8, 8))for i in range(8):c = 0if i == 0:c = np.sqrt(1 / 8)else:c = np.sqrt(2 / 8)for j in range(8):self.__dctA[i, j] = c * np.cos(np.pi * i * (2 * j + 1) / (2 * 8))# 亮度量化矩阵self.__lq = np.array([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,])# 色度量化矩阵self.__cq = np.array([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,])# 标记矩阵类型,lt是亮度矩阵,ct是色度矩阵self.__lt = 0self.__ct = 1# Zig编码表self.__zig = np.array([0, 1, 8, 16, 9, 2, 3, 10,17, 24, 32, 25, 18, 11, 4, 5,12, 19, 26, 33, 40, 48, 41, 34,27, 20, 13, 6, 7, 14, 21, 28,35, 42, 49, 56, 57, 50, 43, 36,29, 22, 15, 23, 30, 37, 44, 51,58, 59, 52, 45, 38, 31, 39, 46,53, 60, 61, 54, 47, 55, 62, 63])# Zag编码表self.__zag = np.array([0, 1, 5, 6, 14, 15, 27, 28,2, 4, 7, 13, 16, 26, 29, 42,3, 8, 12, 17, 25, 30, 41, 43,9, 11, 18, 24, 31, 40, 44, 53,10, 19, 23, 32, 39, 45, 52, 54,20, 22, 33, 38, 46, 41, 55, 60,21, 34, 37, 47, 50, 56, 59, 61,35, 36, 48, 49, 57, 58, 62, 63])self.__Q=Qdef __Rgb2Yuv(self, r, g, b):# 从图像获取YUV矩阵y = 0.299 * r + 0.587 * g + 0.114 * bu = -0.1687 * r - 0.3313 * g + 0.5 * b + 128v = 0.5 * r - 0.419 * g - 0.081 * b + 128return y, u, vdef __Fill(self, matrix):# 图片的长宽都需要满足是16的倍数(采样长宽会缩小1/2和取块长宽会缩小1/8)# 图像压缩三种取样方式4:4:4、4:2:2、4:2:0fh, fw = 0, 0if self.height % 16 != 0:fh = 16 - self.height % 16if self.width % 16 != 0:fw = 16 - self.width % 16res = np.pad(matrix, ((0, fh), (0, fw)), 'constant',constant_values=(0, 0))return resdef __Encode(self, matrix, tag):# 先对矩阵进行填充matrix = self.__Fill(matrix)# 将图像矩阵切割成8*8小块height, width = matrix.shape# 减少for循环语句,利用numpy的自带函数来提升算法效率# numpy的函数自带并行处理,不用像for循环一样串行处理shape = (height // 8, width // 8, 8, 8)strides = matrix.itemsize * np.array([width * 8, 8, width, 1])blocks = np.lib.stride_tricks.as_strided(matrix, shape=shape, strides=strides)res = []for i in range(height // 8):for j in range(width // 8):res.append(self.__Quantize(self.__Dct(blocks[i, j]).reshape(64), tag))return resdef __Dct(self, block):# DCT变换res = np.dot(self.__dctA, block)res = np.dot(res, np.transpose(self.__dctA))return resdef __Quantize(self, block, tag):res = blockif tag == self.__lt:res = np.round(res / (self.__lq*self.__Q))elif tag == self.__ct:res = np.round(res / (self.__lq*self.__Q))return resdef __Zig(self, blocks):ty = np.array(blocks)tz = np.zeros(ty.shape)for i in range(len(self.__zig)):tz[:, i] = ty[:, self.__zig[i]]tz = tz.reshape(tz.shape[0] * tz.shape[1])return tz.tolist()def __Rle(self, blist):res = []cnt = 0for i in range(len(blist)):if blist[i] != 0:res.append(cnt)res.append(int(blist[i]))cnt = 0elif cnt == 15:res.append(cnt)res.append(int(blist[i]))cnt = 0else:cnt += 1# 末尾全是0的情况if cnt != 0:res.append(cnt - 1)res.append(0)return res#压缩def Compress(self, filename):# 根据路径image_path读取图片,并存储为RGB矩阵image = Image.open(filename)# 获取图片宽度width和高度heightself.width, self.height = image.sizeimage = image.convert('RGB')image = np.asarray(image)r = image[:, :, 0]g = image[:, :, 1]b = image[:, :, 2]# 将图像RGB转YUVy, u, v = self.__Rgb2Yuv(r, g, b)# 对图像矩阵进行编码y_blocks = self.__Encode(y, self.__lt)u_blocks = self.__Encode(u, self.__ct)v_blocks = self.__Encode(v, self.__ct)# 对图像小块进行Zig编码和RLE编码y_code = self.__Rle(self.__Zig(y_blocks))u_code = self.__Rle(self.__Zig(u_blocks))v_code = self.__Rle(self.__Zig(v_blocks))# 计算VLI可变字长整数编码并写入文件,未实现Huffman部分buff = 0tfile = os.path.splitext(filename)[0]+'_%d'%self.__Q + ".gpj"if os.path.exists(tfile):os.remove(tfile)with open(tfile, 'wb') as o:o.write(self.height.to_bytes(2, byteorder='big'))o.flush()o.write(self.width.to_bytes(2, byteorder='big'))o.flush()o.write((len(y_code)).to_bytes(4, byteorder='big'))o.flush()o.write((len(u_code)).to_bytes(4, byteorder='big'))o.flush()o.write((len(v_code)).to_bytes(4, byteorder='big'))o.flush()self.__Write2File(tfile, y_code, u_code, v_code)def __Write2File(self, filename, y_code, u_code, v_code):with open(filename, "ab+") as o:buff = 0bcnt = 0data = y_code + u_code + v_codefor i in range(len(data)):if i % 2 == 0:td = data[i]for ti in range(4):buff = (buff << 1) | ((td & 0x08) >> 3)td <<= 1bcnt += 1if bcnt == 8:o.write(buff.to_bytes(1, byteorder='big'))o.flush()buff = 0bcnt = 0else:td = data[i]vtl, vts = self.__VLI(td)for ti in range(4):buff = (buff << 1) | ((vtl & 0x08) >> 3)vtl <<= 1bcnt += 1if bcnt == 8:o.write(buff.to_bytes(1, byteorder='big'))o.flush()buff = 0bcnt = 0for ts in vts:buff <<= 1if ts == '1':buff |= 1bcnt += 1if bcnt == 8:o.write(buff.to_bytes(1, byteorder='big'))o.flush()buff = 0bcnt = 0if bcnt != 0:buff <<= (8 - bcnt)o.write(buff.to_bytes(1, byteorder='big'))o.flush()buff = 0bcnt = 0def __IDct(self, block):# IDCT变换res = np.dot(np.transpose(self.__dctA), block)res = np.dot(res, self.__dctA)return resdef __IQuantize(self, block, tag):res = blockif tag == self.__lt:res *= self.__lq*self.__Qelif tag == self.__ct:res *= self.__cq*self.__Qreturn resdef __IFill(self, matrix):matrix = matrix[:self.height, :self.width]return matrixdef __Decode(self, blocks, tag):tlist = []for b in blocks:b = np.array(b)tlist.append(self.__IDct(self.__IQuantize(b, tag).reshape(8 ,8)))height_fill, width_fill = self.height, self.widthif height_fill % 16 != 0:height_fill += 16 - height_fill % 16if width_fill % 16 != 0:width_fill += 16 - width_fill % 16rlist = []for hi in range(height_fill // 8):start = hi * width_fill // 8rlist.append(np.hstack(tuple(tlist[start: start + (width_fill // 8)])))matrix = np.vstack(tuple(rlist))res = self.__IFill(matrix)return resdef __ReadFile(self, filename):with open(filename, "rb") as o:tb = o.read(2)self.height = int.from_bytes(tb, byteorder='big')tb = o.read(2)self.width = int.from_bytes(tb, byteorder='big')tb = o.read(4)ylen = int.from_bytes(tb, byteorder='big')tb = o.read(4)ulen = int.from_bytes(tb, byteorder='big')tb = o.read(4)vlen = int.from_bytes(tb, byteorder='big')buff = 0bcnt = 0rlist = []itag = 0icnt = 0vtl, tb, tvtl = None, None, Nonewhile len(rlist) < ylen + ulen + vlen:if bcnt == 0:tb = o.read(1)if not tb:breaktb = int.from_bytes(tb, byteorder='big')bcnt = 8if itag == 0:buff = (buff << 1) | ((tb & 0x80) >> 7)tb <<= 1bcnt -= 1icnt += 1if icnt == 4:rlist.append(buff & 0x0F)elif icnt == 8:vtl = buff & 0x0Ftvtl = vtlitag = 1buff = 0else:buff = (buff << 1) | ((tb & 0x80) >> 7)tb <<= 1bcnt -= 1tvtl -= 1if tvtl == 0 or tvtl == -1:rlist.append(self.__IVLI(vtl, bin(buff)[2:].rjust(vtl, '0')))itag = 0icnt = 0y_dcode = rlist[:ylen]u_dcode = rlist[ylen:ylen+ulen]v_dcode = rlist[ylen+ulen:ylen+ulen+vlen]return y_dcode, u_dcode, v_dcodepassdef __Zag(self, dcode):dcode = np.array(dcode).reshape((len(dcode) // 64, 64))tz = np.zeros(dcode.shape)for i in range(len(self.__zag)):tz[:, i] = dcode[:, self.__zag[i]]rlist = tz.tolist()return rlistdef __IRle(self, dcode):rlist = []for i in range(len(dcode)):if i % 2 == 0:rlist += [0] * dcode[i]else:rlist.append(dcode[i])return rlistdef Decompress(self, filename):y_dcode, u_dcode, v_dcode = self.__ReadFile(filename)y_blocks = self.__Zag(self.__IRle(y_dcode))u_blocks = self.__Zag(self.__IRle(u_dcode))v_blocks = self.__Zag(self.__IRle(v_dcode))y = self.__Decode(y_blocks, self.__lt)u = self.__Decode(u_blocks, self.__ct)v = self.__Decode(v_blocks, self.__ct)r = (y + 1.402 * (v - 128))g = (y - 0.34414 * (u - 128) - 0.71414 * (v - 128))b = (y + 1.772 * (u - 128))r = Image.fromarray(r).convert('L')g = Image.fromarray(g).convert('L')b = Image.fromarray(b).convert('L')image = Image.merge("RGB", (r, g, b))wfile = os.path.splitext(filename)[0] + "result.bmp"# image.save("./result.bmp", "bmp")image.save(wfile, "bmp")# image.show()def __VLI(self, n):# 获取整数n的可变字长整数编码ts, tl = 0, 0if n > 0:ts = bin(n)[2:]tl = len(ts)elif n < 0:tn = (-n) ^ 0xFFFFtl = len(bin(-n)[2:])ts = bin(tn)[-tl:]else:tl = 0ts = '0'return (tl, ts)def __IVLI(self, tl, ts):# 获取可变字长整数编码对应的整数nif tl != 0:n = int(ts, 2)if ts[0] == '0':n = n ^ 0xFFFFn = int(bin(n)[-tl:], 2)n = -nelse:n = 0return nfrom matplotlib import pyplot as plt
if __name__ == '__main__':Qun=[20,60,80]pic_id=[3,4]for id in pic_id:for Q in Qun:kjpeg = KJPEG(Q)kjpeg.Compress("%d.jpg"%id)# print('"%d_%d压缩完成'%(id,Q))kjpeg.Decompress("%d_%d.gpj"%(id,Q))# print('解压缩完成')# 计算压缩比oldpic="%d.jpg"%idcomgpj="%d_%d.gpj"%(id,Q)comppic="%d_%dresult.bmp"%(id,Q)size1 = os.path.getsize(oldpic)# print(size1)size11 = size1 / 1024size2 = os.path.getsize(comgpj)# print(size2)size22 = size2 / 1024ys_rate = size1 / size2print(oldpic+'与'+comppic+'的')print('压缩比为' + str(ys_rate) )# 计算均方根误差im1 = Image.open(oldpic)im2 = Image.open(comppic)diff = ImageChops.difference(im1.convert('RGBA'), im2.convert('RGBA')) # 计算两个图像之间逐像素差异的绝对值h = diff.histogram()sq = (value * ((idx % 256) ** 2) for idx, value in enumerate(h))sum_of_squares = sum(sq)rms = math.sqrt(sum_of_squares / float(im1.size[0] * im1.size[1]))print('均方根误差为' + str(rms))#画图img1 = plt.imread('3.jpg')img1_20=plt.imread('3_20result.bmp')img1_60 = plt.imread('3_60result.bmp')img1_80 = plt.imread('3_80result.bmp')plt.subplot(221), plt.imshow(img1), plt.title('original'), plt.axis('off')plt.subplot(222), plt.imshow(img1_20), plt.title('Q=20'), plt.axis('off')plt.subplot(223), plt.imshow(img1_60), plt.title('Q=60'), plt.axis('off')plt.subplot(224), plt.imshow(img1_80), plt.title('Q=80'), plt.axis('off')plt.savefig('1_JPEG.png')img2 = plt.imread('4.jpg')img2_20=plt.imread('4_20result.bmp')img2_60 = plt.imread('4_60result.bmp')img2_80 = plt.imread('4_80result.bmp')plt.subplot(221), plt.imshow(img2), plt.title('original'), plt.axis('off')plt.subplot(222), plt.imshow(img2_20), plt.title('Q=20'), plt.axis('off')plt.subplot(223), plt.imshow(img2_60), plt.title('Q=60'), plt.axis('off')plt.subplot(224), plt.imshow(img2_80), plt.title('Q=80'), plt.axis('off')plt.savefig('2_JPEG.png')

数字图像处理作业: 包含质量因子的 JPEG压缩 python代码相关推荐

  1. 几何畸变图像恢复 openCV3 - 数字图像处理作业3

    几何畸变图像恢复 OpenCV3 - 数字图像处理作业3 作业3:相同条件下拍到的棋盘图和日历钟表图,尝试建立几何畸变关系,并对它们进行恢复.注意:不能采用椭圆的变换. 算法步骤: 坐标变换: 在畸变 ...

  2. 数字图像处理作业——直方图均衡处理

    将下面的图像进行直方图均衡处理. (1)列表写出图像直方图均衡化的过程(书上的格式或补充课件上的格式均可以): (2)画出均衡化以后的图像: (3)画出原始图像直方图和均衡化以后的图像直方图. [例一 ...

  3. 数字图像处理作业文档整合

    第一章: 1-3课时 一.单选题 1.一幅数字图像是:( ). A.一个观测系统 B.一个有许多像素排列而成的实体 C.一个2-D数组中的元素 D.一个3-D空间的场景 正确答案: B 二.多选题(共 ...

  4. 美颜算法--数字图像处理作业

    1. 简答题 请为图中人做美肌处理,去痘或去皱纹.(2选1,或自选图片) 作业内容请包含所用的算法说明,效果图,主要算法的代码,以及对效果图的分析.所有内容用图片或文字的形式提交,不要用附件. 2. ...

  5. 数字图像处理作业-医学图像浏览器

    使用Qt框架编写的一个支持课程提供的.raw格式16位灰度图操作的图像处理器,自定义Image类型,封装几何变换.灰度映射.图像增强等功能,可以多步撤回及多图切换,附加一个比较简单的伪彩色图生成. 感 ...

  6. 数字图像处理:第二十章 视频编码与压缩

    第二十章 视频编码与压缩 目录 1.    引言 2.    有运动补偿的预测编码 3.    运动估计算法 作业 1. 引言 视频信号具有巨大的数据量,例如:在不做任何压缩的条件下,对于NTSC视频 ...

  7. 【数字图像处理】图像内插“双线性内插法 Bilinear interpolation”代码演示(以像素中心点确定像素位置)(图像放大缩小)具有低通滤波性质,使高频分量受损,图像轮廓可能会有一点模糊

    文章目录 原理 代码 开了个jit加速(没有提高太多) 开numexpr加速 总结 直接调用opencv库(不知为啥那么快) 原理 双线性内插法是使用目标点四周最近的四个点,沿距目标点x方向.y方向的 ...

  8. 数值分析实习作业(各种插值函数与积分公式的python代码实现)

    数值分析实习作业 1.对函数 f(x) f ( x ) f(x)进行插值 f(x)=11+x2 f ( x ) = 1 1 + x 2 f(x)=\frac{1}{1+x^{2}} (1)令插值节点为 ...

  9. 数字图像处理第五次作业——频域滤波器

    目 录 一.基本概念及原理 1. 理想低通滤波器: 2. 巴特沃斯低通滤波器: 3. 高斯低通滤波器: 4. 高通滤波器: 5. 拉普拉斯高通滤波器: 6. Unmask高通滤波器: 二.实现过程和结 ...

最新文章

  1. ORA-10873解决办法
  2. 能干掉苹果的中国黑客
  3. java 断点续传组件_chunkupload 文件上传断点续传组件(java) - 正式发布
  4. html原文档流样式,html之样式
  5. 【报告分享】2019双11洞察报告资料包(含6大权威报告下载链接)
  6. 性能 1.84 倍于 Ceph!网易数帆开源分布式存储系统 Curve
  7. Vue中条件判断 v-if 、v-show
  8. 蚂蚁森林用户须知_公益传播视角下“蚂蚁森林”的用户使用行为研究
  9. 冒泡排序,插入排序--- PYTHON
  10. linux查看硬件信息及驱动设备
  11. MVC.NET 出现诡异的 “IIS运行停止”
  12. css img 适配尺寸_一次解决你的图像尺寸和定位问题
  13. 将谷歌浏览器(chrome)设置为全黑色主题背景
  14. 2^n-1的因数分解问题
  15. 语音特征:mfcc、fbank和语谱图概述
  16. nodejs之utility,crypto使用
  17. 自动驾驶技术-环境感知篇:V2X技术的介绍
  18. 新起典|《奇妙·夜德天蚂拐节》定终身场景多媒体内容升级项目
  19. sortby降序java_lodash多列sortBy降序
  20. 抓包工具charles实践分享

热门文章

  1. 客户端接收WIFI发送的数据
  2. 数据集大全之 披萨店销售含披萨配料、销售信息、订单信息(含数据集)
  3. DSO(5)——零空间的计算与推导
  4. 金蝶EAS新增数据库字段并在列表上显示
  5. 美团热更新方案 ASM 实践
  6. 程序员应该学会正确休息
  7. 计算机网络基础知识点学习(一)(概述)
  8. 流量红利退潮后互联网产品怎么取得成功
  9. linux 下通过smbclient访问windows共享目录
  10. python实现汉诺塔递归经典算法_Python递归实现汉诺塔算法示例