本文是机器视觉实战系列第1篇,实现的一个通过颜色来小球的检测和运动轨迹跟踪,效果如下:
原文出处:Ball Tracking with OpenCV.
项目学习
代码学习
我使用的环境是:MacOS+Python3.7.4+OpenCV 4.2.0.
相比于原文,我对代码做了一些小修改,并且加了一些中文注释,方便你理解。整个项目代码如下:
import argparse
import time
from collections import deque
import cv2
import imutils
import numpy as np
from imutils.video import VideoStream
# 命令行参数
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", help="path to video")
ap.add_argument("-b", "--buffer", type=int, default=64, help="max buffer size")
args = vars(ap.parse_args())
# 绿色球的HSV色域空间范围
greenLower = (29, 86, 6)
greenUpper = (64, 255, 255)
pts = deque(maxlen=args["buffer"])
# 判断是读入的视频文件,还是摄像头实时采集的,这里作区分是因为两种情况下后面的有些操作是有区别的
if args.get("video", None) is None:
useCamera = True
print("video is none, use camera...")
vs = VideoStream(src=0).start()
else:
useCamera = False
vs = cv2.VideoCapture(args["video"])
time.sleep(2.0)
while True:
frame = vs.read()
# 摄像头返回的数据格式为(帧数据),而从视频抓取的格式为(grabbed, 帧数据),grabbed表示是否读到了数据
frame = frame if useCamera else frame[1]
# 对于从视频读取的情况,frame为None表示数据读完了
if frame is None:
break
# resize the frame(become small) to process faster(increase FPS)
frame = imutils.resize(frame, width=600)
# blur the frame to reduce high frequency noise, and allow
# us to focus on the structural objects inside the frame
# 通过高斯滤波去除掉一些高频噪声,使得重要的数据更加突出
blurred = cv2.GaussianBlur(frame, (11, 11), 0)
# convert frame to HSV color space
hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV)
# handles the actual localization of the green ball in the frame
# inRange的作用是根据阈值进行二值化:阈值内的像素设置为白色(255),阈值外的设置为黑色(0)
mask = cv2.inRange(hsv, greenLower, greenUpper)
# A series of erosions and dilations remove any small blobs that may be left on the mask
# 腐蚀(erode)和膨胀(dilate)的作用:
# 1. 消除噪声;
# 2. 分割(isolate)独立的图像元素,以及连接(join)相邻的元素;
# 3. 寻找图像中的明显的极大值区域或极小值区域
mask = cv2.erode(mask, None, iterations=2)
mask = cv2.dilate(mask, None, iterations=2)
# 寻找轮廓,不同opencv的版本cv2.findContours返回格式有区别,所以调用了一下imutils.grab_contours做了一些兼容性处理
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
center = None
# only proceed if at least one contour was found
if len(cnts) > 0:
# find the largest contour in the mask, then use it to compute the minimum enclosing circle
# and centroid
c = max(cnts, key=cv2.contourArea)
((x, y), radius) = cv2.minEnclosingCircle(c)
M = cv2.moments(c)
# 对于01二值化的图像,m00即为轮廓的面积, 一下公式用于计算中心距
center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
# only proceed if the radius meets a minimum size
if radius > 10:
# draw the circle and centroid on the frame, then update the list of tracked points
cv2.circle(frame, (int(x), int(y)), int(radius), (0, 255, 255), 2)
cv2.circle(frame, center, 5, (0, 0, 255), -1)
pts.appendleft(center)
for i in range(1, len(pts)):
# if either of the tracked points are None, ignore them
if pts[i - 1] is None or pts[i] is None:
continue
# compute the thickness of the line and draw the connecting line
thickness = int(np.sqrt(args["buffer"] / float(i + 1)) * 2.5)
cv2.line(frame, pts[i - 1], pts[i], (0, 0, 255), thickness)
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
if useCamera:
vs.stop()
else:
vs.release()
cv2.destroyAllWindows()
该项目主要使用OpenCV实现,里面重要的地方我都已经加了注释说明,这里再理一下整个流程:
- 处理命令行参数。
- 定义绿色球在HSV色域空间的颜色范围,后面会根据这个范围进行二值化,从而实现轮廓检测。
- 区分一下是直接从摄像头读取的数据还是处理的已经拍好的视频,这里作区分是因为两种情况下,后面有些代码要做不同处理。
循环处理视频中的帧:
- 获取1帧的图像。对于从已有视频读的情况,如果读到的帧为空,则表示处理视频处理完了。
- 调整图片大小,主要是缩小一下图片,提高处理速度。
- 通过高斯滤波去除掉一些高频噪声,使得重要的数据更加突出。
- 将图像从BGR色域转换到HSV色域。
- 根据绿色球在HSV色域的颜色范围进行二值化。这样处理之后,绿色就会变成白色,其它都会变成黑色,方便后面提取球的轮廓。后面进行的腐蚀和膨胀操作也是为了消除噪声点(毛刺),更准确的提取小球轮廓。
- 提取轮廓。
- 后面就是根据提取出来的轮廓,画了一些小球的边沿,以及运动轨迹,就不细述了,代码里面很清楚。
图像处理效果展示
对于没有图像处理基础的人来说,可能不了解其中一些处理的作用,这里我把一些关键步骤处理的效果图放上来,方便你理解(以其中一帧图片为例):
滤掉了高频噪音,看着变模糊了。
HSV颜色空间
先简单介绍下HSV颜色空间吧(其实还有个HSL,和HSV类似,但有些不同)。RGB是在笛卡尔坐标系来表示颜色的,而HSV则是在圆柱坐标系中表示颜色的,所以它能表示更多的信息。下图是一个HSV的图(图片来自维基百科):
简单说就是HSV使用圆柱坐标系表示颜色,分三个维度(如上图中的e):
- Hue:即色相,就是我们平时说的颜色名称,比如红色、黄色。H的取值为绕圆柱中心轴一圈的角度(即圆柱坐标系中的方位角),所以取值范围是0\~360。
- Saturation:即饱和度,是指色彩的纯度,越高色彩越纯,低则逐渐变灰,取0-100%的数值。S为圆柱的半径值(即圆柱坐标系中的径向距离)。
- Value:即明度,也称亮度,取0-100%的数值。V为圆柱坐标系中的高度。
圆柱的中心轴底部为黑色,顶部为白色,中间为灰色。更详细的信息请参加维基百科:HSV。
那为什么我们要将图像从RGB转到HSV再做颜色识别呢?因为虽然RGB通道对人眼来说比较友好,但却不能很好地反映出物体具体的颜色信息。相反,HSV空间能够非常直观的表达色彩的明暗,色调,以及鲜艳程度,方便进行颜色之间的对比。所以在通过颜色识别对象时,都是采用HSV颜色空间的。
最后说一下两个实战时需要注意的事项:
- HSV标准中,Hue是角度,所以取值范围是0\~360,但在OpenCV中做了精简,实际范围变成了0\~180;S和V的取值是0\~100%,但OpenCV中是0\~255.所以使用OpenCV的时候需要注意一下。
一个颜色在HSV中没有特别明确的值或者范围,实际使用的时候往往需要我们自己去观察测试进行确定。下面是一个常见颜色H值大致的范围参考(注意范围是0\~360):
- 红色(Red):0\~60
- 黄色(Yellow):61\~120
- 绿色(Green): 121\~180
- 青色(Cyan): 181\~240
- 蓝色(Blue): 241\~300
- 品红(Magenta): 301\~360
资源信息
- 完整代码:code.
- 涉及的视频资源:在公众号回复“机器视觉实战1”获取下载链接。
通过这个项目我们可以学到如何通过颜色来做物体识别。
评论已关闭