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, 参数值]
⚠️ 注意: imwrite() 保存的图像默认是BGR顺序。如果你用matplotlib显示过图像,再保存,颜色会乱掉。我遇到过好几次,同事把RGB图像直接传给imwrite,结果保存出来的图偏蓝。解决办法很简单:保存前用 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])
💡 我的经验: PNG压缩是「无损」的,所以不管压缩级别是0还是9,像素数据完全一样。区别只在文件大小和保存时间。我一般设成3到6之间,平衡速度和体积。设成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除非特殊需求否则别用。下次保存图像时,多花一秒钟想想,你到底要什么。