OpenCV Python 入门必看教程 – wiki基地


OpenCV Python 入门必看教程:从零开始掌握计算机视觉利器

引言

在当今数字化的世界里,图像和视频无处不在。从手机相机的美颜滤镜到自动驾驶汽车的感知系统,从医学影像分析到工业产品的缺陷检测,计算机视觉技术正以前所未有的速度发展并深入影响着我们的生活。而 OpenCV(Open Source Computer Vision Library)作为目前功能最强大、应用最广泛的开源计算机视觉库,无疑是进入这个迷人领域的最佳起点。

OpenCV 最初由 Intel 开发,使用 C++ 编写,但它提供了丰富的 Python 接口,使得计算机视觉的开发变得异常便捷。Python 简洁的语法、强大的生态系统(尤其是与 NumPy、Matplotlib 等科学计算库的结合)与 OpenCV 高效的底层实现完美结合,成为了许多研究人员和工程师的首选工具。

本篇教程旨在成为你迈向 OpenCV Python 世界的基石。我们将从最基础的概念和安装开始,逐步深入到图像和视频的基本操作、常用处理技术,并通过代码示例让你亲手实践。无需深厚的计算机视觉理论背景,只要你具备一定的 Python 编程基础,就可以跟随本教程一步步掌握 OpenCV 的核心用法。

准备好了吗?让我们一起开启 OpenCV Python 的学习之旅吧!

第一部分:准备工作

1. 了解基础概念

在深入代码之前,我们需要了解一些计算机视觉和图像处理的基础概念:

  • 数字图像 (Digital Image): 数字图像是由离散的像素点组成的二维矩阵。每个像素点代表图像中的一个微小区域,并包含了该区域的颜色信息。
  • 像素 (Pixel): 图像的最小单元。在灰度图像中,每个像素通常用一个 0-255 的整数表示亮度(0为黑色,255为白色);在彩色图像中,每个像素通常由多个通道(如红、绿、蓝)的数值组成。
  • 颜色空间 (Color Space): 描述颜色如何表示的方式。常见的颜色空间有:
    • BGR: OpenCV 默认使用的颜色空间,通道顺序是蓝(Blue)、绿(Green)、红(Red)。这与我们更熟悉的 RGB 顺序不同,需要注意。
    • 灰度 (Grayscale): 只使用一个通道表示亮度。
    • HSV: 色相(Hue)、饱和度(Saturation)、亮度(Value)。常用于颜色分割。
  • 图像的表示: 在 Python 中,OpenCV 图像通常以 NumPy 数组的形式存储。这使得我们可以利用 NumPy 强大的数组操作能力对图像进行处理。一个彩色图像通常是一个形状为 (高度, 宽度, 通道数) 的 NumPy 数组,数据类型通常是 uint8(无符号8位整数,范围0-255)。灰度图像则是 (高度, 宽度) 的数组。

2. 安装 OpenCV Python

安装 OpenCV Python 非常简单,只需要使用 Python 的包管理器 pip

打开你的终端或命令提示符,输入以下命令:

bash
pip install opencv-python

这条命令会安装 OpenCV 的核心模块。如果你还需要一些额外的、非免费或受专利限制的算法(例如 SIFT、SURF 等),可以安装 opencv-contrib-python

bash
pip install opencv-contrib-python

对于初学者,安装 opencv-python 通常就足够了。

安装完成后,你可以通过在 Python 解释器中导入 cv2 库来验证安装是否成功:

python
import cv2
print(cv2.__version__)

如果输出了 OpenCV 的版本号,说明安装成功。

第二部分:图像的基本操作

3. 读取、显示和保存图像

这是使用 OpenCV 进行图像处理的第一步。

3.1 读取图像

使用 cv2.imread() 函数读取图像。

