平移/倾斜伺服装置,帮助摄像机使用视觉自动跟踪颜色对象。
现在我们将使用我们的设备帮助相机自动跟踪颜色对象,如下所示:
OpenCV可免费用于学术和商业用途。它具有C ++,C,Python和Java接口,并支持Windows,Linux,Mac OS,iOS和Android。在我的一系列OpenCV教程中,我们将专注于Raspberry Pi(因此,Raspbian as OS)和Python。OpenCV专为提高计算效率而设计,专注于实时应用。因此,它非常适合物理计算项目!
2.安装OpenCV 3软件包
我正在使用Raspberry Pi V3更新到最新版本的Raspbian(Stretch),因此安装OpenCV的最佳方法是遵循Adrian Rosebrock开发的优秀教程:Raspbian Stretch:在Raspberry Pi上安装OpenCV 3 + Python 。
我尝试了几个不同的指南在我的Pi上安装OpenCV。阿德里安的教程是最好的。我建议你按照他的指导方针一步一步地做同样的事情。
完成Adrian的教程后,您应该准备好在您的Pi上运行我们的实验的OpenCV虚拟环境。
让我们转到我们的虚拟环境并确认OpenCV 3已正确安装。
Adrian建议每次打开新终端时运行命令“source”以确保系统变量已正确设置。
source ~/.profile
接下来,让我们进入我们的虚拟环境:
workon cv
如果您在提示符前面看到文本(cv),那么您就在cv virtualenvironment中:
(cv) pi@raspberry:~$
Adrian提请注意,cv Python虚拟环境完全独立,并与Raspbian Stretch下载中包含的默认Python版本隔离。因此,全局site-packages目录中的任何Python包都不可用于cv虚拟环境。同样,安装在cv的site-packages中的任何Python包都不可用于全局安装的Python。
现在,输入你的Python解释器:
python
并确认您运行的是3.5(或更高版本)版本
在解释器内部(将出现“>>>”),导入OpenCV库:
import cv2
如果没有出现错误消息,则在您的PYTHON VIRTUAL ENVIRONMENT上正确安装OpenCV。
您还可以检查安装的OpenCV版本:
cv2.__version__
应该出现3.3.0(或者可以在将来发布的高级版本)。上述终端PrintScreen显示前面的步骤。
一旦你在RPi中安装了OpenCV,让我们测试你的相机是否正常工作。
我假设您的Raspberry Pi上已经安装了PiCam。
在IDE上输入以下Python代码:
import numpy as np
import cv2
cap = cv2.VideoCapture(0)while(True):
ret, frame = cap.read()
frame = cv2.flip(frame, -1) # Flip camera vertically
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('frame', frame)
cv2.imshow('gray', gray)
if cv2.waitKey(1) & 0xFF == ord('q'):
breakcap.release()
cv2.destroyAllWindows()
上面的代码将捕获将由您的PiCam生成的视频流,以BGR颜色和灰色模式显示它们。
请注意,由于组装方式,我将相机垂直旋转。如果不是您的情况,请评论或删除“翻转”命令行。
您也可以从我的GitHub:https://github.com/Mjrovai/OpenCV-Object-Face-Tracking/blob/master/simpleCamTest.py下载代码
要执行,请输入命令:
python simpleCamTest.py
要完成程序,您必须按键盘上的[q] [Ctrl] + [C]键
图为结果。
4.使用OpenCV在Python中进行颜色检测
我们将尝试完成的一件事是检测和跟踪某个颜色对象。为此,我们必须更多地了解OpenCV如何解释颜色。
Henri Dang用OpenCV编写了一篇关于Python中的颜色检测的精彩教程。
通常,我们的相机将使用RGB颜色模式,可以通过将其视为可以由红色,绿色和蓝色的三种彩色灯制成的所有可能颜色来理解。我们将在这里使用BGR(蓝色,绿色,红色)。
如上所述,对于BGR,像素由3个参数表示,蓝色,绿色和红色。每个参数的值通常为0-255(或以十六进制表示的O到FF)。例如,计算机屏幕上的纯蓝色像素的B值为255,G值为0,R值为0。
OpenCV与HSV(色调,饱和度,值)颜色模型一起使用,它是RGB颜色模型的替代表示,由计算机图形学研究人员在20世纪70年代设计,以更加贴近人类视觉感知颜色制作属性的方式:
因此,如果要使用OpenCV跟踪某种颜色,则必须使用HSV模型对其进行定义。
假设我必须跟踪黄色物体,如上图所示的塑料盒。易用部分是找到它的BGR元素。您可以使用任何设计程序来查找它(我使用PowerPoint)。
在我的情况下,我发现:
蓝色:71
绿色:234
红色:213
接下来,我们必须将BGR(71,234,213)模型转换为HSV模型,该模型将使用上限和下限范围进行定义。为此,让我们运行以下代码:
import sys
import numpy as np
import cv2blue = sys.argv[1]
green = sys.argv[2]
red = sys.argv[3] color = np.uint8([[[blue, green, red]]])
hsv_color = cv2.cvtColor(color, cv2.COLOR_BGR2HSV)
hue = hsv_color[0][0][0]print("Lower bound is :"),
print("[" + str(hue-10) + ", 100, 100]\n")
print("Upper bound is :"),
print("[" + str(hue + 10) + ", 255, 255]")
您也可以从我的GitHub下载代码:https://github.com/Mjrovai/OpenCV-Object-Face-Tracking/blob/master/bgr_hsv_converter.py
要执行,请输入以下命令,其中包含之前找到的BGR值作为参数:
python bgr_hsv_converter.py 71 234 213
程序将打印对象颜色的上下边界。
在这种情况下:
lower bound: [24, 100, 100]
upper bound: [44, 255, 255]
终端打印屏幕显示结果。
最后,但并非最不重要的,让我们看看OpenCV在确定其颜色后如何“掩盖”我们的对象:
import cv2
import numpy as np# Read the picure - The 1 means we want the image in BGR
img = cv2.imread('yellow_object.JPG', 1) # resize imag to 20% in each axis
img = cv2.resize(img, (0,0), fx=0.2, fy=0.2)# convert BGR image to a HSV image
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # NumPy to create arrays to hold lower and upper range
# The “dtype = np.uint8” means that data type is an 8 bit integer
lower_range = np.array([24, 100, 100], dtype=np.uint8)
upper_range = np.array([44, 255, 255], dtype=np.uint8)# create a mask for image
mask = cv2.inRange(hsv, lower_range, upper_range)# display both the mask and the image side-by-side
cv2.imshow('mask',mask)
cv2.imshow('image', img)# wait to user to press [ ESC ]
while(1):
k = cv2.waitKey(0)
if(k == 27):
breakcv2.destroyAllWindows()
您也可以从我的GitHub:https://github.com/Mjrovai/OpenCV-Object-Face-Tracking/blob/master/colorDetection.py下载代码
要执行,请在目录中输入以下命令,其中包含目标对象的照片(在我的情况下:yellow_object.JPG):
python colorDetection.py
上面的图片将显示原始图像(“图像”)以及在应用蒙版之后对象将如何显示(“蒙版”)。
5.物体移动跟踪
现在我们知道如何使用蒙版“选择”我们的对象,让我们使用相机实时跟踪它的移动。为此,我将我的代码基于Adrian Rosebrock的Ball Tracking with OpenCV教程。
我强烈建议您详细阅读Adrian的教程。
首先,确认您是否安装了imutils库。这是Adrian的OpenCV便利功能集合,可以更轻松地完成一些基本任务(如调整大小或翻转屏幕)。如果没有,请输入以下命令在虚拟Python环境中安装库:
pip install imutils
接下来,从我的GitHub下载代码https://github.com/Mjrovai/OpenCV-Object-Face-Tracking/blob/master/ball_tracking.py,并使用以下命令执行它:
python ball_traking.py
因此,您将看到类似于以下gif的内容:
基本上,除了“视频垂直翻转”之外,它与Adrian的代码相同,我用这条线:
frame = imutils.rotate(frame, angle=180)
另请注意,使用的掩码边界是我们在上一步中获得的掩码边界。
6.测试GPIO
现在我们已经使用了OpenCV的基础知识,让我们为我们的RPi安装一个LED并开始与我们的GPIO进行交互。
按照上面的电气图:LED的阴极将通过一个220欧姆的电阻连接到GPIO 21,其阳极连接到GND。
让我们在虚拟Python环境中测试我们的LED。
请记住,在Python虚拟环境中可能没有安装RPi.GPIO!要解决此问题,一旦您在那里(记得确认(cv)在您的终端中),您需要使用pip将其安装到您的虚拟环境中:
pip install RPi.GPIO
让我们使用python脚本执行一个简单的测试:
import sys
import time
import RPi.GPIO as GPIO# initialize GPIO and variables
redLed = int(sys.argv[1])
freq = int(sys.argv[2])
GPIO.setmode(GPIO.BCM)
GPIO.setup(redLed, GPIO.OUT)
GPIO.setwarnings(False)print("\n [INFO] Blinking LED (5 times) connected at GPIO {0} \
at every {1} second(s)".format(redLed, freq))
for i in range(5):
GPIO.output(redLed, GPIO.LOW)
time.sleep(freq)
GPIO.output(redLed, GPIO.HIGH)
time.sleep(freq)# do a bit of cleanup
print("\n [INFO] Exiting Program and cleanup stuff \n")
GPIO.cleanup()
此代码将接收GPIO编号作为参数,以及LED应闪烁的频率(以秒为单位)。LED将闪烁5次,程序将终止。请注意,在终止之前,我们将释放GPIO。
因此,要执行脚本,必须输入参数,LED GPIO和频率。
例如:
python LED_simple_test.py 21 1
上述命令将每隔“1”秒闪烁5次连接到“GPIO 21”的红色LED。
可以从我的GitHub下载文件https://github.com/Mjrovai/OpenCV-Object-Face-Tracking/blob/master/GPIO_LED_test.py
上面的终端打印屏幕显示结果(当然您应该确认LED闪烁。
现在,让我们使用OpenCV和一些基本的GPIO。
7.识别颜色和GPIO交互
让我们开始将我们的OpenCV代码与GPIO交互集成。我们将从最后的OpenCV代码开始,我们将在其上集成GPIO-RPI库,因此我们将在相机找到彩色物体的任何时候打开红色LED。此步骤中使用的代码基于Adrian的优秀教程OpenCV,RPi.GPIO和Raspberry Pi上的GPIO Zero:
首先要做的是“创建”我们的LED,将其连接到特定的GPIO:
import RPi.GPIO as GPIO
redLed = 21
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(redLed, GPIO.OUT)
其次,我们必须初始化我们的LED(关闭):
GPIO.output(redLed, GPIO.LOW)
ledOn = False
现在,在循环内部,当找到对象时创建“圆圈”,我们将打开LED:
GPIO.output(redLed, GPIO.HIGH)
ledOn = True
让我们从我的GitHub下载完整的代码:
https://github.com/Mjrovai/OpenCV-Object-Face-Tracking/blob/master/object_detection_LED.py
使用以下命令运行代码:
python object_detection_LED.py
结果如下。请注意,每次检测到物体时,LED(左下角)都会亮起:
尝试使用不同的对象(颜色和格式)。您将看到,一旦掩模边界内的颜色匹配,LED就会亮起。
以下视频显示了一些经验。请注意,只会检测到位于颜色范围内的黄色物体,从而打开LED。忽略具有不同颜色的对象。
我们只在这里使用LED,如上一步所述。当我拍摄视频时,我的Pan Tilt已经组装完毕,所以请忽略它。我们将在下一步处理PAN / TILT机制。
8.泛倾斜机制
现在我们已经使用了OpenCV和GPIO的基础知识,让我们安装我们的Pan / tilt机制。
伺服电机应连接到外部5V电源,其数据引脚(在我的情况下,它们的黄色接线)连接到Raspberry Pi GPIO,如下所示:
GPIO 17 ==>倾斜伺服
GPIO 27 ==> Pan Servo
不要忘记将GND连接在一起==> Raspberry Pi - Servos - 外部电源)
您可以选择在Raspberry Pi GPIO和服务器数据输入引脚之间串联一个1K欧姆的电阻。这可以在发生伺服问题时保护您的RPi。
让我们也利用这个机会,在我们的虚拟Python环境中测试我们的伺服器。
让我们使用Python脚本用我们的驱动程序执行一些测试:
from time import sleep
import RPi.GPIO as GPIOGPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)def setServoAngle(servo, angle):
pwm = GPIO.PWM(servo, 50)
pwm.start(8)
dutyCycle = angle / 18. + 3.
pwm.ChangeDutyCycle(dutyCycle)
sleep(0.3)
pwm.stop()if __name__ == '__main__':
import sys
servo = int(sys.argv[1])
GPIO.setup(servo, GPIO.OUT)
setServoAngle(servo, int(sys.argv[2]))
GPIO.cleanup()
上面代码的核心是函数setServoAngle(伺服,角度)。该功能接收伺服GPIO编号作为参数,以及伺服必须定位的角度值。一旦此函数的输入为“角度”,我们必须将其转换为等效的占空比。
要执行脚本,必须输入参数,伺服GPIO和角度。
例如:
python angleServoCtrl.py 17 45
上述命令将连接在GPIO 17上的伺服(“倾斜”)与“仰角”成45度。
文件
https://github.com/Mjrovai/OpenCV-Object-Face-Tracking/blob/master/angleServoCtrl.py
可以从我的GitHub下载
9.查找对象实时位置
这里的想法是使用平移/倾斜机制将对象定位在屏幕中间。坏消息是,为了开始我们必须实时知道对象的位置。但好消息是,一旦我们已经拥有了对象中心的坐标,这很容易。
首先,让我们使用之前使用的“object_detect_LED”代码并修改它以打印已创建对象的x,y坐标。
从我的GitHub下载代码:objectDetectCoord.py
代码的“核心”是我们找到对象并在其上绘制一个圆圈的部分,其中心有一个红点。
# 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)
# print center of circle coordinates
mapObjectPosition(int(x), int(y))
# if the led is not already on, turn the LED on
if not ledOn:
GPIO.output(redLed, GPIO.HIGH)
ledOn = True
让我们将中心坐标“导出”到mapObjectPosition(int(x),int(y))函数以打印其坐标。功能下方:
def mapObjectPosition (x, y):
print ("[INFO] Object Center coordinates at \
X0 = {0} and Y0 = {1}".format(x, y))
运行程序,我们将在终端上看到(x,y)位置坐标,如上所示。移动对象并观察坐标。我们将意识到x从0到500(从左到右),y从o到350(从上到下)。见上图。
现在我们必须使用这些坐标作为我们的Pan / Tilt跟踪系统的起点
10.物体位置跟踪系统
我们希望我们的对象始终以屏幕为中心。所以,让我们定义一个例子,如果符合以下情况我们会认为我们的对象是“居中的”:
220 160 在这些边界之外,我们必须移动我们的Pan / Tilt机制以补偿偏差。基于此,我们可以构建函数mapServoPosition(x,y),如下所示。请注意,此函数中用作参数的“x”和“y”与我们之前用于打印中心位置的参数相同: # position servos to present object at center of the frame def mapServoPosition (x, y): global panAngle global tiltAngle if (x < 220): panAngle += 10 if panAngle > 140: panAngle = 140 positionServo (panServo, panAngle) if (x > 280): panAngle -= 10 if panAngle < 40: panAngle = 40 positionServo (panServo, panAngle) if (y < 160): tiltAngle += 10 if tiltAngle > 140: tiltAngle = 140 positionServo (tiltServo, tiltAngle) if (y > 210): tiltAngle -= 10 if tiltAngle < 40: tiltAngle = 40 positionServo (tiltServo, tiltAngle) 基于(x,y)坐标,使用函数positionServo(伺服,角度)生成伺服位置命令。例如,假设y位置是“50”,这意味着我们的对象几乎位于屏幕的顶部,可以转换为“相机视线”为“低”(假设倾角为120度) 所以我们必须“减少”倾斜角度(让我们说100度),所以相机视线将“向上”并且物体将在屏幕上“向下”(y将增加,比方说,190)。 上图显示了几何方面的示例。 想想泛相机将如何操作。请注意,屏幕没有镜像,这意味着如果您将对象移动到“左侧”,一旦您与相机相反,它将在屏幕上移动“右侧”。 函数positionServo(伺服,角度)可写为: def positionServo (servo, angle): os.system("python angleServoCtrl.py " + str(servo) + " " + str(angle)) print("[INFO] Positioning servo at GPIO {0} to {1} \ degrees\n".format(servo, angle)) 我们将调用之前显示的脚本进行伺服定位。 请注意,angleServoCtrl.py必须与objectDetectTrac.py位于同一目录中 完整的代码可以从我的GitHub:objectDetectTrack.py下载 下面的gif显示了我们项目工作的一个例子: 11.结论 一如既往,我希望这个项目可以帮助其他人进入激动人心的电子世界! 相关整套源码关注微信公众号:“图像算法”或者微信搜索账号imalg_cn关注公众号