4. 图像保存与格式:imwrite()函数、JPEG/PNG/BMP格式对比、压缩质量参数
好,图像处理完了,显示也看过了,接下来最关键的一步——保存。你想想看,辛辛苦苦调了半天参数,结果忘了保存,或者保存成了错误的格式,那真是欲哭无泪。我早期做项目时就干过这种蠢事,调了一下午的滤波效果,最后用默认参数存成了低质量的JPEG,细节全丢了,被项目经理骂得狗血淋头。
今天我们就来彻底搞懂OpenCV的保存函数,以及几种常见图像格式的「脾气秉性」。
4.1 imwrite():保存图像的核心函数
OpenCV里保存图像的函数就是 cv2.imwrite()。它的用法其实很简单,但有几个坑,我一个个说。
import cv2
# 读取一张图
img = cv2.imread('example.jpg')
# 保存为PNG
cv2.imwrite('output.png', img)
# 保存为JPEG,并指定质量
cv2.imwrite('output_high.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 95])
函数原型长这样:cv2.imwrite(filename, img, params=None)
- filename:文件名。扩展名决定了保存格式。写
.jpg就是JPEG,写.png就是PNG。 - img:要保存的图像数据,numpy数组。
- params:可选参数。以列表形式传入,成对出现。比如
[参数ID, 参数值]。
cv2.cvtColor(img, cv2.COLOR_RGB2BGR) 转一下。
4.2 JPEG / PNG / BMP:三种格式的「性格」对比
这三种格式,说白了就是「要质量还是要体积」的博弈。我根据自己的经验,给你总结一下:
| 特性 | JPEG (.jpg / .jpeg) | PNG (.png) | BMP (.bmp) |
|---|---|---|---|
| 压缩方式 | 有损压缩 | 无损压缩 | 无压缩 |
| 文件大小 | 小(可调) | 中等 | 巨大 |
| 支持透明通道 | 不支持 | 支持(Alpha通道) | 不支持 |
| 色彩深度 | 8位/通道 | 8位或16位/通道 | 最高24位 |
| 适用场景 | 照片、网络传输 | 图标、截图、需要透明的图 | 临时文件、绝对无损需求 |
JPEG:我个人的习惯是,如果图片最终要放到网页上,或者通过微信发送,我会用JPEG。它体积小,传输快。但代价是细节会丢失。你放大看JPEG图片,边缘会有「马赛克」一样的块状伪影,这就是有损压缩的痕迹。
PNG:这个格式我特别喜欢。它保存的是像素的精确值,一个点都不会变。我在做图像标注、医学图像处理时,一律用PNG。另外,PNG支持透明背景,做UI切图时必不可少。
BMP:说实话,现在很少用了。它不压缩,文件大得离谱。一张1920x1080的BMP图片,轻松几十MB。我只有在做底层像素操作、需要绝对保证数据不被任何算法修改时,才会用BMP作为中间格式。
4.3 压缩质量参数:怎么调才合适?
这里重点讲JPEG和PNG的参数设置。嗯,这里要注意,参数不是越大越好,也不是越小越好。
JPEG质量参数
参数ID是 cv2.IMWRITE_JPEG_QUALITY,取值范围0到100。数值越大,质量越高,文件也越大。
# 高质量(95),适合存档
cv2.imwrite('high.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 95])
# 中等质量(75),适合网络传输
cv2.imwrite('medium.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 75])
# 低质量(30),文件极小,但画质惨不忍睹
cv2.imwrite('low.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 30])
我个人建议:日常用85到95之间。低于70,图像中的文字边缘就开始模糊了。我曾经为了省空间,把一批文档扫描件压缩到50,结果OCR识别率直接掉了20个百分点,得不偿失。
PNG压缩级别
PNG的参数是 cv2.IMWRITE_PNG_COMPRESSION,取值范围0到9。0表示不压缩,9表示最大压缩。
# 不压缩,保存速度快,文件大
cv2.imwrite('fast.png', img, [cv2.IMWRITE_PNG_COMPRESSION, 0])
# 最大压缩,保存慢,文件小
cv2.imwrite('small.png', img, [cv2.IMWRITE_PNG_COMPRESSION, 9])
4.4 实战:批量保存与格式转换
最后,给你一个我常用的批量处理脚本。假设你有一堆BMP文件,想全部转成JPEG并压缩到合适大小。
import cv2
import os
import glob
# 获取所有bmp文件
bmp_files = glob.glob('input/*.bmp')
for file in bmp_files:
# 读取
img = cv2.imread(file)
# 生成输出文件名
base_name = os.path.basename(file)
name_without_ext = os.path.splitext(base_name)[0]
output_path = f'output/{name_without_ext}.jpg'
# 保存为JPEG,质量85
cv2.imwrite(output_path, img, [cv2.IMWRITE_JPEG_QUALITY, 85])
print(f'已转换: {file} -> {output_path}')
这个脚本我用了好多年,每次从相机导出RAW转成BMP再批量处理时,都会跑一遍。你想想看,如果手动一张张转,几百张图得转到什么时候?
好了,关于图像保存和格式,核心就这些。记住一句话:JPEG省空间但丢细节,PNG保质量但体积大,BMP除非特殊需求否则别用。下次保存图像时,多花一秒钟想想,你到底要什么。