“`python
import cv2
import numpy as np

指定图像文件路径

请替换为你的图像文件路径

image_path = ‘path/to/your/image.jpg’

读取图像

参数1: 图像文件路径

参数2: 读取模式

cv2.IMREAD_COLOR (或 1): 读取彩色图像 (默认)

cv2.IMREAD_GRAYSCALE (或 0): 读取灰度图像

cv2.IMREAD_UNCHANGED (或 -1): 读取包含 Alpha 通道的图像

img = cv2.imread(image_path, cv2.IMREAD_COLOR)

检查图像是否成功读取

if img is None:
print(f”错误: 无法读取图像文件 {image_path}”)
else:
print(f”成功读取图像,类型: {type(img)}”)
print(f”图像形状 (高, 宽, 通道): {img.shape}”)
print(f”图像尺寸 (像素总数): {img.size}”)
print(f”图像数据类型: {img.dtype}”)
“`

注意: cv2.imread() 读取彩色图像时,默认是 BGR 顺序,而不是更常见的 RGB。

3.2 显示图像

使用 cv2.imshow() 函数在窗口中显示图像。显示后需要配合 cv2.waitKey()cv2.destroyAllWindows() 来控制窗口的持续显示和关闭。

“`python

假设 img 已经通过 cv2.imread 成功读取

if img is not None:
# 创建一个窗口并显示图像
# 参数1: 窗口名称 (字符串)
# 参数2: 要显示的图像 (NumPy 数组)
cv2.imshow(‘Original Image’, img)

# 等待按键
# 参数0 表示无限等待,直到任意键按下
# 参数大于0 表示等待指定毫秒数
print("按下任意键关闭窗口...")
cv2.waitKey(0) # 窗口会一直显示直到有键按下

# 销毁所有 OpenCV 创建的窗口
cv2.destroyAllWindows()

“`

  • cv2.waitKey(0) 是必需的,它让程序暂停并等待按键事件。如果没有它,窗口会一闪而过,因为程序会立即执行下一行代码并结束。
  • cv2.destroyAllWindows() 会关闭所有由 OpenCV 创建的窗口。如果你只希望关闭特定窗口,可以使用 cv2.destroyWindow('窗口名称')

3.3 保存图像

使用 cv2.imwrite() 函数将图像保存到文件。

“`python

假设 img 已经通过 cv2.imread 成功读取或经过处理

if img is not None:
# 指定保存路径和文件名
output_path = ‘path/to/save/processed_image.png’ # 可以保存为多种格式,如 .jpg, .png, .bmp 等

# 保存图像
# 参数1: 保存文件路径
# 参数2: 要保存的图像 (NumPy 数组)
success = cv2.imwrite(output_path, img)

if success:
    print(f"图像成功保存到 {output_path}")
else:
    print(f"错误: 无法保存图像到 {output_path}")

“`

4. 获取图像属性和像素操作

图像作为 NumPy 数组,可以很方便地获取其属性和进行像素级别的操作。

“`python

假设 img 已经通过 cv2.imread 成功读取

if img is not None:
# 图像属性
height, width, channels = img.shape # 对于灰度图,channels 会是 1 或直接没有第三个维度
print(f”图像高度: {height} 像素”)
print(f”图像宽度: {width} 像素”)
print(f”图像通道数: {channels} (对于灰度图可能是 1 或没有第三个维度)”)

# 访问单个像素值 (彩色图像)
# 访问第 10 行,第 50 列的像素值 (BGR 顺序)
px = img[10, 50]
print(f"像素 (10, 50) 的 BGR 值: {px}")

# 访问单个像素值 (灰度图像,需要先转换为灰度)
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
px_gray = gray_img[10, 50]
print(f"灰度图像像素 (10, 50) 的值: {px_gray}")

# 修改单个像素值 (例如,将 (10, 50) 处的像素改为蓝色 - BGR 为 (255, 0, 0))
# img[10, 50] = [255, 0, 0] # 直接修改原图,会影响后续显示或保存

# 访问像素区域 (ROI - Region of Interest)
# 裁剪图像的一部分,例如从第 100 行到第 200 行,从第 150 列到第 300 列
roi = img[100:201, 150:301] # 注意 NumPy 切片的结束索引是独占的,所以要 +1
print(f"ROI 区域形状: {roi.shape}")

# 显示 ROI 区域
cv2.imshow('ROI', roi)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 修改像素区域 (例如,将 ROI 区域变为绿色 - BGR 为 (0, 255, 0))
# img[100:201, 150:301] = [0, 255, 0]
# cv2.imshow('Image with Modified ROI', img)
# cv2.waitKey(0)
# cv2.destroyAllWindows()

“`

