第1章:OpenCV基础——图像读取与显示、色彩空间转换、图像滤波、边缘检测(Canny)、形态学操作

各位同学,欢迎来到《仿生眼目标检测与跟踪系统实战》的第一章。我是你们这门课的主讲,一个在计算机视觉和嵌入式系统里摸爬滚打了十几年的老兵。今天咱们不聊虚的,直接上手最核心的东西——OpenCV。

你想想看,仿生眼要能“看见”世界,第一步是什么?就是把图像数据从传感器里读出来,然后显示在屏幕上。这一步看似简单,但坑不少。我刚开始做项目时,就因为图像读取的格式没搞对,白白浪费了两天时间排查。嗯,咱们今天就把这些基础打牢。

本章核心知识点一览:

  • 图像的读取与显示(imread, imshow)
  • 色彩空间转换(BGR ↔ Gray ↔ HSV)
  • 图像滤波(均值、高斯、中值)
  • 边缘检测(Canny算子)
  • 形态学操作(腐蚀、膨胀、开闭运算)
OpenCV图像处理基础 图像读取与显示 色彩空间转换 图像滤波 imread() imshow() BGR ↔ Gray BGR ↔ HSV 均值/高斯/中值 Canny边缘检测 形态学操作 腐蚀 · 膨胀 · 开运算 · 闭运算

1.1 图像读取与显示——别小看这第一步

在OpenCV里,读取图像用的是cv2.imread()。这个函数有两个参数:文件路径和读取模式。我个人习惯用cv2.IMREAD_COLOR,也就是默认的彩色模式。但要注意,OpenCV读进来的颜色顺序是BGR,不是RGB!

为什么?因为早期摄像头硬件就是这么输出的。我在做仿生眼项目时,第一次把图像显示出来,发现人脸是蓝的,吓我一跳。后来才反应过来,是通道顺序搞反了。

import cv2

# 读取图像
img = cv2.imread('test.jpg', cv2.IMREAD_COLOR)

# 显示图像
cv2.imshow('仿生眼 - 原始图像', img)

# 等待按键,然后关闭窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

💡 我的小技巧:在嵌入式设备上,waitKey(0)会阻塞主线程。如果你在做实时视频流,记得改成waitKey(1),这样每1毫秒刷新一次,不会卡死。

1.2 色彩空间转换——让机器看懂颜色

人眼看到的是红绿蓝,但机器不一定这么看。比如做颜色跟踪时,HSV空间比BGR好用得多。为什么?因为HSV把色调(H)、饱和度(S)、明度(V)分开了,光照变化对H通道影响小。

我在做仿生眼的目标跟踪时,就吃过这个亏。一开始在BGR空间里做颜色阈值,结果换个光照环境就全废了。后来换成HSV,稳得很。

# BGR 转灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# BGR 转 HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

⚠️ 注意:OpenCV中HSV的H范围是0-179,不是0-255!S和V是0-255。我见过不少新手用inRange()时,H值设到200多,结果什么都检测不到。嗯,这个坑我踩过。

1.3 图像滤波——去噪是预处理的关键

摄像头采集的图像,多少都有噪声。尤其是仿生眼这种移动平台,运动模糊和传感器噪声是家常便饭。滤波就是把这些噪声干掉,保留有用的边缘信息。

常用的三种滤波:

  • 均值滤波:简单粗暴,但会模糊边缘
  • 高斯滤波:权重按高斯分布,效果更自然
  • 中值滤波:对椒盐噪声特别有效,我经常用
# 均值滤波
blur = cv2.blur(img, (5, 5))

# 高斯滤波
gaussian = cv2.GaussianBlur(img, (5, 5), 1.5)

# 中值滤波
median = cv2.medianBlur(img, 5)

💡 实战经验:在仿生眼项目中,我一般先用高斯滤波做一次轻度去噪,再用中值滤波处理掉那些孤立的噪点。两个滤波器配合使用,效果比单个好很多。

1.4 边缘检测(Canny)——找到物体的轮廓

边缘检测是目标检测的基石。Canny算子是目前最经典的,它分四步走:去噪、算梯度、非极大值抑制、双阈值检测。

双阈值是Canny的精髓。高阈值决定哪些是强边缘,低阈值决定哪些是弱边缘。我一般设高阈值100,低阈值50。但具体数值要看你的图像,没有万能参数。

# 先转灰度,再Canny
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 100)

cv2.imshow('Canny边缘', edges)

⚠️ 避坑指南:我曾经在仿生眼项目中,直接对彩色图做Canny,结果边缘乱七八糟。记住,Canny输入必须是单通道灰度图。另外,阈值设得太低会检测出大量噪声,设得太高又会丢失真实边缘。建议先用cv2.createTrackbar()动态调参。

1.5 形态学操作——修修补补的好工具

边缘检测完了,图像里可能还有断点、空洞、毛刺。这时候形态学操作就派上用场了。说白了,就是用一个小结构元素(比如3x3的矩形)去“扫描”图像,做腐蚀或膨胀。

  • 腐蚀:让白色区域缩小,去掉小噪点
  • 膨胀:让白色区域扩大,连接断开的边缘
  • 开运算:先腐蚀后膨胀,去噪点
  • 闭运算:先膨胀后腐蚀,填小孔
# 定义结构元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))

# 腐蚀
erosion = cv2.erode(img, kernel, iterations=1)

# 膨胀
dilation = cv2.dilate(img, kernel, iterations=1)

# 开运算
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)

# 闭运算
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)

💡 我的习惯:在仿生眼的目标跟踪中,我通常先用开运算去掉背景噪点,再用闭运算把目标内部的空洞填上。这样得到的二值图,后续做轮廓提取就干净多了。

本章小结

好了,这一章的内容就这些。图像读取与显示是基本功,色彩空间转换让你更灵活地处理颜色,滤波帮你降噪,Canny帮你找边缘,形态学操作帮你修图。这五个技能,是仿生眼项目里天天都要用的。

我个人建议,你拿到代码后,不要光看,一定要动手跑一遍。把参数改一改,看看效果有什么变化。只有亲手试过,才能真正理解每个函数的作用。

下一章,我们会把这些基础操作串起来,做一个简单的目标检测Demo。到时候你会发现,原来这些看似零散的知识点,组合起来威力这么大。


专注资料整理