基于yanshee颜色识别任务
设计要求如下:通过使用机器人的摄像头,弹出视频界面,当机器人通过颜色识别时发现红色小球之后,在视频界面用圆圈圈出来, 而且这个圆圈会随着小球的移动而移动,圆圈的大小也会随着距离机器人的远、近而变小、变大,另外,机器人也会根据小球位置的变化进行互动,当小球在视频界面左边(人的视角)举左手,胸前灯闪黄灯,当小球在视频界面的右边(人的视角)举右手,胸前灯闪绿灯。
任务简述:
在摄像头的视野中寻找到我们所需颜色的小球,并以摄像头视野的中点为分界线,判断小球的位置,进而进行对应的反应及动作。
理解:
摄像头的读取其实是一直在进行着对每一帧图像的一个循环,所以在用的时候可以直接将每一帧当作是一张图片来使用。
主要攻略:
- 根据已知的颜色的HSV色彩空间的值,判断在摄像头视野中的小球的颜色已经对其进行一个定位。
具体操作:
1)将颜色的HSV的最小范围和最大范围分别定义在np数组中。
Eg: 橙色的HSV色彩空间为:
orange_min = np.array([0, 49, 117]) //Hmin、Smin、Vmin
orange_max = np.array([40, 255, 255]) //Hmax、Smax、Vmax
2)对 读取摄像头的当前帧 进行一个颜色转换,将原来BGR的图像转化为HSV:
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
3)创建一个掩膜mask(蒙版),将我们所需的目标进行一个定位。
掩膜mask的作用:图像掩模一般用来对处理的图像(全部或者局部)进行遮挡,来控制图像处理的区域或处理过程。
mask = cv2.inRange(hsv, orange_min, orange_max)
代码解释:参数hsv为我们已转换颜色空间的图片,后两个参数为1)橙色的HSV色彩空间的最小值范围和最大值范围的np数组。
该代码的作用:将hsv图片中符合橙色的HSV色彩空间(阈值范围)的部分设为255(白色),其他部分掩盖为0(黑色),进而让我们清晰地知道小球的位置。
- 找到摄像头帧数中有我们所需要的小球后,将其用圆圈圈出来,圆圈跟踪小球的位置并随小球的远近而变化。
(主要思路:寻找小球的轮廓,并用cv2.circle圈出它)
具体步骤:
前提说明:我们所进行蒙版操作的图像中,很有可能会存在与我们所设置的色彩颜色空间相似的物品或其他东西(下列简称干扰项),
这些干扰项会对我们在对我们的目标进行轮廓绘制以及圈出有影响,因此在绘制之前需要对蒙版图像进行一个过滤操作(排除干扰项,此文档仅介绍代码中所提到的过滤操作)。
1)对蒙版图像进行优化 尽可能减少干扰项。
a.先进行一个均值滤波操作,去噪。
blured = cv2.blur(img, (5, 5)) 参数img为蒙版图像
cv2.blur(img,ksize)
参数介绍:
img:需要进行操作的图像
ksize: 核大小
作用:进行一个均值滤波操作,去除一些不必要的噪点,消除一些影响因素。
b.对已进行消噪的图片进行开操作然后闭操作,作用同理,消除干扰项,对图像进行优化,让目标更明显。
opened = cv2.morphologyEx(blured, cv2.MORPH_OPEN, (3, 3))
closed = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, (3, 3))
cv2.morphologyEx(src, op, kernel)
参数介绍:
Src:传入的图片
Op:进行变化的方式
Kernel:表示方框的大小
作用:
开操作:
- 去除噪声,消除小物体
- 在纤细点处分离物体
- 平滑较大物体的边界的同时并不明显改变其面积
闭操作:
- 排除小型空洞(指黑色区域)
- 平滑物体轮廓
- 弥合(连接)窄的间断点,沟壑
- 填补轮廓线断裂
2)对图像进行简单的优化过滤后,寻找图片中目标的轮廓(需要先做边缘检测)。
- 对图片的内容进行边缘检测。
imgCanny = cv2.Canny(closed, 50, 100)
cv2.Canny(src, thresh1, thresh2) 进行canny边缘检测
参数介绍:
src:表示输入的图片
thresh1:表示最小阈值
thresh2:表示最大阈值
作用:用于进一步筛选边缘信息。
- 边缘检测完毕之后,将边缘检测的图像找出来并画出。(其中可能会检测到多个轮廓,因此要对轮廓进行一个筛选。)。
Aa:寻找轮廓:
contours, hierachy = cv2.findContours(imgCanny, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])
opencv2返回两个值:contours(轮廓本身),hierarchy(轮廓特征)。
注:opencv3会返回三个值,分别是img, countours, hierarchy
cv2.findContours()函数首先返回一个list列表,
list中每个元素都是图像中的一个轮廓。
参数介绍:
Image:寻找轮廓的图像
Mode:表示轮廓的检索模式
Method:轮廓的近似办法
作用:寻找轮廓。
Bb:通过轮廓的面积筛选轮廓并将轮廓画出:
for cnt in contours:
# 计算轮廓的面积
area = cv2.contourArea(cnt)
if area > 50:
# print("面积:{}".format(area))
# 筛选完后将筛选过后的轮廓draw画出来。
cv2.drawContours(frame_copy, cnt, -1, (0, 0, 255), 3)
# 计算轮廓周长
peri = cv2.arcLength(cnt, True)
# print("周长:{}".format(peri))
# 画出的轮廓可能不够规整,比如圆形不像圆形,多边形不像多边形,因此对其进行一个多边形拟合操作approxPolyDP。达到一种轮廓近似的效果。
approx = cv2.approxPolyDP(cnt, 0.02 * peri, True)
cv2.drawContours(img,contours,contourIndex,color,crudeness)
参数介绍:
Img:指明在哪幅图像上绘制轮廓(ima为三通道才能显示轮廓)
Contours:轮廓本身,是一个list;
contourIndex:指定绘制轮廓list中的哪条轮廓,如果是-1,则绘制其中的所有轮廓。
Color:指明颜色(需为三通道,eg:(255,0,0),否则不会显色)
crudeness:线条粗细
作用:画出轮廓。 ps:(在本代码中,此行代码只用于测试,没有也不影响)
cv2.aprroxPolyDP(cnt, epsilon, True)
参数介绍:
Cnt:为输入的轮廓值
Epsilon:为阈值T,通常使用轮廓的周长作为阈值
True:表示的是轮廓是闭合的
作用:画出的轮廓可能不够规整,比如圆形不像圆形,多边形不像多边形,因此对其进行一个多边形拟合操作approxPolyDP。达到一种轮廓近似的效果。
(小说明:进行完上述的操作之后,我们基本已经精准地找到了我们目标(小球)的位置,接下来的任务就是通过找出的轮廓,然后用圆圈圈出它。)
1)根据轮廓的位置,对其进行一个跟踪,即用圆圈把它的位置圈出,进行一个标记。
(主要思路:我们可以根据我们所找出的轮廓,求它的最小整矩形,然后借这个最小正矩形的返回值(返回值会返回它左上角的焦点的x点和y点以及它的宽和高)来得到圆圈的圆心还有半径。)
具体步骤:
- 用上述我们已经找出的轮廓,求它的最小正矩形,并返回四个返回值。
# 进行轮廓近似后的图形,用boundingRect函数得到它的最小正矩形。
# 矩形的边界与图像边界平行
(x,y)最小正矩形左上角的点,w为宽,h为高。
x, y, w, h = cv2.boundingRect(approx)
cv2.boundingRect(cnt)
参数介绍:
Cnt:轮廓近似后的图形
作用:得到图形的最小正矩形,得出四个返回值。
- 用得出的四个返回值,来作为cv.circle的参数。
cv2.circle(frame, ((x + w / 2), (y + h / 2)), w / 2, (0, 255, 0), 2)
cv2.Cricle(img, center, radius, (0, 255, 0), 2)
参数介绍:
Img:表示需要画的图片
Center:表示圆的中心点
Radius:表示圆的半径,(0, 255, 0)表示颜色
2:表示线条的粗细
作用: 根据坐标在图上画出圆
具体代码:
1 #!/usr/bin/env python 2 # coding: utf-8 3 4 # In[ ]: 5 6 7 # python2 所需 ↓ 8 9 10 # 导入需要用到的包 11 import time 12 from picamera.array import PiRGBArray 13 from picamera import PiCamera 14 import numpy as np 15 import cv2 16 import math 17 import requests 18 import json 19 20 # 分辨小球颜色需要用到的HSV值。 21 # 样题只要红色 22 # # 红色: 23 # red_min = np.array([0, 128, 46]) 24 # red_max = np.array([5, 255, 255]) 25 # red2_min = np.array([156, 128, 46]) 26 # red2_max = np.array([180, 255, 255]) 27 28 # # 绿色: 29 # green_min = np.array([35, 128, 46]) 30 # green_max = np.array([77, 255, 255]) 31 32 # # 蓝色: 33 # blue_min = np.array([100, 128, 46]) 34 # blue_max = np.array([124, 255, 255]) 35 36 # # 黄色: 37 # yellow_min = np.array([15, 128, 46]) 38 # yellow_max = np.array([34, 255, 255]) 39 40 # 橙色: 41 orange_min = np.array([0, 49, 117]) 42 orange_max = np.array([40, 255, 255]) 43 # # ...等等颜色 44 45 # # 把颜色都汇总到一个数组中,顺带给各自定义一个颜色名。 46 # COLOR_ARRAY = [[red_min, red_max, 'red'], [red2_min, red2_max, 'red'], [green_min, green_max, 'green'], 47 # [blue_min, blue_max, 'blue'], [yellow_min, yellow_max, 'yellow'], [orange_min, orange_max, 'orange']] 48 49 50 //建立与机器人的连接。 51 root_url = "http://127.0.0.1:9090/v1" 52 motions_url = root_url + "/motions" 53 led_url = root_url + "/devices/led" 54 headers = {'Content-Type': 'application/json'} 55 56 57 # 定义一个停止运动的函数。 58 def stopmotion(): 59 motion = {"operation": "stop", "motion": {"name": ""}} 60 json_data = json.dumps(motion) 61 response = requests.put(url=motions_url, data=json_data, headers=headers) 62 63 64 # 定义一个带有颜色参数的函数。 65 # 作用在于传对应参数颜色给机器人,机器人就闪什么颜色。 66 def putled(color): 67 led = {"type": "button", "color": color, "mode": "on"} 68 json_data = json.dumps(led) 69 response = requests.put(url=led_url, data=json_data, headers=headers) 70 71 # 定义一个带有参数的函数。 72 # 作用在于传对应参数方向(左右手)给机器人,机器人就举起哪只手。 73 def putmotion(direction): 74 motion = {"operation": "start", "motion": {"name": "raise","direction": direction}} 75 json_data = json.dumps(motion) 76 response = requests.put(url=motions_url, data=json_data, headers=headers) 77 78 79 80 # 先初始化摄像头: 81 camera = PiCamera() 82 camera.resolution = (320, 240) 83 camera.framerate = 40 84 rawCapture = PiRGBArray(camera, size=(320, 240)) 85 time.sleep(2) 86 # 在初始化摄像头后定义一个全局变量(global ) change,好让机器人在检测到小球的位置后 所执行的次数仅为1,不会死循环执行。 87 change = 0 88 89 90 # 查找颜色小球并圆圈圈出: 91 def find_color(img): 92 global change 93 cv2.imwrite('frame.jpg', img) 94 hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) 95 cv2.imwrite('hsv.jpg', hsv) 96 97 # for (color_min, color_max, name) in COLOR_ARRAY: 98 99 # 创建一个蒙版,在阈值范围内的像素点全变为255 白色,之外的全都变成0 黑色。 100 # mask = cv2.inRange(hsv, color_min, color_max) 101 # 创建一个蒙版,在阈值范围内的像素点全变为255 白色,之外的全都变成0 黑色。 102 mask = cv2.inRange(hsv, orange_min, orange_max) 103 cv2.imwrite('mask.jpg', mask) 104 # '与'操作,有蒙版参数在内的话,则仅上色蒙版中255的部分。 105 resultImg = cv2.bitwise_and(img, img, mask=mask) 106 cv2.imwrite('res.jpg', resultImg) 107 x, y, w, h = getContours(mask) 108 if x != 0 and y != 0 and w != 0 and h != 0: 109 cv2.circle(frame, ((x + w / 2), (y + h / 2)), w / 2, (0, 255, 0), 2) 110 cv2.putText(frame, 'orange', (x + w / 2, y + h + 10), cv2.FONT_HERSHEY_COMPLEX, 0.4, (0, 0, 255), 1) 111 # print(x + w / 2) 112 if change == 0: 113 change = 1 if (x + w // 2) < 160 else 2 114 print 'change001 = %d' % change 115 if ((x + w // 2) < 160) and (change == 1): 116 print("左手一个慢动作") 117 change = 2 118 putmotion("left") 119 putled("yellow") 120 elif ((x + w // 2) > 160) and (change == 2): 121 print("右手一个慢动作") 122 change = 1 123 putmotion("right") 124 putled("green") 125 126 127 # 找出轮廓: 128 def getContours(img): 129 x, y, w, h = 0, 0, 0, 0 130 # 进行一个均值滤波操作,去除一些不必要的噪点,消除一些影响因素。 131 blured = cv2.blur(img, (5, 5)) 132 # kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3)) 133 # 对进行过滤后的图片进行一个开操作 (先腐蚀再膨胀) 134 # 作用: 135 去除噪声,消除小物体 136 在纤细点处分离物体 137 平滑较大物体的边界的同时并不明显改变其面积 138 139 opened = cv2.morphologyEx(blured, cv2.MORPH_OPEN, (3, 3)) 140 cv2.imwrite('opened.jpg', opened) 141 142 143 144 145 146 147 # 对进行过滤后的图片进行一个闭操作 (先膨胀再腐蚀) 148 # 作用: 149 排除小型空洞(指黑色区域) 150 平滑物体轮廓 151 弥合(连接)窄的间断点,沟壑 152 填补轮廓线断裂 153 154 closed = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, (3, 3)) 155 cv2.imwrite('closed.jpg', closed) 156 # 对图像进行边缘检测。 157 imgCanny = cv2.Canny(closed, 50, 100) 158 cv2.imwrite('imgCanny.jpg', imgCanny) 159 # 将边缘检测的图像找出来。(其中可能会检测到多个轮廓,因此要对轮廓进行一个筛选。) 160 contours, hierachy = cv2.findContours(imgCanny, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE) 161 for cnt in contours: 162 area = cv2.contourArea(cnt) 163 if area > 50: 164 # print("面积:{}".format(area)) 165 # 筛选完后将筛选过后的轮廓draw画出来。 166 cv2.drawContours(frame_copy, cnt, -1, (0, 0, 255), 3) 167 peri = cv2.arcLength(cnt, True) 168 # print("周长:{}".format(peri)) 169 # 画出的轮廓可能不够规整,比如圆形不像圆形,多边形不像多边形,因此对其进行一个多边形拟合操作approxPolyDP。达到一种轮廓近似的效果。 170 approx = cv2.approxPolyDP(cnt, 0.02 * peri, True) 171 # 进行轮廓近似后的图形,用boundingRect函数得到它的最小正矩形。 172 # 矩形的边界与图像边界平行 173 # (x,y)为最小正矩形左上角的点,w为宽,h为高。 174 x, y, w, h = cv2.boundingRect(approx) 175 return x, y, w, h 176 177 178 # 关闭摄像头与窗口: 179 def close_it(): 180 camera.close() 181 cv2.destroyAllWindows() 182 stopmotion() 183 184 185 for frame in camera.capture_continuous(rawCapture, format='bgr', use_video_port=True): 186 # 当前帧 187 frame = frame.array 188 frame_copy = frame.copy() 189 find_color(frame) 190 cv2.imshow("Result", frame) 191 rawCapture.truncate(0) 192 if cv2.waitKey(1) & 0xFF == ord('q'): 193 close_it() 194 break
浙公网安备 33010602011771号