通过 NumPy 的切片和索引功能,我们可以高效地访问和修改图像的任意像素或区域。

第三部分:视频的基本操作

OpenCV 不仅能处理静态图像,也能轻松处理视频流,无论是本地视频文件还是摄像头输入。

5. 读取和显示视频

5.1 从文件读取视频

使用 cv2.VideoCapture() 创建一个视频捕获对象。

“`python
import cv2

指定视频文件路径

请替换为你的视频文件路径

video_path = ‘path/to/your/video.mp4’

创建视频捕获对象

cap = cv2.VideoCapture(video_path)

检查视频是否成功打开

if not cap.isOpened():
print(f”错误: 无法打开视频文件 {video_path}”)
else:
# 获取视频的一些属性
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
fps = cap.get(cv2.CAP_PROP_FPS)
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

print(f"视频总帧数: {frame_count}")
print(f"视频帧率 (FPS): {fps}")
print(f"视频分辨率: {frame_width}x{frame_height}")

# 逐帧读取和显示视频
while True:
    # 读取一帧
    # ret 是布尔值,表示是否成功读取帧
    # frame 是读取到的帧 (NumPy 数组)
    ret, frame = cap.read()

    # 如果未成功读取帧 (例如,视频结束),则退出循环
    if not ret:
        print("视频播放结束")
        break

    # 在窗口中显示当前帧
    cv2.imshow('Video Frame', frame)

    # 设置延迟时间,以控制视频播放速度
    # delay = int(1000 / fps) # 根据帧率计算每帧延迟 (毫秒)
    delay = 25 # 例如,固定延迟 25ms (约 40 FPS)

    # 等待按键,同时设置延迟
    # 如果在延迟时间内按下 'q' 键,则退出循环
    if cv2.waitKey(delay) & 0xFF == ord('q'):
        print("检测到 'q' 键,退出播放")
        break

# 释放视频捕获对象
cap.release()
# 销毁所有窗口
cv2.destroyAllWindows()

“`

5.2 从摄像头读取视频

cv2.VideoCapture() 的参数改为摄像头的索引即可。通常,0 代表你的默认摄像头。

“`python
import cv2

创建视频捕获对象 (0 表示默认摄像头)

cap = cv2.VideoCapture(0)

检查摄像头是否成功打开

if not cap.isOpened():
print(“错误: 无法打开摄像头”)
else:
print(“摄像头已打开,按下 ‘q’ 键退出”)
while True:
# 读取一帧
ret, frame = cap.read()

    if not ret:
        print("无法接收帧 (流结束?)。正在退出...")
        break

    # 可选: 对帧进行处理 (例如,转换为灰度)
    # gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 在窗口中显示当前帧
    cv2.imshow('Camera Feed', frame) # 或显示 gray_frame

    # 等待按键,设置延迟
    # 按下 'q' 键退出
    if cv2.waitKey(1) & 0xFF == ord('q'): # 通常摄像头每帧延迟设置较小,例如 1ms
        break

# 释放资源
cap.release()
cv2.destroyAllWindows()

“`

第四部分:图像处理基础

OpenCV 提供了大量的图像处理函数。这里我们介绍一些最常用和基础的。

6. 绘制图形和文本

可以在图像上绘制点、线、圆、矩形以及添加文本。这些函数通常需要指定颜色(BGR 格式)和线条粗细。

