图像基础与 OpenCV 起步
数字图像的本质
计算机视觉处理的对象是数字图像。数字图像本质上就是一个二维像素数组。
像素与灰度图
灰度图是最简单的数字图像——每个像素存储一个亮度值,范围 0(纯黑)到 255(纯白)。在 Python 中,一张宽 W、高 H 的灰度图就是一个形状为 (H, W) 的二维数组。
| |
uint8 的取值范围是 0-255——8 位(bit)恰好表示 2^8 = 256 个值。如果运算超出范围,会发生回绕(wrap around):255 + 1 不会变成 256,而是变成 0(像汽车里程表归零)。处理像素运算时推荐用 cv2.add()(自动饱和截断)或 np.clip()(手动限幅)来避免回绕。
彩色图像与 RGB
彩色图像的每个像素包含三个通道——R(红)、G(绿)、B(蓝),每个通道同样是 0-255。但 OpenCV 默认使用 BGR 排列,不是常见的 RGB。形状为 (H, W, 3):
| |
BGR 的由来:OpenCV 早期开发时,相机厂商的 byte 序约定普遍是 BGR,OpenCV 沿用了这一约定。如果直接用 Matplotlib 的
imshow显示 OpenCV 图像,颜色会反掉(R↔B),因为 Matplotlib 按 RGB 解释通道。修正方法:先转换颜色空间img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)。
图1 - 数字图像(以彩色图为例)的数据结构:
flowchart TD
IMG["彩色图像<br/>shape: (H, W, 3)"] --> R["R 通道<br/>0-255 红色分量"]
IMG --> G["G 通道<br/>0-255 绿色分量"]
IMG --> B["B 通道<br/>0-255 蓝色分量"]
R --> PIXEL["单个像素<br/>[B, G, R]<br/>如 [0, 0, 255] = 纯红"]
G --> PIXEL
B --> PIXEL
classDef img fill:#9C27B0,color:#fff
classDef ch fill:#2196F3,color:#fff
classDef px fill:#f44336,color:#fff
class IMG img
class R,G,B ch
class PIXEL px动手试试 RGB/BGR 的区别——拖动滑块看看 [B, G, R] 数组如何对应颜色:
分辨率与位深度
- 分辨率:图像的宽×高像素数,决定了图像的细节密度
- 位深度:每个通道用多少比特表示。
uint8是 8 位(0-255),也有 16 位或 32 位浮点图
OpenCV 简介
OpenCV(Open Source Computer Vision Library)是目前最广泛使用的计算机视觉库,支持 C++、Python、Java 等语言。核心模块包括:
- core:基础数据结构(Mat、Point、Rect)
- imgproc:图像处理(滤波、几何变换、颜色空间)
- highgui:GUI 交互(显示窗口、滑动条)
- videoio:视频读写
Python 安装后直接 import cv2 即可使用。OpenCV 的 Python 接口是对 C++ 的封装,函数名和参数基本一致。
基本操作
读写与显示图像
| |
imread 返回 None 时说明文件路径不对或格式不支持——务必检查返回值。
图像属性
| |
ROI 裁剪
NumPy 切片直接取图像子区域——这是最常用的操作:
| |
注意切片的顺序:[行范围, 列范围],对应 [y1:y2, x1:x2]。
新手最容易犯的错误:
img[y1:y2, x1:x2]第一个维度是行范围(Y 方向),第二个维度是列范围(X 方向)——千万别写反成img[x1:x2, y1:y2]!
图3 - OpenCV 图像坐标系约定:
flowchart TD
NOTE["OpenCV 图像坐标系:<br/>原点在左上角<br/>rows = Y (向下增长)<br/>cols = X (向右增长)<br/>img[y, x] 不是 img[x, y]"]
classDef note fill:#f44336,color:#fff
class NOTE note通道分离与合并
| |
split 会创建新数组,频繁调用有额外开销。可以用 NumPy 切片直接操作通道:
| |
基础图像处理
颜色空间转换
| |
HSV 颜色空间在做颜色范围筛选时很常用——H 通道表示色相,S 表示饱和度,V 表示明度。
几何变换
| |
图像滤波
滤波用于降噪或平滑图像。核心参数是核大小(kernel size)——核越大,平滑效果越强。
直觉上,滤波相当于用邻域像素重新计算每个像素的值:
- 均值滤波(
blur):对核内所有像素做算术平均——速度最快,但会让边缘模糊 - 高斯滤波(
GaussianBlur):加权平均,中心像素权重最大,越靠近中心权重越大,越远越小,权重分布服从高斯分布(钟形曲线)——比均值滤波更能保留边缘 - 中值滤波(
medianBlur):取核内像素的中位数,对椒盐噪声(随机黑白点)特别有效——因为中位数能忽略极端值
| |
卷积核滑过图像的过程用动画看最清楚——每个位置取 3×3 邻域做加权平均:
高斯滤波的第三个参数是标准差——传 0 让函数自动计算。
阈值处理
阈值处理把灰度图转成二值图(只有黑和白):
| |
光照不均的图像适合用自适应阈值,它根据每个像素邻域计算局部阈值。
图2 - OpenCV 常见图像处理流水线(从读取到轮廓提取):
flowchart TD
A["imread<br/>读取图像"] --> B["cvtColor<br/>BGR→GRAY"]
B --> C["GaussianBlur<br/>降噪"]
C --> D["threshold<br/>二值化"]
D --> E["Canny<br/>边缘检测"]
E --> F["findContours<br/>轮廓提取"]
classDef io fill:#2196F3,color:#fff
classDef proc fill:#9C27B0,color:#fff
class A,F io
class B,C,D,E proc边缘检测
Canny 边缘检测是目前最常用的边缘检测算法:
| |
Canny 边缘检测分为两步:先用 Sobel 算子计算水平和垂直方向的梯度,得到每个像素的梯度幅值和方向;然后通过**滞后阈值(hysteresis thresholding)**来判断——
两个阈值参数:低于 threshold1 的像素被丢弃(非边缘),高于 threshold2 的确定为边缘,介于两者之间的只在与强边缘相连时保留。一般 threshold2 取 threshold1 的 2-3 倍。