使用 OpenCV 跟踪我的 Pen
用 OpenCV 跟踪我的 Pen。
写于:2020 年 8 月
Github 上的代码链接: https://github.com/osinkolu/Tutorial-3-Outro-to-OpenCV
Tracing my red pen.
欢迎大家回到 OpenCV 教程的另一集。请允许我与您分享一个简短的故事。前段时间,我的同事尝试用陀螺仪搭建一个可以直接写入电脑的设备,想法是在任何表面上书写,并在电脑上得到相应的结果。很酷,这就像写作而不是打字。是的,他们制造了这个设备,但在项目答辩当天,Akintadec 博士(其中一位指挥讲师)不同意这个想法。他声称陀螺仪应该在 (X,Y,Z) 轴上有一个原点,以便软件后端可以进行合理的测量并将其转换为轨迹。这最终将我们带到了这个快速跟踪项目,我们在其中检测我的对象(PEN)的颜色并跟踪它的运动。
管道
管道很简单,首先是获取框架,屏蔽除我们感兴趣的颜色之外的所有其他颜色区域,最后绘制轮廓的边界圆以及最后找到轮廓的位置之间的线以及现在的位置。
导入包。
首先,我们导入运行算法所需的包。如果你一直在关注我之前的教程,那么对你来说唯一新的包可能是 Deque。双端队列只是一种具有超快速编辑功能的列表形式,例如快速追加、快速弹出等。它甚至还有一个 append-left 功能,在这个项目中非常有用。
Time 只是 python 中另一个可用的包,它可以帮助我们实时跟踪。
from collections import deque # import deque 一个更快的列表
import numpy as np # 根据需要导入 numpy
import imutils # 我们肯定需要 imutils 的便利功能。
导入时间#时间就是时间
import cv2 # 主要库,opencv
设置参数
在下面的简短代码片段中,我为我的对象(红笔)的颜色定义了上下边界,我实际上是使用试错法来获得上下颜色边界的良好跨度,我留下了提示在奖金部分做到这一点。
在定义了这些之后,我将点初始化为一个最大长度为 64 的双端队列(把它想象成一个速度列表),当我追加一个新的项目时,那里的最大长度会弹出列表中的项目。最后,我开始播放视频流,给它一些 2 秒的预热时间。
lower_color_boundary = (0,150,150) # 定义HSV中物体颜色的下边界
upper_color_boundary = (5, 255, 255)# 定义HSV中物体颜色的上部区域
points = deque(maxlen = 64) # 最大容纳 64 个内容的 Deque。
print("启动视频流")
vs = cv2.VideoCapture("video_file.mp4")#启动视频流。
#vs = cv2.VideoCapture(0)# 如果要实时流式传输,请取消注释。
time.sleep(2.0) #设置睡眠时间为2.0秒 启动视频流,
大交易
在本节中,我们什么都做。首先,我将整个过程放在一个while循环中,因为视频只是一个连续移动的图片帧。
在下一行中,我从视频母版中读取了帧,接下来,我将 ret 和帧分开。 ret 只是一个关于 vs.read() 是否有返回的布尔值。接下来,如果框架中没有任何内容,则中断并停止该过程。另一种方法可能是,如果 ret 为假,则中断。在下一行中,我使用 imutils 来调整框架的大小。与压力大的标准 OpenCV 方法相比,imutils resize 在保持纵横比方面节省了很多压力。
之后,我决定翻转图像。我这样做是为了获得我的框架的镜像视图,这是从相机直接流式传输绝对需要的。
处理帧。
框架准备好了,首先,我使用流行的高斯模糊方法对框架进行模糊处理,这有助于减少图像中的噪点。接下来,我将颜色从 BGR 转换为 HSV。 HSV 是一个色带,表示色相、饱和度和值。它是更接近人类视觉(我们的观看方式)的颜色表示。
在下一步中,我通过对图像进行阈值化创建了一个蒙版,这样我只保存了上下边界内的颜色,在本例中是我的红笔的颜色。在屏蔽掉图像的其他部分后,我进行了腐蚀和膨胀,以进一步降低阈值化带来的高频噪声。
都说了又做了??
现在我们已经获得了对象的视图(我的红笔),我们找到轮廓并使用 imutils 抓取它们。在下面的 IF 语句中,我选择了面积最大的轮廓,这绝对是我的对象的轮廓,其余我相当肯定的轮廓只是噪声。除非您打算跟踪两个相同颜色的对象。在下一行中,我准备了在对象上构建边界圆所需的参数。首先是半径和 XY 坐标,接下来我使用 cv2.Moments 来获取它的中心。然后,如果半径足够合理,我会在对象周围绘制一个边界圆,并在对象中心绘制一个实心点。
非常重要的是,请注意我将对象的中心追加到双端队列。请记住,我的双端队列的最大长度为 64。我将很快解释我为什么这样做。
绘制痕迹。
最后一步也是最后一步是绘制痕迹。为此,我决定用一条线按顺序连接我的所有点,就像链式一样。我还指定如果列表中的值是无,它应该忽略并重新开始 while 循环。我这样做是为了避免在未检测到对象时在我的框架上的(0,0)处绘制一个点(无,无)。
另一方面,如果我找到我的对象,它会将所有 64 个点连接在一起并追踪它。我使用了 64 的最大长度来避免我的屏幕被所有痕迹弄乱。自己看效果。运行代码。
而真:
frame = vs.read() #从视频流中读取帧
ret,frame = frame # 如果你想在视频中加载,请使用它
if frame is None: # 如果没有框架,让我的电脑免于承受任何压力
休息
# 否则,如果我们有一个框架,我们继续下面的代码
frame = imutils.resize(frame, width = 700) # 比打开 cv 简单多了,保持纵横比不变
frame = cv2.flip(frame,1) # 我想要镜像视图,这非常有用,尤其是当我在流式传输时
#处理帧
blurred = cv2.GaussianBlur(frame, (11,11),0) # blurr 有助于降低高频噪声,肯定有助于模型
hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV) # 将我的颜色转换为HSV格式
# 创建一个蒙版
mask = cv2.inRange(hsv, lower_color_boundary, upper_color_boundary) # 屏蔽除从上到下范围内的颜色以外的其他区域(阈值化)
mask = cv2.erode(mask, None, iterations =2) # 减少阈值引起的噪声
mask = cv2.dilate(mask, None, iterations =2) # 将找到的对象放在前台,即进一步降低噪音。
contours = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 查找轮廓
contours = imutils.grab_contours(contours) # 使用 imutils 抓取轮廓
center = None # center 初始设置为 none
if len(contours) > 0: # 如果等高线列表不为空则继续
contour = max(contours, key = cv2.contourArea) # 选择面积最大的轮廓,最有可能是我们的对象
((x,y), radius) = cv2.minEnclosureCircle(contour) # 拾取坐标以在对象周围画一个圆
M = cv2.moments(contour) # 从轮廓中提取矩。
center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"])) # 获取物体的质心。
if radius > 10: # 如果我们对检测到的提议对象有一个合理的半径
cv2.circle(frame, (int(x), int(y)), int(radius), (0,255,255), 2) # 画一个圆来绑定Object
cv2.circle(frame, center, 5, (0,0,225), -1) # 在圆的中心画一个实心点
points.appendleft(center) # 告诉我的双端队列保持该帧中心的位置。
# 快速绘制双端队列中的所有元素。
for i in range (1, len(points)): #对于双端队列中的所有点
if points[i-1] is None or points is None: # if we have none as the current point or previous
continue # 重新开始 while 循环。
厚度 = int(np.sqrt(64 / float(i+1)) * 2.5) # 让厚度随着双端队列中的点而变化
cv2.line(frame, points[i-1], points[i], (0,0,225), thickness) #在双端队列的所有64个点之间画一条线
cv2.imshow("逐帧制作视频", frame) #我们看帧X帧
# 关闭一个视频帧
key = cv2.waitKey(1) #等待cv键
if key == ord("x"): # 如果 x 按钮被按下
break # 从循环中中断
vs.release() # 让opencv释放视频加载器
cv2.destroyAllWindows() # 销毁所有窗口关闭它
奖金提示。
我到底是如何找到我的红笔的 HSV 值的?我不是魔术师,我是程序员,而且我绝对不知道所有的事情。您可以在谷歌搜索中查找您感兴趣的颜色的 HSV 值,并像我一样使用这个简短的代码片段在此处测试它们(试错)。我已将代码放入 RAWNB 格式。
img = cv2.imread('gold.jpg') #load in your image of intrest
img = imutils.resize(img,width = 400)
RED_MIN = np.array([260, 100, 80],np.uint8) # 红色的最小颜色
RED_MAX = np.array([280, 200, 160],np.uint8) # 最大颜色偏红
hsv_img = cv2.cvtColor(img,cv2.COLOR_BGR2HSV) #将图像颜色格式转换为HSV
frame_threshed = cv2.inRange(hsv_img, RED_MIN, RED_MAX) #对图像设置阈值
cv2.imshow('阈值图像', frame_threshed) # 显示阈值图像
cv2.imshow('original image', img) #显示原图
cv2.waitKey(0) # 关闭方便。 -1
我总是乐于帮助爱好者解决他们在机器学习和深度学习方面面临的困难。请随时与我联系:最好在 LinkedIn 上。
. 推特 .
. 领英 .
. GitHub
作者:Olufemi Victor Tolulope
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通