前言
图片压缩应用很广泛, 如生成缩略图等前期我在进行图片处理的过程中碰到了一个问题, 就是如何将图片压缩到指定尺寸, 此处尺寸指的是生成图片文件的大小
我使用 opencv 进行图片处理, 于是想着直接使用 opencv 进行图片压缩处理, opencv 本身包含了压缩到指定像素大小的方法, 奈何寻找了很多方法均不能压缩到指定文件尺寸, 于是自己在思考后写出了此方法本文使用 python 语言
一 opencv 常规使用
opencv 无需多言, 做过图片处理的人应该都知道此类库, 下面我介绍一些常用方法
1.1 安装 opencv
首先安装 python , 建议 python3 , 然后执行
- :
- pip install opencv-python
1.2 读取图片
首先引入 opencv 包:
import cv2 as cv
而后读取图片:
image = cv.imread(path)
其中 path 为图片路径, image 为图片数据, 是一个 numpy.ndarray 对象, 其实就是一个多维数组目前 opencv 支持几乎所有格式的图片 (参考 http://blog.csdn.net/mars_xiaolei/article/details/78890971)
1.3 保存图片
代码:
cv.imwrite(path, image)
其中 path 为保存的文件路径, image 为读取或者处理过的图片数据, opencv 根据保存文件的后缀名来写不同格式的图片数据, 所以后缀名一定要写正确
二图片压缩
2.1 常规压缩
opencv 支持常规压缩, 可以将图片压缩到指定的像素尺寸或者按比例缩放
压缩到指定的像素尺寸:
new_image = cv.resize(image, size)
其中 size 是一个二维元组, 表示压缩后图片的宽高
按比例缩放:
new_image = cv.resize(image, None, fx, fy)
其中 fx , fy 表示图片在宽和高方向的压缩了比例
2.2 压缩到指定文档大小
有了上面的基础我们来分析一下如何实现压缩到指定文档大小
首先我们要读取原始文档的大小, 算出原始文档大小和压缩目标值的比例, 由于我们要实现的是宽高等比例压缩, 于是将其开根号即表示在单边的压缩比例, 调用 2.1 节中的按比例压缩理论上一次就能达到效果, 但是由于图片本身存在压缩, 所以可能一次无法达到预期, 只要对压缩后的图片重复此步骤, 直到达到预期即可
2.2.1 读取文档尺寸
- def get_doc_size(path):
- try:
- size = os.path.getsize(path)
- return get_mb_size(size)
- except Exception as err:
- print(err)
- def get_mb_size(bytes):
- bytes = float(bytes)
- mb = bytes / 1024 / 1024
- return mb
get_doc_size 函数返回图片的文档大小, 单位为 MB
2.2.2 删除文件
- def delete_file(path):
- if file_exist(path):
- os.remove(path)
- else:
- print('no such file:%s' % path)
- def file_exist(path):
- return os.path.exists(path)
由于我们需要删除压缩过程中产生的中间文件, 所以需要调用 delete_file 方法删除之
2.2.3 压缩
- size = get_doc_size(path)
- delete_file(resize_path)
- while size > filesize:
- rate = math.ceil((size / filesize) * 10) / 10 + 0.1
- rate = math.sqrt(rate)
- rate = 1.0 / rate
- if file_exist(resize_path):
- resize_rate(resize_path, resize_path, rate, rate)
- else:
- resize_rate(path, resize_path, rate, rate)
- size = get_doc_size(resize_path)
其中 filesize 表示压缩目标值, path 表示原始文件路径, resize_path 表示压缩后存放路径, resize_rate 表示上述按比例压缩方法, 定义如下:
- def resize_rate(path, resize_path, fx, fy):
- image = read_image(path)
- im_resize = cv.resize(image, None, fx=fx, fy=fy)
- delete_file(resize_path)
- save_image(resize_path, im_resize)
- def save_image(path, image):
- cv.imwrite(path, image)
- def read_image(path):
- return cv.imread(path)
当然此处为了效果更好, 我做了一些优化
首先在获取压缩比例的时候我做了下述操作:
rate = math.ceil((size / filesize) * 10) / 10 + 0.1
理论情况应当是直接返回 size / filesize 即可, 但是在实际测试过程中为了加速收敛, 我采用上述方式, 将一个小数先乘以 10 对其向上取整, 这样就表示精度保留到原始数值小数后 1 位, 即如果是 3.14 将得到 32 , 而后将此结果再除以 10 , 即得到 3.2 , 所以最终结果就是对小数后第二位进行向上进位, 最后结果又加了 0.1 以更快速的收敛, 当然你也可以去掉
实际测试发现, 一般重复执行两次即可得到理想的压缩效果, 并且结果值与理想压缩尺寸相差无几
三结论
本文简单介绍了如何使用 opencv 将图片压缩到指定文件尺寸, 当然你也可以选择其他文件处理类库而不是 opencv , 这个完全可以根据用户自己的兴趣而来, 并且也可以优化最终的循环算法, 以达到更佳的效果, 或者更快的收敛
来源: https://www.cnblogs.com/shoufengwei/p/8526105.html