目录
[10.1 OpenCV的Python接口](#10.1 OpenCV的Python接口)
[10.2 OpenCV基础知识](#10.2 OpenCV基础知识)
[10.4.2 Lucas-Kanade算法](#10.4.2 Lucas-Kanade算法)
10.1 OpenCV的Python接口
OpenCV 是一个 C++ 库,它包含了计算机视觉领域的很多模块。除了 C++ 和 C, Python 作为一种简洁的脚本语言,在 C++ 代码基础上的 Python 接口得到了越来越广泛的支持。
OpenCV 2.3.1 版本实际上提供了两个 Python 接口。旧的 cv 模块使用 OpenCV 内部 数据类型,并且从 NumPy 使用起来可能需要一些技巧。新的 cv2 模块用到了 NumPy 数组,并且使用起来更加直观 1 ,可以通过以下方式导入新的 cv2 模块:
import cv2
10.2 OpenCV基础知识
10.2.1 读取和写入图像
载入一张图像,打印出图像大小,对图像进行转换并保存为 .png 格式:
实验代码:
python
import cv2
# 读取图像
im = cv2.imread('suiyuan.jpg')
# 获取图像尺寸
h, w = im.shape[:2]
print(h, w)
# 保存图像为 .png 格式
cv2.imwrite('suiyuanresult.png', im)
分析:
- 读取图像 :使用
cv2.imread
函数加载图像文件。 - 获取尺寸 :通过
im.shape[:2]
获取图像的高度和宽度。 - 打印尺寸:输出图像的高度和宽度。
- 保存图像 :使用
cv2.imwrite
函数将原图保存为 PNG 格式。
结果:
10.2.2 颜色空间
实验代码:
python
import cv2
# 读取图像
im = cv2.imread('suiyuan.jpg')
# 创建灰度图像
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
# 获取原图像尺寸
h, w = im.shape[:2]
print(f'Original Image Size: {h}x{w}')
# 保存灰度图像
cv2.imwrite('gray_image.png', gray)
# 将原图像转换为 RGB 格式
rgb_image = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)
cv2.imwrite('result_rgb.png', rgb_image)
分析:
- 图像读取 :使用
cv2.imread
函数加载图像文件 "suiyuan.jpg"。 - 颜色空间转换 :
- 将原图像从 BGR 转换为灰度图像,使用
cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
。 - 还可以将 BGR 图像转换为 RGB 格式,使用
cv2.cvtColor(im, cv2.COLOR_BGR2RGB)
。
- 将原图像从 BGR 转换为灰度图像,使用
- 打印图像尺寸:输出原图像的高度和宽度。
- 保存图像 :
- 保存灰度图像为 "gray_image.png"。
- 保存 RGB 图像为 "result_rgb.png"。
结果:
原图像:
灰度图像:
RGB图像
10.2.3 显示图像及结果
实验代码:
python
import cv2
# 读取图像
im = cv2.imread('guimie.jpg')
# 转换为灰度图像
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
# 计算积分图像
intim = cv2.integral(gray)
# 归一化积分图像并保存
intim_normalized = (255.0 * intim) / intim.max()
cv2.imwrite('result.jpg', intim_normalized)
分析:
- 图像读取 :使用
cv2.imread
函数读取图像文件 "guimie.jpg"。 - 颜色空间转换 :将图像转换为灰度图像,使用
cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
。 - 计算积分图像 :通过
cv2.integral(gray)
创建积分图像,计算每个像素值为原图左上方所有像素强度的和,这在特征评估中非常有效。 - 归一化和保存 :将积分图像归一化到 0-255 范围,并使用
cv2.imwrite
保存为 "result.jpg"。
结果:
10.3 处理视频
10.3.1 视频输入
实验代码:
python
import cv2
# 打开视频文件
cap = cv2.VideoCapture('wanjia.mp4')
# 获取视频的宽度和高度
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# 定义视频编码和输出文件
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output_video.avi', fourcc, 20.0, (frame_width, frame_height))
while True:
ret, frame = cap.read()
if not ret:
break # 如果没有读取到帧,结束循环
# 在帧上添加文本
cv2.putText(frame, 'Video Output Experiment', (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
# 写入输出视频
out.write(frame)
# 显示当前帧
cv2.imshow('Video', frame)
# 按 'q' 键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放资源
cap.release()
out.release()
cv2.destroyAllWindows()
分析:
- 视频读取 :使用
cv2.VideoCapture
打开视频文件。 - 获取视频信息 :通过
get
方法获取视频的宽度和高度。 - 视频输出设置 :使用
cv2.VideoWriter
定义输出视频文件,设置编码方式、帧率和帧大小。 - 处理每一帧 :
- 在每帧上添加文本,使用
cv2.putText
。 - 将处理后的帧写入输出视频。
- 实时显示当前帧。
- 在每帧上添加文本,使用
- 退出条件:用户可以按 'q' 键退出视频播放。
- 资源释放:在结束时释放视频捕获和写入资源,并关闭所有窗口。
结果:
10.3.2 将视频读取到NumPy数组中
实验代码:
python
import cv2
import numpy as np
# 打开视频文件
cap = cv2.VideoCapture('wanjia.mp4')
# 创建一个空的列表来存储视频帧
frames = []
# 读取视频帧
while True:
ret, frame = cap.read()
if not ret:
break # 如果没有读取到帧,结束循环
# 将帧添加到列表中
frames.append(frame)
# 关闭视频文件
cap.release()
# 将列表转换为 NumPy 数组
video_array = np.array(frames)
# 打印视频数组的形状
print("视频数组形状:", video_array.shape)
# 可选:显示第一帧
cv2.imshow('First Frame', video_array[0])
cv2.waitKey(0)
cv2.destroyAllWindows()
分析:
- 打开视频文件 :使用
cv2.VideoCapture
打开指定的视频文件。 - 读取视频帧 :通过循环读取视频中的每一帧,使用
cap.read()
。- 如果读取成功,
ret
为True
,frame
为当前帧。 - 将每一帧添加到
frames
列表中。
- 如果读取成功,
- 释放资源 :在读取完视频后,调用
cap.release()
释放视频文件。 - 转换为 NumPy 数组 :使用
np.array(frames)
将帧列表转换为 NumPy 数组。 - 打印形状 :输出视频数组的形状,可以看到数组的维度,形状为
(帧数, 高度, 宽度, 通道数)
。 - 显示第一帧:可选地展示第一帧,确保读取成功。
结果:
10.4 跟踪
10.4.1 光流
光流是目标、场景或摄像机在连续两帧图像间运动时造成的目标的运动。它是图像在平移过程中的二维矢量场。
光流法主要依赖于三个假设。
(1) 亮度恒定 图像中目标的像素强度在连续帧之间不会发生变化。
(2) 时间规律 相邻帧之间的时间足够短,以至于在考虑运行变化时可以忽略它们之间的差异。该假设用于导出下面的核心方程。
(3) 空间一致性 相邻像素具有相似的运动。
实验代码:
python
import cv2
import numpy as np
def draw_flow(im, flow, step=16):
""" 在间隔分开的像素采样点处绘制光流 """
h, w = im.shape[:2]
y, x = np.mgrid[step/2:h:step, step/2:w:step].reshape(2, -1)
fx, fy = flow[y.astype(int), x.astype(int)].T
# 创建线的终点
lines = np.vstack([x, y, x + fx, y + fy]).T.reshape(-1, 2, 2)
lines = lines.astype(int)
# 创建图像并绘制
vis = cv2.cvtColor(im, cv2.COLOR_GRAY2BGR)
for (x1, y1), (x2, y2) in lines:
cv2.line(vis, (x1, y1), (x2, y2), (0, 255, 0), 1)
cv2.circle(vis, (x1, y1), 1, (0, 255, 0), -1)
return vis
# 设置视频捕获
cap = cv2.VideoCapture(0)
# 读取第一帧
ret, im = cap.read()
if not ret:
print("无法读取视频流")
exit()
prev_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
while True:
# 获取灰度图像
ret, im = cap.read()
if not ret:
break
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
# 计算光流
flow = cv2.calcOpticalFlowFarneback(prev_gray, gray, None, 0.5, 3, 15, 3, 5, 1.2, 0)
prev_gray = gray
# 画出光流
cv2.imshow('Optical Flow', draw_flow(gray, flow))
if cv2.waitKey(10) == 27: # 按 'ESC' 退出
break
cap.release()
cv2.destroyAllWindows()
分析:
-
引入库:
cv2
用于计算机视觉处理。numpy
用于处理数组和矩阵。
-
函数
draw_flow
:- 输入图像和光流数据,在采样点处绘制光流矢量。
- 通过
np.mgrid
生成采样点,并利用光流计算终点。 - 绘制线条和圆点以可视化流动。
-
视频捕获:
- 使用
cv2.VideoCapture
捕获视频流。 - 读取第一帧并转换为灰度图。
- 使用
-
光流计算:
- 在循环中读取新帧,计算光流并更新前一帧的灰度图。
- 调用
draw_flow
函数可视化光流。
-
退出条件:
- 通过按
ESC
键退出循环,并释放视频捕获资源
- 通过按
结果:
10.4.2 Lucas-Kanade算法
跟踪最基本的形式是跟随感兴趣点,比如角点。对此,一次流行的算法是 Lucas-Kanade 跟踪算法,它利用了稀疏光流算法。
实验代码:
python
import cv2
import numpy as np
def lucas_kanade_optical_flow(video_path):
cap = cv2.VideoCapture(video_path)
ret, old_frame = cap.read()
if not ret:
print("无法读取视频")
return
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, maxCorners=100, qualityLevel=0.3, minDistance=7, blockSize=7)
mask = np.zeros_like(old_frame)
while True:
ret, frame = cap.read()
if not ret:
break
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None)
# 检查 p1 是否为 None
if p1 is None or st is None:
print("光流计算失败")
break
good_new = p1[st[:, 0] == 1] # 注意这里的索引
good_old = p0[st[:, 0] == 1]
for i, (new, old) in enumerate(zip(good_new, good_old)):
a, b = map(int, new.ravel())
c, d = map(int, old.ravel())
mask = cv2.line(mask, (a, b), (c, d), (0, 255, 0), 2)
frame = cv2.circle(frame, (a, b), 5, (0, 0, 255), -1)
img = cv2.add(frame, mask)
cv2.imshow('Optical Flow', img)
old_gray = frame_gray.copy()
p0 = good_new.reshape(-1, 1, 2)
if cv2.waitKey(30) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows()
# 调用函数
video_path = 'wanjia.mp4' # 替换为你的视频路径
lucas_kanade_optical_flow(video_path)
分析:
-
光流原理:
- Lucas-Kanade 算法基于局部平滑性假设和亮度恒定性假设,利用图像的亮度变化来估计光流场。算法通过对邻域内的像素点进行线性最小二乘拟合,得到物体的运动信息。
-
参数设置:
maxCorners
:检测到的最大角点数量。qualityLevel
:角点质量的最小阈值。minDistance
:检测角点之间的最小距离。
结果:
-
运行代码后,将打开一个窗口,显示视频中物体的跟踪效果。
-
绿色线条表示跟踪的运动轨迹,红色圆点表示当前跟踪的角点。