“`python
import cv2
import numpy as np

创建一个空白图像 (例如,黑色背景 500×500 像素)

使用 np.zeros 创建一个全0的数组,表示黑色

img = np.zeros((500, 500, 3), np.uint8) # 3通道,uint8类型

绘制一条直线

cv2.line(图像, 起点坐标(x, y), 终点坐标(x, y), 颜色(B,G,R), 线条粗细)

cv2.line(img, (0, 0), (500, 500), (255, 0, 0), 5) # 蓝色斜线

绘制一个矩形

cv2.rectangle(图像, 左上角坐标(x, y), 右下角坐标(x, y), 颜色(B,G,R), 线条粗细)

线条粗细为 -1 表示填充矩形

cv2.rectangle(img, (100, 100), (400, 400), (0, 255, 0), 3) # 绿色矩形框

绘制一个圆

cv2.circle(图像, 圆心坐标(x, y), 半径, 颜色(B,G,R), 线条粗细)

线条粗细为 -1 表示填充圆

cv2.circle(img, (250, 250), 100, (0, 0, 255), -1) # 红色填充圆

绘制一个椭圆 (相对复杂,了解即可)

cv2.ellipse(图像, 圆心, 长短轴长度(长,短), 旋转角度, 起始角度, 终止角度, 颜色, 线条粗细)

cv2.ellipse(img, (256, 256), (100, 50), 0, 0, 180, (255, 255, 0), -1) # 黄色半椭圆

绘制多边形 (相对复杂,了解即可)

需要定义顶点数组

pts = np.array([[10,5],[20,30],[70,20],[50,10]], np.int32)

pts = pts.reshape((-1,1,2))

cv2.polylines(img,[pts],True,(0,255,255)) # 黄色多边形

添加文本

cv2.putText(图像, 文本内容, 左下角坐标(x, y), 字体, 字号缩放比例, 颜色(B,G,R), 字体粗细, 线型)

font = cv2.FONT_HERSHEY_SIMPLEX # 选择一个字体
cv2.putText(img, ‘Hello OpenCV!’, (50, 50), font, 1.2, (255, 255, 255), 2, cv2.LINE_AA) # 白色文本

显示结果

cv2.imshow(‘Drawing Demo’, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
“`

7. 颜色空间转换

最常用的颜色空间转换是从 BGR 到灰度图或 HSV。

“`python
import cv2
import numpy as np

加载彩色图像

image_path = ‘path/to/your/color_image.jpg’ # 请替换为你的彩色图像路径
img_color = cv2.imread(image_path)

if img_color is not None:
# 转换为灰度图像
# cv2.cvtColor(输入图像, 转换类型)
img_gray = cv2.cvtColor(img_color, cv2.COLOR_BGR2GRAY)

# 转换为 HSV 图像
img_hsv = cv2.cvtColor(img_color, cv2.COLOR_BGR2HSV)

# 分割 HSV 图像的通道 (可选,用于分析)
h, s, v = cv2.split(img_hsv)

# 显示原始、灰度、HSV(只显示 Value 通道) 图像
cv2.imshow('Original Color', img_color)
cv2.imshow('Grayscale', img_gray)
cv2.imshow('HSV Value Channel', v) # 常用 Value 通道进行亮度分析

print(f"灰度图像形状: {img_gray.shape}") # (高, 宽)
print(f"HSV 图像形状: {img_hsv.shape}") # (高, 宽, 3)

cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print(f”错误: 无法读取图像文件 {image_path}”)
“`

颜色空间转换在图像处理中非常重要。例如,灰度图常用于边缘检测、特征匹配等对颜色不敏感的任务;HSV 空间则更适合进行基于颜色的物体检测。

8. 阈值分割 (Thresholding)

阈值处理是一种简单的图像分割技术,常用于将图像转换为二值图像(只有黑色和白色)。

8.1 简单阈值

将图像的每个像素值与一个阈值进行比较,根据比较结果确定输出像素值(通常是 0 或 255)。

