4. OpenCV基础与环境搭建
OpenCV 是工业视觉绕不开的基础库。我入行那会儿,OpenCV 2.x 刚流行,现在都 4.x 了。但说实话,核心的东西没怎么变——Mat 结构、色彩空间、读写显示,这些基本功打扎实了,后面做缺陷检测才能顺手。
这一章,咱们就把环境搭好,把几个最常用的操作捋一遍。别小看这些基础,我在产线上见过不少问题,最后查出来都是色彩空间搞错了,或者 Mat 数据没理解透。
4.1 OpenCV的安装与配置
安装其实很简单,但有几个坑要注意。我个人习惯用 pip 安装,省事。
核心命令:
# 基础版(CPU 推理够用)
pip install opencv-python
# 扩展版(包含 contrib 模块,比如特征匹配、aruco)
pip install opencv-contrib-python
我的建议:工业场景下,除非你需要 SIFT/SURF 这类专利算法,否则装基础版就行。扩展版包更大,但很多模块你用不上。
验证安装是否成功,跑一行代码:
import cv2
print(cv2.__version__) # 输出类似 '4.8.0'
如果报错,多半是环境变量问题。我曾经在 Windows 上折腾了半天,最后发现是 Anaconda 环境没切对。嗯,这里要注意——用虚拟环境管理项目,别一股脑装到 base 里。
| 操作系统 | 推荐安装方式 | 注意事项 |
|---|---|---|
| Windows | pip install opencv-python | 确保 Visual C++ 运行库已安装 |
| Linux (Ubuntu) | sudo apt install python3-opencv | 或 pip 安装,注意系统 Python 版本 |
| macOS | brew install opencv | 或 pip 安装,M1/M2 芯片注意架构 |
4.2 核心数据结构:Mat
Mat 是 OpenCV 的灵魂。说白了,它就是一张图片在内存里的表示方式——一个多维数组。
你想想看,一张 640×480 的灰度图,就是 640 列、480 行、每个像素一个值(0~255)。彩色图呢?多了一个通道——BGR(注意,不是 RGB)。
Mat 的核心属性:
- rows / cols:行数(高度)和列数(宽度)
- channels():通道数,灰度图是 1,彩色图是 3
- depth():像素深度,常见 CV_8U(8位无符号)
- data:指向像素数据的指针
我在项目中遇到过一个问题:用 OpenCV 读图后直接传给深度学习模型,结果模型报错。查了半天,发现 Mat 默认是 H×W×C 的 BGR 格式,而模型要的是 RGB。这就是典型的通道顺序没搞对。
import cv2
import numpy as np
# 创建一个 300x300 的红色图像(BGR 格式)
img = np.zeros((300, 300, 3), dtype=np.uint8)
img[:, :] = (0, 0, 255) # BGR 顺序,红色是 (0,0,255)
# 查看 Mat 属性
print(f"形状: {img.shape}") # (300, 300, 3)
print(f"通道数: {img.shape[2]}") # 3
print(f"数据类型: {img.dtype}") # uint8
避坑指南:OpenCV 的彩色图像通道顺序是 BGR,不是 RGB。用 matplotlib 显示时一定要转换,否则颜色会乱。我曾经因为这个被测试同事追着问了一下午……
4.3 图像读写与显示
读写图像是基本功,但工业场景下有些细节要注意。
4.3.1 读取图像
import cv2
# 读取图像(默认彩色模式)
img = cv2.imread('product.jpg')
# 读取为灰度图
img_gray = cv2.imread('product.jpg', cv2.IMREAD_GRAYSCALE)
# 读取为原始格式(包含 alpha 通道)
img_unchanged = cv2.imread('product.jpg', cv2.IMREAD_UNCHANGED)
经验之谈:工业相机拍出来的图,很多是 16 位深度或 TIFF 格式。用 cv2.IMREAD_UNCHANGED 可以保留原始位深,避免精度丢失。
4.3.2 显示图像
调试时经常要看图。OpenCV 的 imshow 很方便,但有个坑——必须配合 waitKey 才能正常显示。
cv2.imshow('Defect Detection', img)
cv2.waitKey(0) # 等待按键
cv2.destroyAllWindows()
为什么非要加 waitKey?因为 imshow 只是把图像推送到窗口,真正绘制需要事件循环。不加的话,窗口一闪就没了。
4.3.3 保存图像
# 保存为 PNG(无损压缩)
cv2.imwrite('result.png', img)
# 保存为 JPEG(可调质量)
cv2.imwrite('result.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 95])
注意:保存路径不能有中文!OpenCV 底层用的是 C++ 的 imwrite,对 Unicode 支持不好。我曾经因为路径带中文,调试了半小时才发现是这个问题。
4.4 色彩空间转换
色彩空间转换是缺陷检测里最常用的操作之一。为什么?因为很多缺陷在 RGB 空间里不明显,但转到 HSV 或灰度空间就一目了然。
4.4.1 三种常用色彩空间
| 色彩空间 | 特点 | 工业应用场景 |
|---|---|---|
| RGB | 红绿蓝三通道,与人眼感知不完全一致 | 通用显示、深度学习输入 |
| HSV | 色调(H)、饱和度(S)、明度(V),对光照变化鲁棒 | 颜色缺陷检测、物体分割 |
| Gray | 单通道灰度,计算量小 | 边缘检测、模板匹配、OCR |
4.4.2 转换实战
import cv2
img = cv2.imread('defect_sample.jpg')
# BGR 转灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# BGR 转 HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# HSV 转 BGR(还原)
bgr_back = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
核心要点:
- OpenCV 的默认彩色空间是 BGR,不是 RGB
- HSV 的 H 范围是 0~179,S 和 V 是 0~255
- 灰度图只有一个通道,不能直接转回彩色
我举个实际例子。有一次做 PCB 焊点缺陷检测,焊点颜色偏暗,在 RGB 空间里很难分割。转到 HSV 后,用 H 通道就能轻松把焊点区域提取出来。这就是色彩空间转换的价值。
4.4.3 用 HSV 做颜色过滤
工业视觉里经常要提取特定颜色的区域。HSV 比 RGB 好使,因为 H 通道对光照变化不敏感。
import cv2
import numpy as np
img = cv2.imread('color_target.jpg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# 定义红色范围(H 在 0~10 和 160~180)
lower_red1 = np.array([0, 50, 50])
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([160, 50, 50])
upper_red2 = np.array([180, 255, 255])
# 生成掩码
mask1 = cv2.inRange(hsv, lower_red1, upper_red1)
mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
mask = cv2.bitwise_or(mask1, mask2)
# 提取红色区域
result = cv2.bitwise_and(img, img, mask=mask)
经验分享:红色在 HSV 里比较特殊,它跨越了 0° 和 180° 两个区间。所以过滤红色时要用两个范围做或运算。我第一次做红色提取时只设了一个范围,结果漏了一半……
4.5 本章知识体系
下面这张图把本章的核心知识点串起来了。你可以把它当作一个快速索引。
4.6 常见问题与避坑
- 图像读取失败:检查路径、文件权限、格式支持。OpenCV 不支持 GIF 和部分 RAW 格式。
- 显示窗口卡死:忘了加
waitKey(),或者循环里没调waitKey(1)。 - 颜色不对:99% 是 BGR 和 RGB 搞混了。用
cv2.cvtColor转一下就好。 - HSV 范围不对:OpenCV 的 H 范围是 0~179,不是 0~360。很多新手在这里翻车。
我曾经踩过的坑:有一次在产线上部署算法,发现同一张图在不同电脑上颜色不一样。查了半天,原来是其中一台电脑的 OpenCV 是用源码编译的,色彩空间默认值不一样。从那以后,我所有项目都固定 OpenCV 版本,用 pip 安装,避免编译参数差异。
好了,这一章的内容就到这里。环境搭好了,Mat 理解了,读写显示会了,色彩空间转换也练了——接下来就可以开始真正的工业视觉算法实战了。