学习颜色识别之前先介绍一下新认识的图像格式HSV:
色调H
用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,紫色为300°;
饱和度S
饱和度S表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为0,饱和度达到最高。通常取值范围为0%~100%,值越大,颜色越饱和。
明度V
明度表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0%(黑)到100%(白)。
注意:H: 0 — 180;S: 0 — 255;V: 0 — 255。
那么为什么不直接用RGB格式呢?
由于数字图像中物体颜色的R、G和B分量都与照射到物体上的光量相关,因此相互关联,因此根据这些分量的图像描述使得物体识别困难。色调/明度/色度或色调/明度/饱和度方面的描述通常更相关。
预处理
在识别前要对图像做一系列预处理:
1.高斯模糊
将原图像进行模糊处理,方便颜色的提取
img1 = cv2.GaussianBlur(src,ksize,sigmaX,sigmay,borderType)
#src: 输入图像
#ksize:高斯卷积核的大小,注意 : 卷积核的宽度和高度都应为奇数,且可以不同
#sigmaX: 水平方向的标准差
#sigmaY: 垂直方向的标准差,默认值为0,表示与sigmaX相同
#borderType:填充边界类型
2.BGR转化为HSV
cv2.cvtColor(图像对象, cv2.COLOR_之前图像格式2要转换成的图像格式)
#第二个参数的意思就是在被转换格式和预转换格式之间加一个‘2’
#比方RGB转HSV === cv2.COLOR_RGB2HSV
#这里列举一下基本的图片格式
#BGR
#RGB
#GRAY
#HSV
#YCRCb
#HLS
#XYZ
#LAB
#YUV
3.去噪
基本上是腐蚀、膨胀、开闭运算的运用,具体要看环境情况,可以参考Day3博客里介绍的各种方法的优缺点来抉择用上门处理方法
树莓派 Opencv-基于Python学习记录DAY-3 形态学处理-腐蚀、膨胀、开闭运算_凉山有客不自赏的博客-CSDN博客
颜色识别
cv2.inRange(hsv, lower_, upper_)#处理对象,阈值上限,阈值下限
这一步就是对单一颜色的识别,将目标颜色的颜色转换为白色,其他背景转换为黑色
获取阈值
关于颜色阈值的获取可以使用以下代码,通过导入图片,拖动滑块获取阈值
import cv2
import numpy as np
def nothing(x):
pass
cv2.namedWindow("Tracking")
cv2.createTrackbar("LH","Tracking",35,255,nothing)
cv2.createTrackbar("LS","Tracking",43,255,nothing)
cv2.createTrackbar("LV","Tracking",46,255,nothing)
cv2.createTrackbar("UH","Tracking",77,255,nothing)
cv2.createTrackbar("US","Tracking",255,255,nothing)
cv2.createTrackbar("UV","Tracking",255,255,nothing)
#cv2.createTrackbar:绑定滑动条和窗口,定义滚动条的数值
#参数
#第一个参数时滑动条的名字,
#第二个参数是滑动条被放置的窗口的名字,
#第三个参数是滑动条默认值,
#第四个参数时滑动条的最大值,
#第五个参数时回调函数,每次滑动都会调用回调函数。
while True:
frame = cv2.imread('1.jpeg')
hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
#转换图像格式
l_h = cv2.getTrackbarPos("LH","Tracking")
l_s = cv2.getTrackbarPos("LS","Tracking")
l_v = cv2.getTrackbarPos("LV","Tracking")
u_h = cv2.getTrackbarPos("UH","Tracking")
u_s = cv2.getTrackbarPos("US","Tracking")
u_v = cv2.getTrackbarPos("UV","Tracking")
#cv2.getTrackbarPos:得到滑动条的数值
#参数
#第一个参数是滑动条名字,
#第二个时所在窗口,
#返回值是滑动条的数值。
l_g = np.array([l_h, l_s, l_v]) # 阈值下限
u_g = np.array([u_h,u_s,u_v]) # 阈值上限
mask = cv2.inRange(hsv,l_g,u_g) # 二值化
res=cv2.bitwise_and(frame,frame,mask=mask)
#cv2.bitwise_and是对二进制数据进行“与”操作,即对图像(灰度图像或彩色图像均可)每个像素值进行二进制“与”操作,将原图与二值化图像与运算,将阈值内的颜色以原本颜色显示
cv2.imshow("frame", frame)
cv2.imshow("mask", mask)
cv2.imshow("res", res)
#显示窗口
key = cv2.waitKey(1)
if key == 27:
break
cv2.destroyAllWindows()
#延时,当按下ESC时关闭窗口,如果用户没有按下键,则继续等待下一个delay时间(循环),直到用户按键触发
效果展示:
摄像头实时颜色识别
import cv2
import numpy as np
ball_color = 'green'
color_dist = {'red': {'Lower': np.array([0, 60, 60]), 'Upper': np.array([6, 255, 255])},
'blue': {'Lower': np.array([100, 80, 46]), 'Upper': np.array([124, 255, 255])},
'green': {'Lower': np.array([35, 43, 35]), 'Upper': np.array([90, 255, 255])},
}
cap = cv2.VideoCapture(0)
cv2.namedWindow('camera', cv2.WINDOW_AUTOSIZE)
while cap.isOpened():
ret, frame = cap.read()
if ret:
if frame is not None:
gs_frame = cv2.GaussianBlur(frame, (5, 5), 0) # 高斯模糊
hsv = cv2.cvtColor(gs_frame, cv2.COLOR_BGR2HSV) # 转化成HSV图像
erode_hsv = cv2.erode(hsv, None, iterations=2) # 腐蚀 粗的变细
inRange_hsv = cv2.inRange(erode_hsv, color_dist[ball_color]['Lower'], color_dist[ball_color]['Upper'])
cv2.imshow('camera', inRange_hsv)
cv2.waitKey(1)
else:
print("无画面")
else:
print("无法读取摄像头!")
cap.release()
cv2.waitKey(0)
cv2.destroyAllWindows()
轮廓识别
主要使用cv2.findContours()函数来查找检测物体的轮廓。前提是对二值化的图片检测。
contours, hierarchy=cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])
#第一个参数是寻找轮廓的图像;
#第二个参数表示轮廓的检索模式,有四种(本文介绍的都是新的cv2接口):
# cv2.RETR_EXTERNAL 表示只检测外轮廓
# cv2.RETR_LIST 检测的轮廓不建立等级关系
# cv2.RETR_CCOMP 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息.
#如果内孔内还有一个连通物体,这个物体的边界也在顶层。
# cv2.RETR_TREE 建立一个等级树结构的轮廓。
#第三个参数method为轮廓的近似办法
# cv2.CHAIN_APPROX_NONE 存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-#x2),abs(y2-y1))==1
# cv2.CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例#如一个矩形轮廓只需4个点来保存轮廓信息
# cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS 使用teh-Chinl chain 近似算法
#返回值
#cv2.findContours()函数返回两个值,一个是轮廓本身contour,还有一个是每条轮廓对应的属性hierarchy。
#contour返回一个list,list中每个元素都是图像中的一个轮廓,用numpy中的ndarray表示。
#hierarchy返回一个可选的hiararchy结果,这是一个ndarray,其中的元素个数和轮廓个数相同,每个轮廓#contours[i]对应4个hierarchy元素hierarchy[i][0] ~hierarchy[i][3],分别表示后一个轮廓、前一个轮
#廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,则该值为负数
轮廓绘制
cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset ]]]]])
#第一个参数是指明在哪幅图像上绘制轮廓;
#第二个参数是轮廓本身,在Python中是一个list。
#第三个参数指定绘制轮廓list中的哪条轮廓,如果是-1,则绘制其中的所有轮廓。
#后面的参数很简单。其中thickness表明轮廓线的宽度,如果是-1(cv2.FILLED),则为填充模式。绘制参数将在以后独立详细介绍。
示例代码
import cv2
img = cv2.imread("./example.png")
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img,contours,-1,(0,0,255),3)
cv2.imshow("img", img)
cv2.waitKey(0)