基于yanshee颜色识别任务

设计要求如下:通过使用机器人的摄像头,弹出视频界面,当机器人通过颜色识别时发现红色小球之后,在视频界面用圆圈圈出来, 而且这个圆圈会随着小球的移动而移动,圆圈的大小也会随着距离机器人的远、近而变小、变大,另外,机器人也会根据小球位置的变化进行互动,当小球在视频界面左边(人的视角举左手,胸前灯闪黄灯,当小球在视频界面的右边(人的视角举右手,胸前灯闪绿灯。

 

 

任务简述:

 

在摄像头的视野中寻找到我们所需颜色的小球,并以摄像头视野的中点为分界线,判断小球的位置,进而进行对应的反应及动作。

 

理解:

 

摄像头的读取其实是一直在进行着对每一帧图像的一个循环,所以在用的时候可以直接将每一帧当作是一张图片来使用。

 

 

主要攻略:

 

  1. 根据已知的颜色的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(黑色),进而让我们清晰地知道小球的位置。

 

  1. 找到摄像头帧数中有我们所需要的小球后,将其用圆圈圈出来,圆圈跟踪小球的位置并随小球的远近而变化。

(主要思路:寻找小球的轮廓,并用cv2.circle圈出它

具体步骤:

前提说明:我们所进行蒙版操作的图像中,很有可能会存在与我们所设置的色彩颜色空间相似的物品或其他东西(下列简称干扰项),

这些干扰项会对我们在对我们的目标进行轮廓绘制以及圈出有影响,因此在绘制之前需要对蒙版图像进行一个过滤操作(排除干扰项,此文档仅介绍代码中所提到的过滤操作)。

1)对蒙版图像进行优化 尽可能减少干扰项。

a.先进行一个均值滤波操作,去噪。

blured = cv2.blur(img, (5, 5)) 参数img为蒙版图像

cv2.blurimg,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)对图像进行简单的优化过滤后,寻找图片中目标的轮廓(需要先做边缘检测)。

 

 

  1. 对图片的内容进行边缘检测。

imgCanny = cv2.Canny(closed, 50, 100)

 

cv2.Canny(src, thresh1, thresh2) 进行canny边缘检测

参数介绍:

src表示输入的图片

thresh1表示最小阈值

thresh2表示最大阈值

作用:用于进一步选边缘信息

  1. 边缘检测完毕之后,将边缘检测的图像找出来并画出。(其中可能会检测到多个轮廓,因此要对轮廓进行一个筛选。)。

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.drawContoursimg,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点以及它的宽和高)来得到圆圈的圆心还有半径。)

具体步骤:

  1. 用上述我们已经找出的轮廓,求它的最小正矩形,并返回四个返回值。

# 进行轮廓近似后的图形,用boundingRect函数得到它的最小正矩形

# 矩形的边界与图像边界平行

x,y最小正矩形左上角的点,w为宽,h为高。
            x, y, w, h = cv2.boundingRect(approx)

cv2.boundingRect(cnt)

参数介绍:

Cnt:轮廓近似后的图形

作用:得到图形的最小正矩形,得出四个返回值。

 

  1. 用得出的四个返回值,来作为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

 

 

 

 

 

 

posted @ 2020-11-23 12:04  ben_simmons  阅读(1085)  评论(0)    收藏  举报