文章目录

  • 致谢
  • 预备知识
  • Python代码

致谢

这学期有幸参加学习学校韩宇星教授的 数字图像工程(全英) 课程,对机器视觉了解更进一步,对韩老师引用世事洞明皆学问,人情练达即文章那节课印象颇深。课程期间,通过网络博客资料学习,收获很多。为表感谢,我记录这篇学习笔记,希望为全世界知识共享迈出一小步。

预备知识

  • JPEG压缩原理详解:https://www.cnblogs.com/Arvin-JIN/p/9133745.html
  • 二维离散余弦变换2D-DCT实战:https://blog.csdn.net/ahafg/article/details/48808443
  • ZigZag变换加速,空间换时间做法:https://my.oschina.net/tigerBin/blog/1083549
  • 使用Python如何将数据1比特1比特地写进文件里:https://blog.csdn.net/weixin_43690347/article/details/84146979

感谢这些博主乐于分享知识的无私精神。

Python代码

接下来贴出本人的Python代码,程序效果如下:

其中amilk.bmp是原图,大小约2652KB;amilk.gpj是程序压缩后存储的文件,大小约380KB;resul.bmp是程序读取amilk.gpj后恢复出来的BMP文件,大小约1990KB,文件大小变化是因为JPEG是有损压缩,这必然导致部分高频信息丢失。

原图效果:

根据amilk.gpj反压缩得到的图片效果:
实验所用图片来源:https://blog.csdn.net/c980129/article/details/88263654

python代码如下:

import numpy as np
import os
from PIL import Imageclass KJPEG:def __init__(self):# 初始化DCT变换的A矩阵,https://blog.csdn.net/ahafg/article/details/48808443self.__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# https://my.oschina.net/tigerBin/blog/1083549# 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])def __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)elif tag == self.__ct:res = np.round(res / self.__cq)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 resdef 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部分# 原理详解:https://www.cnblogs.com/Arvin-JIN/p/9133745.htmlbuff = 0tfile = os.path.splitext(filename)[0] + ".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)# https://blog.csdn.net/weixin_43690347/article/details/84146979def __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.__lqelif tag == self.__ct:res *= self.__cqreturn 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))image.save("./result.bmp", "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 nif __name__ == '__main__':kjpeg = KJPEG()kjpeg.Compress("./amilk.bmp")kjpeg.Decompress("./amilk.gpj")

JPEG图片压缩的Python实现相关推荐

  1. mozjpeg:JPEG图片压缩5%,获Facebook支持

    mozjpeg是一个来自Mozilla实验室的JPEG图像编码器项目,目标是在不降低图像质量且兼容主流的解码器的情况下,提供产品级的JPEG格式编码器来提高压缩率以减小JPEG文件的大小. Mozil ...

  2. 【golang】处理jpeg图片压缩后方向发生变化问题

    本人宝宝出生后,特地为宝宝做了一个网站,用于上传照片.视频,想着是自己的网站,因此在前端上传的时候没有进行压缩,同时又为了提高图片加载速度,所以在后台进行了压缩.列表中看压缩图,点击看原图.然而通过苹 ...

  3. xHiveAI Jetson NX盒子:jpeg图片编码

    在实际的AI处理中,经常有保存和上传jpeg报警图片的操作.Nvidia提供的jpeg encoder库中只能对YUV图片来操作,我们盒子中的libjpegenc.so库提供了图片转换和jpeg压缩一 ...

  4. Android 图片压缩各种方式

    前言:由于公司项目当中需要用到压缩这块的相应技术,之前也做过的图片压缩都不是特别的理想, 所以这次花了很多心思,仔细研究和在网上找到了很多相对应的资料.为了就是 以后再做的时候直接拿来用就可以了! 第 ...

  5. jpg图片与jpeg图片格式的区别(没有区别,.jpg只是扩展名.jpeg的缩写)JPEG图像压缩(YUV4:2:0 缩减采样、缩减取样)(离散余弦变换 DCT算法)(量化)(熵编码)(霍夫曼哈夫曼)

    文章目录 20191026 20220414 更新,更系统去了解里面的编码压缩流程 科普:关于图像格式JPG和JPEG你知多少? 一.前言 二.JPEG和JPG的关系 三.色彩空间转换 缩减取样 离散 ...

  6. Android 高效图片压缩

    使用libjpeg-turbo进行图片压缩 1. JEPG 是什么? 相信有一部分使用 iPhone 手机用微信发送图片的时候,明明图片大小只有 1M ,但清晰度比 Android 手机 5 M 图片 ...

  7. python怎么把图片压缩_使用Python轻松批量压缩图片

    在互联网,图片的大小对一个网站的响应速度有着明显的影响,因此在提供用户预览的时候,图片往往是使用压缩后的.如果一个网站图片较多,一张张压缩显然很浪费时间.那么接下来,我就跟大家分享一个批量压缩图片的方 ...

  8. Python图片压缩

    Python图片压缩 原图片: 压缩后: 压缩后效果图: 使用方法: 在桌面文件夹新建名为"img"文件夹,将要压缩的图片放在里面即可,记得改变一下DIR路径. 第三方库:PIL ...

  9. 【Android 内存优化】Android 原生 API 图片压缩代码示例 ( PNG 格式压缩 | JPEG 格式压缩 | WEBP 格式压缩 | 动态权限申请 | Android10 存储策略 )

    文章目录 一. 图片质量压缩 二. 图片尺寸压缩 三. Android 10 文件访问 四. 完整源码示例 上一篇博客 [Android 内存优化]图片文件压缩 ( Android 原生 API 提供 ...

最新文章

  1. 操作系统 实验三 进程调度模拟程序
  2. 全球及中国膀胱癌药物行业“十四五”专项规划及市场调研分析报告2021-2027年
  3. linux禁止其他主机ping自己
  4. java重载与重写的区别+重写父类equals方法的完美实现
  5. 泰坦尼克号数据_数据分析-泰坦尼克号乘客生存率预测
  6. fiber报错 (type *big.Int has no field or method FillBytes)
  7. QT TextEdit设置背景、明文加密、弹出网站、弹出自定义对话框、gif动态图片、程序启动动画、打包程序、关闭事件
  8. OpenGL:配置glad
  9. 【Vue】五个最常用的事件修饰符
  10. Oracle 10g ORA-12154: TNS: could not resolve the connect identifier specified 问题解决!
  11. 关于OPENGL与OPENGL ES的区别
  12. 柏林噪声实践 水与火,顶点纹理拾取
  13. iphonexr电池容量_iPhone12mini电池容量多少毫安能用多久 iPhone12mini适合打游戏王者吗...
  14. 机器学习视频课程(超清完整11周)分享给大家!
  15. java微信授权登录回调地址,微信开发者工具,注册微信公共平台
  16. 数据库驱动加载失败问题
  17. office服务器草稿位置,精确定位图片在Word文档中的位置的操作方法
  18. 修改360浏览器主页
  19. Capture One 22 最新推出全景拼接功能
  20. 关闭 Flink Checkpoint,引发 P3 故障

热门文章

  1. Excel 使用vba脚本按条件清除行数据
  2. 区分event对象中的[clientX,offsetX,screenX,pageX]
  3. 利用亚运会,读懂 Python装饰器
  4. 拖拽功能之水平拖动图片
  5. pip安装三方库不成功的解决方法
  6. Lua实现md5加密
  7. 简单的python小程序祝福母亲,母亲节快乐!
  8. 一文了解各大数据库查询语言(Gremlin vs Cypher vs nGQL操作篇
  9. UiPath是做什么的
  10. 2.Ext JS MVVM特性