4、Python与OpenCV入门:环境配置、图像读取与显示、基本图像处理(缩放、旋转、滤波)

好,咱们进入实战环节。说实话,很多同学学计算机视觉,第一关就卡在环境配置上。我当年也踩过不少坑,装个OpenCV折腾一下午,结果发现是Python版本不对。今天咱们就把这事彻底捋清楚。

4.1 环境配置:别让工具卡住你

Python环境配置,我个人习惯用Anaconda。为什么?因为它自带了很多科学计算库,省心。你想想看,要是用原生Python,装个numpy都得手动编译,多麻烦。

推荐配置:

  • Python 3.8 或 3.9(别追最新版,有些库还没适配)
  • OpenCV 4.5+(4.x系列稳定好用)
  • numpy 1.21+(OpenCV底层依赖它)

安装命令很简单,一行搞定:

pip install opencv-python==4.5.5.64
pip install numpy==1.21.6

我曾经踩过的坑: 直接用 pip install opencv-python 装最新版,结果跟系统里的Python 3.10不兼容,报了一堆DLL错误。后来老老实实降级到3.8,世界清净了。

验证安装是否成功,打开Python交互环境:

import cv2
print(cv2.__version__)
# 输出类似 '4.5.5' 就对了

嗯,这里要注意:如果你用的是PyCharm或者VS Code,记得检查解释器路径。我见过太多人装好了库,但IDE用的是另一个Python环境,结果死活import失败。

4.2 图像读取与显示:第一行代码

好,环境配好了,咱们来写第一行正经的视觉代码。说白了,就是让计算机「看」到一张图片。

import cv2

# 读取图像
img = cv2.imread('lena.jpg')

# 显示图像
cv2.imshow('My First Image', img)
cv2.waitKey(0)  # 等待按键
cv2.destroyAllWindows()

这段代码看起来简单,但有几个细节我得跟你聊聊:

  • imread() 返回的是一个numpy数组。对,就是那个多维数组。彩色图是三维的(高、宽、通道数),灰度图是二维的。
  • imshow() 会弹出一个窗口。窗口名字别用中文,有些系统会乱码。
  • waitKey(0) 这行不能省。没有它,窗口一闪就没了。0表示无限等待,直到你按键盘。

小技巧: 我习惯在读取图片后先检查一下:if img is None: print('图片没找到')。路径写错是家常便饭,加个检查能省不少调试时间。

你可能会问:为什么OpenCV读出来的颜色怪怪的?

问得好。OpenCV默认用BGR顺序,而我们平时用的RGB是反的。所以如果你用matplotlib显示,会发现蓝色和红色互换。解决办法很简单:

img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

我在项目中遇到过好几次这种「色差」问题,尤其是做图像标注的时候,标注的颜色跟显示的不一样,排查了半天才发现是通道顺序搞反了。

4.3 基本图像处理:缩放、旋转、滤波

好,现在咱们来动真格的。图像处理说白了就是操作那个numpy数组。你想想看,一张图就是一堆数字,缩放就是插值,旋转就是矩阵变换,滤波就是卷积。

4.3.1 缩放图像

# 方法一:指定目标尺寸
resized = cv2.resize(img, (300, 200))

# 方法二:按比例缩放
scale_percent = 50  # 缩小到50%
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
resized = cv2.resize(img, (width, height))

这里有个关键参数:插值方法。默认是双线性插值,效果还行。但如果你要缩小图片,用 cv2.INTER_AREA 效果更好,不会出现锯齿。放大图片用 cv2.INTER_CUBICcv2.INTER_LANCZOS4,更平滑。

经验之谈: 我处理监控视频时,经常需要把1080p缩放到640x360。用 INTER_AREA 处理行人图像,边缘保留得最好,后续检测准确率能高2-3个百分点。

4.3.2 旋转图像

旋转稍微复杂一点,需要先算旋转矩阵:

# 获取图像尺寸
h, w = img.shape[:2]

# 计算旋转矩阵(中心点,角度,缩放)
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, 45, 1.0)  # 顺时针45度

# 执行仿射变换
rotated = cv2.warpAffine(img, M, (w, h))

你可能会发现旋转后图像被裁剪了。为什么会这样?因为旋转后四个角跑到了外面。解决办法是调整输出尺寸:

# 计算旋转后的新边界
cos = abs(M[0, 0])
sin = abs(M[0, 1])
new_w = int(h * sin + w * cos)
new_h = int(h * cos + w * sin)

# 调整旋转矩阵的平移部分
M[0, 2] += (new_w / 2) - center[0]
M[1, 2] += (new_h / 2) - center[1]

rotated = cv2.warpAffine(img, M, (new_w, new_h))

嗯,这段代码看起来有点绕。说白了就是:先算新画布多大,再把图片居中放进去。我在做数据增强时经常用这个,旋转后的行人样本能大大提升模型的泛化能力。

4.3.3 图像滤波

滤波是图像处理的基石。说白了就是用一个「小窗口」在图像上滑动,每个像素取周围像素的加权平均。

均值滤波:

blurred = cv2.blur(img, (5, 5))  # 5x5的核

高斯滤波:

gaussian = cv2.GaussianBlur(img, (5, 5), 1.5)  # 标准差1.5

中值滤波:

median = cv2.medianBlur(img, 5)  # 5x5邻域
滤波类型 特点 适用场景
均值滤波 简单粗暴,但会模糊边缘 去除均匀噪声
高斯滤波 中心权重高,边缘保留稍好 通用去噪,行人检测前预处理
中值滤波 对椒盐噪声效果极佳 传感器噪声、扫描图像

我曾经犯过的错: 做行人姿态估计时,直接用均值滤波把图像糊成一团,结果关键点检测全偏了。后来换成高斯滤波,核大小设为3x3,既去除了噪点,又保留了关节点的边缘信息。记住:滤波核越大,图像越模糊,信息丢失越多。

最后,咱们把今天学的串起来,写个完整的示例:

import cv2

# 读取图像
img = cv2.imread('pedestrian.jpg')
if img is None:
    print('请检查图片路径')
    exit()

# 1. 缩放
resized = cv2.resize(img, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)

# 2. 旋转
h, w = resized.shape[:2]
M = cv2.getRotationMatrix2D((w//2, h//2), 10, 1.0)
rotated = cv2.warpAffine(resized, M, (w, h))

# 3. 高斯滤波
filtered = cv2.GaussianBlur(rotated, (3, 3), 1.0)

# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Processed', filtered)
cv2.waitKey(0)
cv2.destroyAllWindows()

好,今天的内容就到这。你想想看,从环境配置到图像读取,再到缩放、旋转、滤波,咱们已经能对图像做基本的操作了。下一章咱们会深入特征提取,为行人姿态估计打基础。记住:代码多敲几遍,踩过的坑才是你自己的经验。