OpenCV在目标跟踪中的应用

OpenCV是一个强大的计算机视觉库,它提供了超过2500个优化算法,能够处理各种复杂的图像和视频任务。无论是给照片添加90年代的黑白效果,还是进行复杂的数学运算,OpenCV都能满足需求。在计算机视觉领域,掌握OpenCV是必不可少的。这个库被许多行业巨头如谷歌、微软、IBM广泛使用,并且在研究领域也得到了广泛的应用。OpenCV支持多种编程语言,包括Java、C++和Python。

本文将展示如何使用OpenCV的一些基本功能来执行复杂的目标跟踪任务。目标跟踪是指在视频中定位移动目标的过程。以足球比赛为例,需要实时跟踪比赛中的球的位置。对于人类来说,这个任务似乎很简单,但对于即使是最聪明的机器来说,这也是一个极其复杂的任务。因为计算机只理解数字,它们不理解图像是什么,只理解与图像相关的像素值。对于人眼来说看起来完全相同的两幅图像,对于计算机来说可能完全不同,因为即使是像素的微小变化也会导致差异。因此,目标跟踪被认为是计算机视觉中最复杂的任务之一。尽管如此,这并不是不可实现的。

目标跟踪可以通过机器学习以及基于深度学习的方法来实现。深度学习方法在复杂任务上提供更好的结果,并且相当通用,但需要大量的训练数据。而基于机器学习的方法则相对简单,但不够通用。本文将使用基于机器学习的方法,结合将在本文后面讨论的各种计算机视觉技术。这种技术在监控、安全、交通监控、机器人视觉、视频通信等领域被广泛使用。此外,目标跟踪还有许多用例,如人群计数、自动驾驶汽车、面部检测等。能想到在日常生活中使用目标跟踪的更多例子吗?

由于目标跟踪在现实生活中有如此多的应用,这个领域正在进行不断的研究,以实现更高的准确性并使模型更加健壮。本文将使用以下视频作为示例。会看到有一个红色的球在迷宫中移动,任务是检测球的位置并找到它的质心。还可以看到一个巨大的噪声(抱歉,人们),这使得任务更具挑战性。

步骤1:导入所需的库

import numpy as np import cv2

步骤2:定义一个函数来调整图像大小

这个步骤是完全可选的,如果图像足够大,可以调整图像大小以适应屏幕。

def resize(img): return cv2.resize(img, (512, 512)) # arg1-输入图像, arg2-输出宽度, 输出高度

步骤3:读取视频帧

视频由帧组成,帧不过是构成整个动态画面的许多静态图像之一。下一步是使用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()

步骤4:执行阈值处理和预处理

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()

步骤5:轮廓检测

到目前为止,已经创建了帧的掩码图像,并过滤掉了大部分噪声。接下来是获取球的边界。为此,将使用轮廓检测的概念。轮廓不过是围绕球的边界。幸运的是,不需要自己找到这些边界,因为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()

在本文中,使用了每帧的目标检测来完成目标跟踪任务。虽然有用,但可能不适用于每种情况。在阅读文章时,可能会有一些问题。如果视频中有多个目标怎么办?如果掩码图像无助于检测目标怎么办?如果目标不断地进出帧怎么办?如果没有目标怎么办?找到答案的一种方法是亲自尝试。总是可以调整输入,使任务更具挑战性,直到它不再让感兴趣。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485