“`python
import cv2
import numpy as np

加载灰度图像 (阈值处理通常在灰度图上进行)

image_path = ‘path/to/your/gray_image.jpg’ # 请替换为你的图像路径
img_gray = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

if img_gray is not None:
# 应用简单阈值
# cv2.threshold(输入灰度图像, 阈值, 最大值, 阈值类型)
# 返回值 ret 是使用的阈值,thresholded_img 是结果图像
# cv2.THRESH_BINARY: 大于阈值设为最大值,否则设为 0
# cv2.THRESH_BINARY_INV: 小于阈值设为最大值,否则设为 0 (反色)
# cv2.THRESH_TRUNC: 大于阈值设为阈值,否则不变
# cv2.THRESH_TOZERO: 大于阈值不变,否则设为 0
# cv2.THRESH_TOZERO_INV: 小于阈值不变,否则设为 0

ret, thresh_binary = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
ret, thresh_binary_inv = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh_trunc = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TRUNC)
ret, thresh_tozero = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO)
ret, thresh_tozero_inv = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO_INV)

# 显示结果
cv2.imshow('Original Gray', img_gray)
cv2.imshow('THRESH_BINARY', thresh_binary)
cv2.imshow('THRESH_BINARY_INV', thresh_binary_inv)
cv2.imshow('THRESH_TRUNC', thresh_trunc)
cv2.imshow('THRESH_TOZERO', thresh_tozero)
cv2.imshow('THRESH_TOZERO_INV', thresh_tozero_inv)

print(f"使用的阈值: {ret}")

cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print(f”错误: 无法读取图像文件 {image_path}”)
“`

简单阈值的缺点是需要手动选择一个合适的全局阈值,这对于光照不均匀的图像效果不好。

8.2 自适应阈值

自适应阈值根据图像的局部区域计算不同的阈值,从而更好地处理光照不均匀的情况。

“`python
import cv2
import numpy as np

加载灰度图像

image_path = ‘path/to/your/uneven_light_image.jpg’ # 尝试用光照不均匀的图片
img_gray = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

if img_gray is not None:
# 应用自适应阈值
# cv2.adaptiveThreshold(输入灰度图像, 最大值, 自适应方法, 阈值类型, 块大小, 常量C)
# 自适应方法:
# cv2.ADAPTIVE_THRESH_MEAN_C: 阈值等于邻近区域的平均值减去常数C
# cv2.ADAPTIVE_THRESH_GAUSSIAN_C: 阈值等于邻近区域的加权平均值(高斯权重)减去常数C
# 块大小 (blockSize): 用于计算阈值的邻近区域大小 (必须是奇数,如 3, 5, 7…)
# 常量C (C): 从平均值或加权平均值中减去的常数

thresh_mean = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
thresh_gaussian = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)

# 显示结果
cv2.imshow('Original Gray (Uneven Light)', img_gray)
cv2.imshow('Adaptive Threshold (Mean)', thresh_mean)
cv2.imshow('Adaptive Threshold (Gaussian)', thresh_gaussian)

cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print(f”错误: 无法读取图像文件 {image_path}”)
“`

自适应阈值在处理扫描文档等光照不均的场景时非常有效。

9. 图像滤波 (Smoothing / Blurring)

图像滤波常用于去除噪声或平滑图像。OpenCV 提供了多种滤波方法。

“`python
import cv2
import numpy as np

加载图像

image_path = ‘path/to/your/noisy_image.jpg’ # 尝试用带噪声的图片
img = cv2.imread(image_path)

if img is not None:
# 平均滤波 (cv2.blur)
# 简单的用一个矩形核覆盖区域像素的平均值替换中心像素值
# 参数: 图像,核大小 (width, height)
blur_avg = cv2.blur(img, (5, 5)) # 使用 5×5 的核

# 高斯滤波 (cv2.GaussianBlur)
# 使用高斯函数作为权重,距离中心越近的像素权重越高
# 参数: 图像,核大小 (width, height, 必须是奇数),sigmaX (X方向标准差,通常设为 0 让其根据核大小计算)
blur_gaussian = cv2.GaussianBlur(img, (5, 5), 0)

# 中值滤波 (cv2.medianBlur)
# 用核区域内像素的中值替换中心像素值,对于椒盐噪声效果很好
# 参数: 图像,核大小 (必须是大于 1 的奇数)
blur_median = cv2.medianBlur(img, 5) # 使用 5x5 的核

# 双边滤波 (cv2.bilateralFilter)
# 同时考虑空间距离和像素值差异,能在平滑噪声的同时保留边缘
# 参数: 图像,邻域直径,颜色空间标准差,坐标空间标准差
blur_bilateral = cv2.bilateralFilter(img, 9, 75, 75)

# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Average Blur (5x5)', blur_avg)
cv2.imshow('Gaussian Blur (5x5)', blur_gaussian)
cv2.imshow('Median Blur (5x5)', blur_median)
cv2.imshow('Bilateral Filter', blur_bilateral)


cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print(f”错误: 无法读取图像文件 {image_path}”)
“`

