第2章 OpenCV基础与图像读写
说实话,OpenCV 这个名字听起来挺唬人的。Open Source Computer Vision Library,开源计算机视觉库。但说白了,它就是一套帮你处理图像和视频的工具集。我刚开始接触时也觉得门槛高,后来发现,只要把最基础的几个操作搞明白,后面的事情就顺了。
这一章,我们就从零开始。把 OpenCV 装好,学会怎么把一张图片读进来、显示出来、再存回去。最后,我们还要碰一碰像素——图像的本质,其实就是一堆数字。
2.1 OpenCV 是什么?
OpenCV 诞生于 1999 年,最初是英特尔的一个研究项目。到现在,它已经成了计算机视觉领域的事实标准。支持 C++、Python、Java 等多种语言,跨平台,而且——完全免费。
我个人习惯用 Python 来调 OpenCV,因为开发效率高。你写几行代码,就能看到效果。这种即时反馈,对学习来说太重要了。
- 2500+ 优化算法(图像处理、特征检测、机器学习等)
- 支持 Windows / Linux / macOS / Android / iOS
- 硬件加速(Intel IPP、CUDA、OpenCL)
- 社区活跃,文档齐全
2.2 安装与配置
安装 OpenCV 的 Python 版本,其实就一条命令的事。但我建议你用虚拟环境,别把系统 Python 搞乱了。我在项目里吃过这个亏——装了一堆包,版本冲突,最后只能重装系统。
# 创建虚拟环境(推荐)
python -m venv cv_env
source cv_env/bin/activate # Linux/macOS
cv_env\Scripts\activate # Windows
# 安装 OpenCV
pip install opencv-python
# 如果需要额外模块(如特征匹配、SIFT等)
pip install opencv-contrib-python
import cv2,如果不报错,就说明装好了。注意,包名叫 opencv-python,但导入时写的是 cv2。
你可能会问:为什么是 cv2?不是应该叫 cv 或者 opencv 吗?嗯,这里有个历史原因。OpenCV 的 C 接口叫 cv,C++ 接口叫 cv2。Python 绑定的是 C++ 版本,所以就叫 cv2 了。别纠结这个,记住就行。
2.3 图像的读取、显示与保存
这三个操作,是图像处理的“三板斧”。你以后写的每一个程序,几乎都会用到它们。
2.3.1 读取图像
用 cv2.imread() 读取图像。它返回的是一个 NumPy 数组。对,就是那个多维数组。图像在计算机里,其实就是一堆数字。
import cv2
# 读取图像(默认彩色模式)
img = cv2.imread('lena.jpg')
# 读取为灰度图
img_gray = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)
# 读取为原始格式(包含Alpha通道)
img_unchanged = cv2.imread('lena.jpg', cv2.IMREAD_UNCHANGED)
| 参数 | 含义 | 数值 |
|---|---|---|
cv2.IMREAD_COLOR |
彩色模式(默认) | 1 |
cv2.IMREAD_GRAYSCALE |
灰度模式 | 0 |
cv2.IMREAD_UNCHANGED |
包含Alpha通道 | -1 |
2.3.2 显示图像
用 cv2.imshow() 显示图像。它会弹出一个窗口,把图像展示给你看。
cv2.imshow('My Image', img)
cv2.waitKey(0) # 等待按键
cv2.destroyAllWindows()
cv2.waitKey(0) 的意思是:无限等待,直到用户按下任意键。如果你传一个数字,比如 cv2.waitKey(1000),那就是等待 1000 毫秒(1 秒),然后自动关闭。
我个人习惯在调试时用 cv2.waitKey(0),这样我可以仔细看看图像效果。确认没问题了,再按任意键继续。
2.3.3 保存图像
用 cv2.imwrite() 保存图像。它根据文件扩展名自动判断格式。
# 保存为 PNG
cv2.imwrite('output.png', img)
# 保存为 JPEG,指定质量(95%)
cv2.imwrite('output.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 95])
| 格式 | 扩展名 | 参数 |
|---|---|---|
| JPEG | .jpg / .jpeg | cv2.IMWRITE_JPEG_QUALITY (0-100) |
| PNG | .png | cv2.IMWRITE_PNG_COMPRESSION (0-9) |
| WebP | .webp | cv2.IMWRITE_WEBP_QUALITY (0-100) |
2.4 像素操作基础
图像的本质,就是像素。每个像素,就是一个数值。对于灰度图,每个像素是 0-255 的整数(0 黑,255 白)。对于彩色图,每个像素是三个数值(B、G、R),范围也都是 0-255。
你想想看,一张 1920×1080 的彩色图像,就是 1920×1080×3 = 6,220,800 个数字。我们做的所有图像处理,本质上就是在操作这些数字。
2.4.1 访问像素值
# 访问 (100, 200) 位置的像素
(b, g, r) = img[100, 200]
print(f'B: {b}, G: {g}, R: {r}')
# 只访问蓝色通道
b_value = img[100, 200, 0]
# 修改像素值
img[100, 200] = (255, 0, 0) # 变成蓝色
注意,OpenCV 的坐标是 (行, 列),也就是 (y, x)。这和数学里的 (x, y) 是反的。我第一次用的时候,经常搞混。后来养成了习惯:先写行(高度方向),再写列(宽度方向)。
2.4.2 图像属性
# 获取图像形状 (高度, 宽度, 通道数)
h, w, c = img.shape
print(f'高度: {h}, 宽度: {w}, 通道数: {c}')
# 获取像素总数
total_pixels = img.size
# 获取数据类型
dtype = img.dtype
2.4.3 感兴趣区域(ROI)
很多时候,我们只关心图像中的某一块区域。比如人脸检测,我们只想知道人脸在哪里。这时候,就可以用 ROI 来提取子图像。
# 提取 (50:150, 100:200) 区域
roi = img[50:150, 100:200]
# 将 ROI 复制到另一个位置
img[200:300, 300:400] = roi
.copy()。
2.5 本章知识体系
下面这张图,把本章的核心知识点串了起来。你可以把它当作一张地图,随时回来看看。
2.6 避坑指南
最后,分享几个我踩过的坑。你遇到了,能少走弯路。
- 路径问题:
imread()读取失败时不会报错,而是返回None。我曾经因为路径写错,调试了半天才发现图像根本没读进来。建议每次读图后检查一下:if img is None: print('读取失败')。 - BGR vs RGB: OpenCV 用 BGR,Matplotlib 用 RGB。如果你用 Matplotlib 显示 OpenCV 读的图像,记得转换:
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)。 - 窗口卡死: 如果你用了
cv2.imshow()但没有调用cv2.waitKey(),窗口会无响应。嗯,这个我犯过好几次。 - 数据类型: OpenCV 的图像数组默认是
uint8(0-255)。如果你做数学运算,结果可能溢出。记得用np.clip()或转换数据类型。