学习 OpenCV Python:基础知识与核心概念详解 (约3000字)
引言:开启计算机视觉之门
在人工智能和数据科学浪潮席卷全球的今天,计算机视觉(Computer Vision, CV)作为其关键分支,正以前所未有的速度渗透到我们生活的方方面面,从人脸识别解锁手机,到自动驾驶汽车感知环境,再到医疗影像分析辅助诊断,其应用场景日益广泛。而要在 Python 这个强大的编程语言生态中驾驭计算机视觉,OpenCV (Open Source Computer Vision Library) 无疑是最核心、最流行、功能最全面的库之一。
OpenCV 是一个开源的、跨平台的计算机视觉和机器学习软件库,它包含了超过 2500 种优化的算法,涵盖了经典的计算机视觉任务和最先进的机器学习技术。结合 Python 语言的简洁性、易用性和丰富的第三方库支持,OpenCV Python 绑定(cv2
模块)成为了研究人员、开发者和爱好者进入计算机视觉领域的首选工具。
本文旨在为初学者和希望系统学习 OpenCV Python 的读者提供一份详尽的指南,深入探讨其基础知识和核心概念,助您稳步踏上计算机视觉的探索之旅。
一、 为什么选择 OpenCV 和 Python?
-
OpenCV 的优势:
- 功能强大: 提供了涵盖图像处理、视频分析、特征检测、目标识别、3D 重建、机器学习等众多领域的算法。
- 性能优越: 核心代码由 C/C++ 编写,并针对多种处理器架构(包括 Intel IPP 和 CUDA)进行了优化,运行效率高。
- 跨平台性: 支持 Windows, Linux, macOS, Android, iOS 等多种操作系统。
- 开源免费: 遵循 BSD 许可证,可免费用于学术和商业目的。
- 社区活跃: 拥有庞大的用户和开发者社区,文档丰富,遇到问题容易找到解决方案。
-
Python 的优势:
- 简洁易学: 语法清晰,代码可读性强,上手门槛相对较低。
- 生态丰富: 拥有 NumPy, SciPy, Matplotlib, Scikit-learn, TensorFlow, PyTorch 等强大的科学计算和机器学习库,可以与 OpenCV 无缝集成,实现复杂任务。
- 开发效率高: “胶水语言”特性使其能够快速整合不同模块,适合快速原型设计和迭代开发。
- 广泛应用: 在数据科学、Web 开发、自动化等领域均有广泛应用,学习 Python 具有普适性。
OpenCV 与 Python 的结合,可谓强强联手,既利用了 OpenCV 底层的高性能,又享受了 Python 的开发便利性,是进行计算机视觉项目开发的理想组合。
二、 环境搭建:安装 OpenCV Python 库
在开始之前,确保您的系统已经安装了 Python(推荐 3.6 或更高版本)和 pip 包管理器。
安装 OpenCV 的 Python 绑定通常非常简单,只需在终端或命令行中执行:
bash
pip install opencv-python
这将安装包含主要模块的核心包。然而,在实际开发中,我们往往还需要一些额外的算法,特别是涉及特征检测(如 SIFT, SURF)等专利算法(在新版本中可能受限或移至主库)以及其他贡献模块。为此,推荐安装 opencv-contrib-python
包,它包含了核心包以及所有的扩展模块:
bash
pip install opencv-contrib-python
注意: 请不要同时安装 opencv-python
和 opencv-contrib-python
,选择后者即可。安装 opencv-contrib-python
会自动包含 opencv-python
的内容。
为了验证安装是否成功,可以在 Python 解释器中尝试导入 cv2
模块:
python
import cv2
print(cv2.__version__)
如果成功打印出 OpenCV 的版本号,则表示安装完成。
三、 核心基石:图像在 OpenCV 中的表示 —— NumPy 数组
理解 OpenCV 如何处理图像至关重要。在 OpenCV Python 中,图像被表示为 NumPy 数组(numpy.ndarray
)。这是学习 OpenCV 的第一个,也是最重要的核心概念。
- 灰度图像: 一个二维 NumPy 数组,形状为
(height, width)
。数组中的每个元素代表一个像素的强度值,通常是uint8
类型(0-255),0 代表黑色,255 代表白色。 - 彩色图像: 一个三维 NumPy 数组,形状为
(height, width, channels)
。height
:图像的高度(像素行数)。width
:图像的宽度(像素列数)。channels
:颜色通道数。OpenCV 默认使用 BGR(蓝-绿-红)颜色顺序,而不是常见的 RGB。因此,一个彩色像素通常由 3 个uint8
值组成,分别代表蓝色、绿色和红色的强度。例如,img[y, x]
会返回一个包含[Blue, Green, Red]
值的列表或数组。
示例:
“`python
import numpy as np
import cv2
创建一个 100×200 的黑色灰度图像
gray_img = np.zeros((100, 200), dtype=np.uint8)
创建一个 100×200 的蓝色 BGR 彩色图像
B=255, G=0, R=0
blue_img = np.zeros((100, 200, 3), dtype=np.uint8)
blue_img[:, :] = [255, 0, 0] # 将所有像素设置为蓝色
访问图像属性
print(“灰度图像形状:”, gray_img.shape) # 输出: (100, 200)
print(“彩色图像形状:”, blue_img.shape) # 输出: (100, 200, 3)
print(“图像数据类型:”, blue_img.dtype) # 输出: uint8
print(“图像总像素数:”, blue_img.size) # 输出: 100 * 200 * 3 = 60000
“`
由于图像就是 NumPy 数组,这意味着你可以直接使用 NumPy 提供的所有强大的数组操作功能来处理图像,如切片、索引、算术运算、逻辑运算等,这极大地简化了图像处理任务。
四、 基础操作:图像的读写与显示
-
读取图像 (
cv2.imread
)cv2.imread(filepath, flags)
:从指定路径加载图像。filepath
:图像文件的路径。flags
:指定加载图像的颜色类型,常用:cv2.IMREAD_COLOR
(或 1):加载彩色图像(默认),忽略 Alpha 透明通道。cv2.IMREAD_GRAYSCALE
(或 0):以灰度模式加载图像。cv2.IMREAD_UNCHANGED
(或 -1):加载完整图像,包括 Alpha 通道(如果存在)。
“`python
img_color = cv2.imread(‘path/to/your/image.jpg’, cv2.IMREAD_COLOR)
img_gray = cv2.imread(‘path/to/your/image.jpg’, cv2.IMREAD_GRAYSCALE)if img_color is None:
print(“无法加载彩色图像”)
if img_gray is None:
print(“无法加载灰度图像”)
``
imread
**注意:** 如果图像路径错误或文件损坏,会返回
None`,务必进行检查。 -
显示图像 (
cv2.imshow
,cv2.waitKey
,cv2.destroyAllWindows
)cv2.imshow(window_name, image)
:在一个窗口中显示图像。window_name
:显示窗口的标题(字符串)。image
:要显示的 NumPy 数组图像。
cv2.waitKey(delay)
:等待键盘事件。这是 必须 的步骤,否则图像窗口可能一闪而过或无响应。delay
:等待时间(毫秒)。如果为 0,则无限期等待,直到用户按键。如果大于 0,则等待指定毫秒数。它返回按键的 ASCII 码,如果超时则返回 -1。常用于视频播放的帧率控制或等待用户交互。
cv2.destroyAllWindows()
:关闭所有由 OpenCV 创建的窗口。cv2.destroyWindow(window_name)
:关闭指定的窗口。
“`python
cv2.imshow(‘Color Image’, img_color)
cv2.imshow(‘Grayscale Image’, img_gray)print(“按任意键退出…”)
key = cv2.waitKey(0) # 等待用户按键
print(f”你按下的键的 ASCII 码是: {key}”)cv2.destroyAllWindows()
“` -
保存图像 (
cv2.imwrite
)cv2.imwrite(filename, image)
:将图像保存到文件。filename
:保存的文件名(包含扩展名,如.jpg
,.png
)。OpenCV 根据扩展名确定文件格式。image
:要保存的 NumPy 数组图像。
python
success = cv2.imwrite('output/gray_image.png', img_gray)
if success:
print("灰度图像保存成功!")
else:
print("灰度图像保存失败。")
五、 颜色空间转换 (cv2.cvtColor
)
虽然 OpenCV 默认使用 BGR,但很多其他库(如 Matplotlib)或应用场景(如颜色检测)可能需要 RGB、HSV、HLS 或灰度等不同的颜色空间。
cv2.cvtColor(src, code)
:转换图像的颜色空间。src
:源图像。code
:转换代码,例如:cv2.COLOR_BGR2GRAY
:BGR 到灰度。cv2.COLOR_BGR2RGB
:BGR 到 RGB。cv2.COLOR_BGR2HSV
:BGR 到 HSV。cv2.COLOR_RGB2BGR
:RGB 到 BGR。cv2.COLOR_GRAY2BGR
:灰度到 BGR(会复制灰度值到三个通道)。
“`python
img_rgb = cv2.cvtColor(img_color, cv2.COLOR_BGR2RGB)
img_hsv = cv2.cvtColor(img_color, cv2.COLOR_BGR2HSV)
注意:如果使用 Matplotlib 显示 OpenCV 加载的彩色图像,需要先转为 RGB
import matplotlib.pyplot as plt
plt.imshow(img_rgb)
plt.show()
“`
HSV (Hue, Saturation, Value) 颜色空间 对于基于颜色的对象分割特别有用,因为它将颜色信息(色调 H)与光照强度(饱和度 S,明度 V)分离开来。
六、 基本图像操作
-
访问和修改像素值: 直接使用 NumPy 索引。
“`python
# 获取 (y, x) 处的像素值 (假设是彩色图像)
(b, g, r) = img_color[100, 50]
print(f”Pixel at (50, 100) – B:{b}, G:{g}, R:{r}”)修改 (y, x) 处的像素值为白色
img_color[100, 50] = [255, 255, 255]
获取灰度图像 (y, x) 处的像素值
intensity = img_gray[100, 50]
print(f”Intensity at (50, 100): {intensity}”)修改灰度值
img_gray[100, 50] = 0 # 设置为黑色
``
[行(y), 列(x)]`。直接访问像素效率较低,应尽量使用 OpenCV 的内置函数或 NumPy 的向量化操作。
**注意:** 索引顺序是 -
获取图像属性: 已在第三节介绍 (
shape
,size
,dtype
)。 -
图像 ROI (Region of Interest): 感兴趣区域,即图像的一个子区域。利用 NumPy 切片可以轻松获取 ROI。
“`python
# 选择从 (x1, y1) 到 (x2, y2) 的矩形区域
# 注意 NumPy 切片是 [y1:y2, x1:x2]
roi = img_color[100:200, 200:300] # 行从100到199,列从200到299
cv2.imshow(‘ROI’, roi)
cv2.waitKey(0)
cv2.destroyAllWindows()对 ROI 的修改会直接反映在原图上,因为 ROI 是原数组的一个视图 (View)
roi[:, :] = [0, 255, 0] # 将 ROI 区域变为绿色
cv2.imshow(‘Modified Original’, img_color)
cv2.waitKey(0)
cv2.destroyAllWindows()如果需要独立副本,使用 .copy()
roi_copy = img_color[100:200, 200:300].copy()
“` -
拆分和合并颜色通道 (
cv2.split
,cv2.merge
)
“`python
b, g, r = cv2.split(img_color) # 拆分为三个单通道灰度图像
cv2.imshow(‘Blue Channel’, b)
cv2.waitKey(0)merged_img = cv2.merge((b, g, r)) # 重新合并通道
cv2.imshow(‘Merged Image’, merged_img)
cv2.waitKey(0)
cv2.destroyAllWindows()可以将某个通道置零
img_no_blue = img_color.copy()
img_no_blue[:, :, 0] = 0 # 将蓝色通道设置为 0
cv2.imshow(‘No Blue’, img_no_blue)
cv2.waitKey(0)
cv2.destroyAllWindows()
“` -
图像缩放 (
cv2.resize
)cv2.resize(src, dsize, fx=0, fy=0, interpolation=cv2.INTER_LINEAR)
src
:输入图像。dsize
:目标尺寸(width, height)
。如果设置为None
,则尺寸由fx
和fy
决定。fx
,fy
:沿 x 轴和 y 轴的缩放比例。如果dsize
不是None
,则忽略这两个参数。interpolation
:插值方法,常用的有:cv2.INTER_NEAREST
:最近邻插值(速度快,效果差)。cv2.INTER_LINEAR
:双线性插值(默认,常用)。cv2.INTER_CUBIC
:双三次插值(效果更好,速度较慢)。cv2.INTER_AREA
:区域插值(用于缩小图像,效果好)。
“`python
height, width = img_color.shape[:2]按绝对尺寸缩放
resized_abs = cv2.resize(img_color, (int(width0.5), int(height0.5)), interpolation=cv2.INTER_AREA)
按比例缩放
resized_rel = cv2.resize(img_color, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)
cv2.imshow(‘Original’, img_color)
cv2.imshow(‘Resized Absolute’, resized_abs)
cv2.imshow(‘Resized Relative’, resized_rel)
cv2.waitKey(0)
cv2.destroyAllWindows()
“` -
图像旋转与平移(仿射变换
cv2.warpAffine
)
平移、旋转、缩放等几何变换可以通过仿射变换实现。这需要定义一个 2×3 的变换矩阵M
。- 平移:
python
rows, cols = img_color.shape[:2]
tx, ty = 100, 50 # 向右平移 100 像素,向下平移 50 像素
M_trans = np.float32([[1, 0, tx], [0, 1, ty]])
img_translated = cv2.warpAffine(img_color, M_trans, (cols, rows))
cv2.imshow('Translated', img_translated)
cv2.waitKey(0)
cv2.destroyAllWindows() - 旋转: OpenCV 提供了更方便的函数
cv2.getRotationMatrix2D
来生成旋转矩阵。
python
center = (cols // 2, rows // 2) # 旋转中心
angle = 45 # 旋转角度(逆时针为正)
scale = 1.0 # 缩放因子
M_rot = cv2.getRotationMatrix2D(center, angle, scale)
img_rotated = cv2.warpAffine(img_color, M_rot, (cols, rows))
cv2.imshow('Rotated', img_rotated)
cv2.waitKey(0)
cv2.destroyAllWindows()
- 平移:
-
在图像上绘制图形和文字
OpenCV 提供了绘制线段、矩形、圆形、椭圆、多边形和文字的功能。这些函数会直接修改输入的图像。cv2.line(img, pt1, pt2, color, thickness)
cv2.rectangle(img, pt1, pt2, color, thickness)
(pt1: 左上角, pt2: 右下角)cv2.circle(img, center, radius, color, thickness)
(thickness=-1 表示填充)cv2.putText(img, text, org, fontFace, fontScale, color, thickness, lineType)
“`python
draw_img = img_color.copy() # 创建副本进行绘制绘制绿色直线
cv2.line(draw_img, (0, 0), (cols//2, rows//2), (0, 255, 0), 5) # BGR
绘制蓝色矩形
cv2.rectangle(draw_img, (100, 100), (300, 250), (255, 0, 0), 3)
绘制红色填充圆形
cv2.circle(draw_img, (400, 150), 50, (0, 0, 255), -1)
添加文字
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(draw_img, ‘OpenCV Basics’, (50, 350), font, 1.5, (255, 255, 255), 2, cv2.LINE_AA)cv2.imshow(‘Drawing Demo’, draw_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
七、 核心图像处理技术
-
阈值处理 (
cv2.threshold
,cv2.adaptiveThreshold
)
阈值处理是图像分割的一种简单而有效的方法,用于将图像转换为二值图像(只有两种像素值,通常是黑和白)。-
简单阈值 (
cv2.threshold
): 全局应用一个阈值。
“`python
# ret: 实际使用的阈值(对于 Otsu 方法有用)
# thresholded_img: 输出的二值图像
ret, thresh_binary = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
# >127 的像素变为 255 (maxval), <=127 的变为 0
ret, thresh_binary_inv = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)
# >127 的像素变为 0, <=127 的变为 255Otsu’s Binarization: 自动寻找最优阈值(假设图像直方图有双峰)
ret_otsu, thresh_otsu = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
cv2.imshow(‘Binary Threshold’, thresh_binary)
cv2.imshow(‘Otsu Threshold’, thresh_otsu)
cv2.waitKey(0)
cv2.destroyAllWindows()
* **自适应阈值 (`cv2.adaptiveThreshold`):** 对图像不同区域使用不同的阈值,适用于光照不均的图像。
pythoncv2.ADAPTIVE_THRESH_MEAN_C: 邻域均值
cv2.ADAPTIVE_THRESH_GAUSSIAN_C: 邻域高斯加权和
blockSize: 计算阈值的邻域大小 (必须是奇数)
C: 从均值或加权和中减去的常数
thresh_adaptive_mean = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 11, 2)
thresh_adaptive_gaussian = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
cv2.imshow(‘Adaptive Mean’, thresh_adaptive_mean)
cv2.imshow(‘Adaptive Gaussian’, thresh_adaptive_gaussian)
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
-
-
图像平滑/模糊 (
cv2.blur
,cv2.GaussianBlur
,cv2.medianBlur
,cv2.bilateralFilter
)
用于降噪和模糊图像。- 均值滤波 (
cv2.blur
): 用邻域像素的平均值代替中心像素。 - 高斯滤波 (
cv2.GaussianBlur
): 用高斯核进行加权平均,离中心越近的像素权重越大。对高斯噪声效果好。 - 中值滤波 (
cv2.medianBlur
): 用邻域像素的中值代替中心像素。对椒盐噪声效果极佳。 - 双边滤波 (
cv2.bilateralFilter
): 在平滑的同时保持边缘清晰。考虑了空间距离和像素值差异。
“`python
核大小,必须是正奇数
kernel_size = (5, 5)
blurred_avg = cv2.blur(img_color, kernel_size)
blurred_gaussian = cv2.GaussianBlur(img_color, kernel_size, 0) # sigmaX=0, OpenCV 会自动计算中值滤波的核大小是一个整数 k, 表示 kxk 的核
blurred_median = cv2.medianBlur(img_color, 5)
双边滤波参数: d(邻域直径), sigmaColor(颜色空间标准差), sigmaSpace(坐标空间标准差)
blurred_bilateral = cv2.bilateralFilter(img_color, 9, 75, 75)
cv2.imshow(‘Gaussian Blur’, blurred_gaussian)
cv2.imshow(‘Median Blur’, blurred_median)
cv2.imshow(‘Bilateral Filter’, blurred_bilateral)
cv2.waitKey(0)
cv2.destroyAllWindows()
“` - 均值滤波 (
-
形态学操作 (
cv2.erode
,cv2.dilate
,cv2.morphologyEx
)
基于形状的图像处理技术,通常作用于二值图像。需要定义一个结构元素 (Structuring Element)。- 腐蚀 (
cv2.erode
): “收缩” 物体边界,可以去除小的噪点。 - 膨胀 (
cv2.dilate
): “扩张” 物体边界,可以填充物体内部的小孔洞,连接断开的区域。 - 开运算 (
cv2.MORPH_OPEN
): 先腐蚀后膨胀。去除小的噪点(类似腐蚀),同时基本保持物体大小(类似膨胀)。 - 闭运算 (
cv2.MORPH_CLOSE
): 先膨胀后腐蚀。填充物体内部的小孔洞,连接邻近物体(类似膨胀),同时基本保持物体大小(类似腐蚀)。 - 形态学梯度 (
cv2.MORPH_GRADIENT
): 膨胀图减腐蚀图,得到物体轮廓。 - 顶帽 (
cv2.MORPH_TOPHAT
): 原图减开运算图,得到噪声或小亮点。 - 黑帽 (
cv2.MORPH_BLACKHAT
): 闭运算图减原图,得到物体内部的小孔洞或暗点。
“`python
创建结构元素 (例如 5×5 的矩形)
kernel = np.ones((5, 5), np.uint8)
或者使用 cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
cv2.MORPH_ELLIPSE, cv2.MORPH_CROSS
img_binary = thresh_otsu # 使用前面得到的二值图
eroded = cv2.erode(img_binary, kernel, iterations=1)
dilated = cv2.dilate(img_binary, kernel, iterations=1)
opened = cv2.morphologyEx(img_binary, cv2.MORPH_OPEN, kernel)
closed = cv2.morphologyEx(img_binary, cv2.MORPH_CLOSE, kernel)
gradient = cv2.morphologyEx(img_binary, cv2.MORPH_GRADIENT, kernel)cv2.imshow(‘Original Binary’, img_binary)
cv2.imshow(‘Eroded’, eroded)
cv2.imshow(‘Dilated’, dilated)
cv2.imshow(‘Opened’, opened)
cv2.imshow(‘Closed’, closed)
cv2.waitKey(0)
cv2.destroyAllWindows()
“` - 腐蚀 (
-
边缘检测 (Canny 算法
cv2.Canny
)
Canny 是最常用、效果也较好的边缘检测算法之一。cv2.Canny(image, threshold1, threshold2)
image
:通常是灰度图。threshold1
,threshold2
:低阈值和高阈值(用于滞后阈值处理)。梯度大于threshold2
的像素被视为强边缘,梯度在两者之间的像素如果连接到强边缘则被视为边缘。推荐threshold2
是threshold1
的 2 到 3 倍。
python
edges = cv2.Canny(img_gray, 100, 200) # 调整阈值观察效果
cv2.imshow('Canny Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows() -
轮廓检测与绘制 (
cv2.findContours
,cv2.drawContours
)
轮廓可以看作是连接了所有具有相同颜色或强度的连续点的曲线。常用于物体检测和识别。-
cv2.findContours(image, mode, method)
image
:通常是二值图像(Canny 边缘图或阈值图)。注意:此函数会修改源图像,如果需要保留原图,请传入副本 (image.copy()
)。mode
:轮廓检索模式。常用:cv2.RETR_EXTERNAL
:只检索最外层轮廓。cv2.RETR_LIST
:检索所有轮廓,不建立父子关系。cv2.RETR_TREE
:检索所有轮廓,并建立完整的层级结构。
method
:轮廓逼近方法。常用:cv2.CHAIN_APPROX_NONE
:存储所有轮廓点。cv2.CHAIN_APPROX_SIMPLE
:压缩水平、垂直和对角线段,只存储端点。节省内存。
- 返回值:
contours
(轮廓列表,每个轮廓是点的 NumPy 数组),hierarchy
(轮廓间的层级关系)。在新版 OpenCV (4.x+) 中,返回值可能只有contours
和hierarchy
两项。
-
cv2.drawContours(image, contours, contourIdx, color, thickness)
image
:在其上绘制轮廓的图像(通常是原彩色图)。contours
:findContours
返回的轮廓列表。contourIdx
:要绘制的轮廓索引(-1 表示绘制所有轮廓)。color
,thickness
:绘制的颜色和线条粗细。
“`python
使用 Canny 边缘图查找轮廓
contours, hierarchy = cv2.findContours(edges.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
print(f”找到了 {len(contours)} 个外部轮廓。”)在原彩色图上绘制轮廓
contour_img = img_color.copy()
cv2.drawContours(contour_img, contours, -1, (0, 255, 0), 2) # 绘制所有轮廓,绿色,粗细为 2cv2.imshow(‘Contours’, contour_img)
cv2.waitKey(0)
cv2.destroyAllWindows()还可以计算轮廓的面积、周长、边界框、最小外接圆/矩形等
for cnt in contours:
area = cv2.contourArea(cnt)
perimeter = cv2.arcLength(cnt, True) # True表示闭合轮廓
x, y, w, h = cv2.boundingRect(cnt) # 边界框
cv2.rectangle(contour_img, (x, y), (x+w, y+h), (0, 0, 255), 1)
“`
-
-
直方图 (
cv2.calcHist
, Matplotlib 可视化)
图像直方图是像素强度分布的图形表示。cv2.calcHist([images], [channels], mask, [histSize], ranges)
[images]
: 源图像列表 (用方括号括起来)。[channels]
: 要计算直方图的通道索引列表。灰度图是[0]
,彩色图 BGR 分别是[0]
,[1]
,[2]
。mask
: 掩码图像 (可选,只统计掩码区域)。[histSize]
: BINS 的数量 (每个维度)。例如[256]
。ranges
: 像素值范围。例如[0, 256]
(不包括 256)。
“`python
import matplotlib.pyplot as plt计算灰度直方图
hist_gray = cv2.calcHist([img_gray], [0], None, [256], [0, 256])
计算彩色图像各通道直方图
color = (‘b’, ‘g’, ‘r’)
plt.figure()
plt.title(‘Color Histogram’)
plt.xlabel(‘Bins’)
plt.ylabel(‘# of Pixels’)
for i, col in enumerate(color):
hist_color = cv2.calcHist([img_color], [i], None, [256], [0, 256])
plt.plot(hist_color, color=col)
plt.xlim([0, 256])plt.figure()
plt.title(‘Grayscale Histogram’)
plt.xlabel(‘Bins’)
plt.ylabel(‘# of Pixels’)
plt.plot(hist_gray)
plt.xlim([0, 256])plt.show()
直方图均衡化 (改善对比度)
equ_gray = cv2.equalizeHist(img_gray)
cv2.imshow(‘Original Gray’, img_gray)
cv2.imshow(‘Equalized Gray’, equ_gray)
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
八、 处理视频
OpenCV 可以轻松处理视频文件或来自摄像头的实时视频流。视频本质上是连续的图像帧序列。
- 从摄像头捕获 (
cv2.VideoCapture(0)
)
0
通常代表默认的内置摄像头。如果有多个摄像头,可以尝试1
,2
等。 - 从视频文件读取 (
cv2.VideoCapture('video.mp4')
)
指定视频文件路径。
“`python
从摄像头捕获
cap = cv2.VideoCapture(0)
或者从文件读取
cap = cv2.VideoCapture(‘my_video.avi’)
if not cap.isOpened():
print(“错误:无法打开视频源”)
exit()
while True:
# 逐帧读取
# ret: 读取是否成功 (True/False)
# frame: 读取到的当前帧 (NumPy 数组)
ret, frame = cap.read()
# 如果正确读取帧 ret 为 True
if not ret:
print("无法接收帧 (视频流结束?)。正在退出...")
break
# 在这里对 frame 进行处理 (例如转灰度, Canny边缘检测等)
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
edges_frame = cv2.Canny(gray_frame, 50, 150)
# 显示处理后的帧
cv2.imshow('Original Video', frame)
cv2.imshow('Edges Video', edges_frame)
# 按 'q' 键退出循环
# waitKey(1) 等待 1ms, 使得视频能正常播放,同时也检测按键
if cv2.waitKey(1) == ord('q'):
break
释放资源
cap.release()
cv2.destroyAllWindows()
“`
-
保存视频 (
cv2.VideoWriter
)
需要指定输出文件名、编解码器 (FourCC)、帧率 (fps) 和帧大小。“`python
…(在 cap = cv2.VideoCapture(0) 之后)…
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = int(cap.get(cv2.CAP_PROP_FPS)) # 获取原始帧率可能不准确,有时需手动设置定义编解码器并创建 VideoWriter 对象
FourCC 是一个4字节码,用于指定视频编解码器
常见: ‘XVID’, ‘MJPG’, ‘MP4V’, ‘DIVX’ (可能需安装对应解码器)
在 Windows 上 ‘XVID’ 通常可用,在 Linux/macOS 上 ‘MJPG’ 或 ‘MP4V’ 可能更好
fourcc = cv2.VideoWriter_fourcc(*’XVID’)
out = cv2.VideoWriter(‘output/output_video.avi’, fourcc, 20.0, (frame_width, frame_height)) # 使用 20fps 保存…(在 while 循环内部,处理完 frame 之后)…
if ret:
# 写入处理后的帧 (确保写入的帧尺寸与 VideoWriter 初始化时一致)
# 如果处理改变了尺寸或通道数,需要调整
# 例如,如果想保存灰度视频,需要先将 gray_frame 转回 BGR
# gray_bgr_frame = cv2.cvtColor(gray_frame, cv2.COLOR_GRAY2BGR)
# out.write(gray_bgr_frame)# 保存原始彩色帧 out.write(frame) cv2.imshow('Frame', frame) if cv2.waitKey(1) == ord('q'): break
else:
break…(在循环结束后)…
cap.release()
out.release() # 释放 VideoWriter 对象
cv2.destroyAllWindows()
“`
九、 进阶之路:下一步学什么?
掌握了以上基础知识和核心概念后,您已经具备了使用 OpenCV Python 进行基本计算机视觉任务的能力。后续可以深入探索以下方向:
- 特征检测与匹配: SIFT, SURF, ORB, AKAZE 等算法,用于图像匹配、目标跟踪、全景拼接。
- 目标检测:
- 传统方法: Haar 级联分类器, HOG (Histogram of Oriented Gradients)。
- 深度学习方法: SSD (Single Shot MultiBox Detector), YOLO (You Only Look Once), Faster R-CNN 等,通常需要结合 TensorFlow, PyTorch 等框架。
- 图像分割: GrabCut, 分水岭算法, 以及基于深度学习的语义分割、实例分割。
- 光流估计: 跟踪视频中像素点的运动。
- 机器学习集成: 使用 OpenCV 内置的 SVM, K-Means 等,或结合 Scikit-learn 进行更复杂的分类、聚类任务。
- 3D 视觉: 相机标定, 立体视觉, Structure from Motion (SfM)。
- 性能优化: 利用 T-API (Transparent API) 加速,或直接在 C++ 中实现性能关键部分。
结语
OpenCV Python 是一个强大而迷人的工具,它为我们打开了理解和交互视觉世界的大门。本文从环境搭建、核心概念(图像即 NumPy 数组)、基础操作(读写、显示、颜色、几何变换、绘图)到核心图像处理技术(阈值、滤波、形态学、边缘、轮廓、直方图)以及视频处理,为您构建了一个相对完整的知识框架。
学习计算机视觉是一个持续实践和探索的过程。最重要的不是记住所有函数的参数,而是理解每个操作背后的原理、适用场景以及它们如何组合起来解决实际问题。动手编写代码,处理不同的图像和视频,尝试各种参数组合,是巩固知识、提升技能的最佳途径。希望本文能成为您 OpenCV Python 学习道路上的坚实起点,祝您在计算机视觉的奇妙世界中探索愉快!