选择哪种滤波方法取决于图像中的噪声类型和处理需求。

10. 边缘检测 (Edge Detection)

边缘检测用于找出图像中像素值变化剧烈的区域,这些区域通常对应物体的轮廓。Canny 边缘检测器是其中最经典和常用的算法之一。

“`python
import cv2
import numpy as np

加载灰度图像 (Canny 通常在灰度图上进行)

image_path = ‘path/to/your/image_with_edges.jpg’ # 尝试用有清晰边缘的图片
img_gray = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

if img_gray is not None:
# 应用 Canny 边缘检测
# cv2.Canny(输入灰度图像, 低阈值, 高阈值)
# 高于高阈值的边缘像素被认为是“强边缘”
# 低于低阈值的边缘像素被抑制
# 在低阈值和高阈值之间的像素,如果与强边缘相连,则被认为是边缘
edges = cv2.Canny(img_gray, 100, 200) # 阈值需要根据图像内容调整

# 显示结果
cv2.imshow('Original Gray', img_gray)
cv2.imshow('Canny Edges', edges)

cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print(f”错误: 无法读取图像文件 {image_path}”)
“`

Canny 边缘检测涉及到多个步骤(高斯平滑、计算梯度、非极大值抑制、双阈值检测、边缘跟踪),但通过 cv2.Canny() 函数一步到位,非常方便。选择合适的低阈值和高阈值是关键。

第五部分:一个简单的应用示例

让我们结合前面学到的知识,实现一个简单的应用:实时检测摄像头画面中的蓝色物体并用矩形框标记出来。

这个示例将结合视频读取、颜色空间转换、颜色阈值分割和绘制图形。

“`python
import cv2
import numpy as np

打开默认摄像头

cap = cv2.VideoCapture(0)

if not cap.isOpened():
print(“错误: 无法打开摄像头”)
else:
print(“摄像头已打开,尝试检测蓝色物体,按下 ‘q’ 键退出”)

while True:
    # 读取一帧
    ret, frame = cap.read()

    if not ret:
        print("无法接收帧 (流结束?)。正在退出...")
        break

    # 镜像翻转画面,让操作更直观 (可选)
    frame = cv2.flip(frame, 1) # 参数 1 表示水平翻转

    # 1. 将图像从 BGR 转换为 HSV
    hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

    # 2. 定义蓝色的 HSV 范围
    # 蓝色在 HSV 空间的 H 通道通常在 100-124 左右,具体范围需要根据实际情况调整
    # H (色相): 0-179 (OpenCV 的 H 通道范围是 0-179,而不是 0-360)
    # S (饱和度): 0-255
    # V (亮度): 0-255
    # 这里的范围 (100, 50, 50) 到 (140, 255, 255) 是一个大致的蓝色范围
    lower_blue = np.array([100, 50, 50])
    upper_blue = np.array([140, 255, 255])

    # 3. 根据 HSV 范围创建掩码 (mask)
    # cv2.inRange() 函数会生成一个二值图像 (掩码),
    # 其中在指定 HSV 范围内的像素值为 255 (白色),否则为 0 (黑色)
    mask = cv2.inRange(hsv_frame, lower_blue, upper_blue)

    # 4. 查找掩码中的轮廓 (Contours)
    # 轮廓是一条连接所有连续的同颜色点(或强度)的曲线。
    # cv2.findContours() 函数用于从二值图像中查找轮廓
    # 参数1: 输入的二值图像 (通常是掩码)
    # 参数2: 轮廓检索模式 (这里使用 cv2.RETR_EXTERNAL 只检索最外层轮廓)
    # 参数3: 轮廓近似方法 (这里使用 cv2.CHAIN_APPROX_SIMPLE 压缩水平、垂直和对角线段,只保留端点)
    # 返回值 contours 是一个包含所有找到的轮廓的列表
    # 返回值 hierarchy 是轮廓之间的层级关系信息 (这里我们不需要)
    contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    # 5. 遍历找到的轮廓
    for contour in contours:
        # 过滤掉太小的轮廓 (可能是噪声)
        area = cv2.contourArea(contour)
        if area > 500: # 设置一个最小面积阈值,根据需要调整

            # 计算轮廓的最小外接矩形
            # cv2.boundingRect() 返回矩形的 (x, y, w, h),其中 (x, y) 是左上角坐标,w 是宽度,h 是高度
            x, y, w, h = cv2.boundingRect(contour)

            # 在原始帧上绘制矩形框
            # cv2.rectangle(图像, 左上角坐标, 右下角坐标, 颜色(B,G,R), 线条粗细)
            cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2) # 绘制蓝色矩形框

            # 可选: 在框上方添加文本说明
            cv2.putText(frame, 'Blue Object', (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)


    # 6. 显示原始帧和掩码图像
    cv2.imshow('Camera Feed', frame)
    cv2.imshow('Blue Mask', mask) # 显示掩码有助于理解颜色分割效果

    # 7. 等待按键,按下 'q' 键退出
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 8. 释放资源
cap.release()
cv2.destroyAllWindows()

“`

