OpenCV是一个强大的计算机视觉库,它提供了超过2500个优化算法,能够处理各种复杂的图像和视频任务。无论是给照片添加90年代的黑白效果,还是进行复杂的数学运算,OpenCV都能满足需求。在计算机视觉领域,掌握OpenCV是必不可少的。这个库被许多行业巨头如谷歌、微软、IBM广泛使用,并且在研究领域也得到了广泛的应用。OpenCV支持多种编程语言,包括Java、C++和Python。
本文将展示如何使用OpenCV的一些基本功能来执行复杂的目标跟踪任务。目标跟踪是指在视频中定位移动目标的过程。以足球比赛为例,需要实时跟踪比赛中的球的位置。对于人类来说,这个任务似乎很简单,但对于即使是最聪明的机器来说,这也是一个极其复杂的任务。因为计算机只理解数字,它们不理解图像是什么,只理解与图像相关的像素值。对于人眼来说看起来完全相同的两幅图像,对于计算机来说可能完全不同,因为即使是像素的微小变化也会导致差异。因此,目标跟踪被认为是计算机视觉中最复杂的任务之一。尽管如此,这并不是不可实现的。
目标跟踪可以通过机器学习以及基于深度学习的方法来实现。深度学习方法在复杂任务上提供更好的结果,并且相当通用,但需要大量的训练数据。而基于机器学习的方法则相对简单,但不够通用。本文将使用基于机器学习的方法,结合将在本文后面讨论的各种计算机视觉技术。这种技术在监控、安全、交通监控、机器人视觉、视频通信等领域被广泛使用。此外,目标跟踪还有许多用例,如人群计数、自动驾驶汽车、面部检测等。能想到在日常生活中使用目标跟踪的更多例子吗?
由于目标跟踪在现实生活中有如此多的应用,这个领域正在进行不断的研究,以实现更高的准确性并使模型更加健壮。本文将使用以下视频作为示例。会看到有一个红色的球在迷宫中移动,任务是检测球的位置并找到它的质心。还可以看到一个巨大的噪声(抱歉,人们),这使得任务更具挑战性。
import numpy as np
import cv2
这个步骤是完全可选的,如果图像足够大,可以调整图像大小以适应屏幕。
def resize(img):
return cv2.resize(img, (512, 512)) # arg1-输入图像, arg2-输出宽度, 输出高度
视频由帧组成,帧不过是构成整个动态画面的许多静态图像之一。下一步是使用OpenCV中的VideoCapture()函数读取这些帧,并通过while循环,可以看到帧的移动。可以通过cv2.waitKey(x)调整视频的速度,它会使屏幕暂停x毫秒。
cap = cv2.VideoCapture(vid_file_path)
ret, frame = cap.read()
while ret == True:
ret, frame = cap.read()
cv2.imshow("frame", resize(frame))
key = cv2.waitKey(1)
if key == ord('q'):
break
cv2.waitKey(0)
cv2.destroyAllWindows()
OpenCV以BGR格式读取图像,因此将颜色空间从BGR转换为HSV。为什么使用HSV而不是BGR或其他格式?使用HSV颜色格式是因为它对外部光照的微小变化更敏感。因此,它将提供更准确的掩码,从而获得更好的结果。
转换颜色空间后,需要过滤掉红色通道并创建一个掩码帧。HSV格式中的红色通道位于[0,230,170]到[255,255,220]的范围内。
cap = cv2.VideoCapture(vid_file_path)
ret, frame = cap.read()
l_b = np.array([0, 230, 170]) # 红色的HSV下界
u_b = np.array([255, 255, 220]) # 红色的HSV上界
while ret == True:
ret, frame = cap.read()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, l_b, u_b)
cv2.imshow("frame", resize(frame))
cv2.imshow("mask", mask)
key = cv2.waitKey(1)
if key == ord('q'):
break
cv2.waitKey(0)
cv2.destroyAllWindows()
到目前为止,已经创建了帧的掩码图像,并过滤掉了大部分噪声。接下来是获取球的边界。为此,将使用轮廓检测的概念。轮廓不过是围绕球的边界。幸运的是,不需要自己找到这些边界,因为OpenCV允许使用findContours()函数来实现目的。它接受一个掩码图像并返回一个轮廓数组。
在案例中,轮廓的值应该是一,因为只有一个球,但由于有些人戴着红帽子,会得到更多的轮廓。能想到什么方法来进一步减少这种噪声吗?为了处理这个问题,将使用OpenCV的另一个函数cv2.contourArea()。知道在掩码图像中,球的面积最大,因此它的轮廓也是如此。因此,将获得面积最大的轮廓。
cap = cv2.VideoCapture(vid_file_path)
ret, frame = cap.read()
l_b = np.array([0, 230, 170]) # 红色的HSV下界
u_b = np.array([255, 255, 220]) # 红色的HSV上界
while ret == True:
ret, frame = cap.read()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, l_b, u_b)
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
max_contour = contours[0]
for contour in contours:
if cv2.contourArea(contour) > cv2.contourArea(max_contour):
max_contour = contour
contour = max_contour
approx = cv2.approxPolyDP(contour, 0.01 * cv2.arcLength(contour, True), True)
x, y, w, h = cv2.boundingRect(approx)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 4)
cv2.imshow("frame", resize(frame))
cv2.imshow("mask", mask)
key = cv2.waitKey(1)
if key == ord('q'):
break
cv2.waitKey(0)
cv2.destroyAllWindows()
此外,可以同时检测球的质心。为此,将使用cv2.moments。cv2.moments计算轮廓内像素强度的加权平均和,从而允许从blob中获得一些更有用的信息,如其半径、质心等。确保在使用该函数之前将图像转换为二进制格式。可以在这里了解更多关于moments的信息。
cap = cv2.VideoCapture(vid_file_path)
ret, frame = cap.read()
l_b = np.array([0, 230, 170]) # 红色的HSV下界
u_b = np.array([255, 255, 220]) # 红色的HSV上界
while ret == True:
ret, frame = cap.read()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, l_b, u_b)
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
max_contour = contours[0]
for contour in contours:
if cv2.contourArea(contour) > cv2.contourArea(max_contour):
max_contour = contour
approx = cv2.approxPolyDP(max_contour, 0.01 * cv2.arcLength(max_contour, True), True)
x, y, w, h = cv2.boundingRect(approx)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 4)
M = cv2.moments(max_contour)
cx = int(M['m10'] // M['m00'])
cy = int(M['m01'] // M['m00'])
cv2.circle(frame, (cx, cy), 3, (255, 0, 0), -1)
cv2.imshow("frame", resize(frame))
cv2.imshow("mask", mask)
key = cv2.waitKey(1)
if key == ord('q'):
break
cv2.waitKey(0)
cv2.destroyAllWindows()
在本文中,使用了每帧的目标检测来完成目标跟踪任务。虽然有用,但可能不适用于每种情况。在阅读文章时,可能会有一些问题。如果视频中有多个目标怎么办?如果掩码图像无助于检测目标怎么办?如果目标不断地进出帧怎么办?如果没有目标怎么办?找到答案的一种方法是亲自尝试。总是可以调整输入,使任务更具挑战性,直到它不再让感兴趣。