运行这段代码,举起一些蓝色的物体对着摄像头,你会看到它们被蓝色矩形框标记出来。通过修改 lower_blueupper_blue 的值,你可以尝试检测其他颜色的物体。

第六部分:下一步去哪里?

恭喜你!通过前面的学习,你已经掌握了 OpenCV Python 的基本操作和常用图像处理技术。这仅仅是计算机视觉世界的冰山一角。OpenCV 库包含了更多强大的功能,例如:

  • 形态学操作 (Morphological Operations): 腐蚀、膨胀、开运算、闭运算等,用于图像的二值化处理和形状分析。
  • 轮廓处理 (Contour Processing): 计算轮廓的周长、面积、中心矩、拟合形状等,用于物体检测和识别。
  • 特征检测与匹配 (Feature Detection and Matching): SIFT, SURF (非免费), ORB 等特征点提取算法,用于图像拼接、目标跟踪等。
  • 目标检测 (Object Detection): Haar 特征级联分类器(如人脸检测)、DNN 模块集成(运行深度学习模型如 YOLO, SSD 等)。
  • 视频分析 (Video Analysis): 光流法、背景减除等。
  • 相机标定与三维重建 (Camera Calibration and 3D Reconstruction): 用于消除相机畸变、测量真实世界尺寸、构建三维模型。

要继续深入学习,你可以:

  1. 阅读官方文档: OpenCV 官方网站提供了详细的 Python API 参考和大量教程。
  2. 参考在线教程和课程: Coursera, Udacity, Bilibili 等平台有许多优质的计算机视觉课程。
  3. 参与开源项目: 通过参与 OpenCV 或其他相关的开源项目,学习实际应用和最佳实践。
  4. 动手实践: 尝试用 OpenCV 解决实际问题,如制作简单的目标检测器、图像风格转换、文档扫描器等。
  5. 学习相关理论: 深入理解计算机视觉和图像处理的数学原理(线性代数、微积分、概率统计)和算法(卷积、傅里叶变换、滤波理论等)将帮助你更好地应用 OpenCV。

总结

本教程从 OpenCV Python 的安装讲起,详细介绍了图像和视频的读取、显示、保存、基本属性获取、像素操作,以及绘制图形、颜色空间转换、阈值处理、图像滤波和边缘检测等核心基础知识。通过一个简单的蓝色物体检测示例,我们将这些基础知识串联起来,展示了 OpenCV 在实际应用中的魅力。

掌握 OpenCV Python 是进入计算机视觉领域的敲门砖。希望本篇教程为你打下了坚实的基础,并激发你继续探索这个充满无限可能的领域。计算机视觉的未来已经到来,祝你在 OpenCV 的学习和应用之路上取得更多成果!


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部