想不想实现不触碰电脑屏幕,只是在空中拿着彩笔绘画,就可以把对应的颜色画到屏幕中去?
今天这个项目就可以帮助你实现该功能,只需要你会使用python,且有opencv和图像处理的基础知识就可以实现该项目了。
下面跟着我一起操作吧!
1.先熟悉什么是hsv图像:
①在 HSV 色彩空间中 H,S,V 这三个通道分别代表着色相(Hue),饱和度(Saturation)和明度(Value)。
②在 OpenCV 视觉库中,HSV 的数值被做了小的修改, H 的范围调整为 0~180,S 和 V 的范围为 0~255。
③通过控制hsv的阈值,就会在原图片上得到不同的图像。
2.在opencv上进行hsv操作:
import cv2
image1=cv2.imread('puke.jfif')
image2=cv2.cvtColor(image1,cv2.COLOR_BGR2HSV)
cv2.imshow('ppp',image2)
cv2.waitKey(0)
就可以把图片变成hsv格式的:
3.我们尝试这,在上面加入createTrackba操作,可以通过设置hsv的值实现不是自己喜欢的颜色,让其消失,并让几张照片叠加在一起显示:
其代码如下:
import cv2
import numpy as np
'如果单纯地使用numpy的函数进行堆叠,对不同大小和不同通道的图像是无法进行堆叠的,所以我们需要自己实现一个堆叠方法,下面就是模板,可以实现我们想要的效果'
def stackImages(scale,imgArray):
rows = len(imgArray)
cols = len(imgArray[0])
rowsAvailable = isinstance(imgArray[0], list)
width = imgArray[0][0].shape[1]
height = imgArray[0][0].shape[0]
if rowsAvailable:
for x in range ( 0, rows):
for y in range(0, cols):
if imgArray[x][y].shape[:2] == imgArray[0][0].shape [:2]:
imgArray[x][y] = cv2.resize(imgArray[x][y], (0, 0), None, scale, scale)
else:
imgArray[x][y] = cv2.resize(imgArray[x][y], (imgArray[0][0].shape[1], imgArray[0][0].shape[0]), None, scale, scale)
if len(imgArray[x][y].shape) == 2: imgArray[x][y]= cv2.cvtColor( imgArray[x][y], cv2.COLOR_GRAY2BGR)
imageBlank = np.zeros((height, width, 3), np.uint8)
hor = [imageBlank]*rows
hor_con = [imageBlank]*rows
for x in range(0, rows):
hor[x] = np.hstack(imgArray[x])
ver = np.vstack(hor)
else:
for x in range(0, rows):
if imgArray[x].shape[:2] == imgArray[0].shape[:2]:
imgArray[x] = cv2.resize(imgArray[x], (0, 0), None, scale, scale)
else:
imgArray[x] = cv2.resize(imgArray[x], (imgArray[0].shape[1], imgArray[0].shape[0]), None,scale, scale)
if len(imgArray[x].shape) == 2: imgArray[x] = cv2.cvtColor(imgArray[x], cv2.COLOR_GRAY2BGR)
hor= np.hstack(imgArray)
ver = hor
return ver
def empty(a):
pass
image=cv2.imread('puke.jfif')
'============ 颜色检测(对想要的颜色进行检测)============='
cv2.namedWindow("TrackBars") # 创建一个窗口
cv2.resizeWindow("TrackBars", 640, 240) # 改变窗口的形状
cv2.createTrackbar("Hue Min", "TrackBars", 0, 180, empty) # 绑定滑动条和窗口,定义滚动条的数值
cv2.createTrackbar("Hue Max", "TrackBars", 0, 255, empty) # 第三个参数是默认值(打开图像的默认值),第四个参数是滑条的最大值
cv2.createTrackbar("Sat Min", "TrackBars", 0, 255, empty)
cv2.createTrackbar("Sat Max", "TrackBars", 0, 255, empty)
cv2.createTrackbar("Val Min", "TrackBars", 0, 255, empty)
cv2.createTrackbar("Val Max", "TrackBars", 0, 255, empty)
while True:
img = image
# 色调(H)、饱和度(S)和明度(V)
imgHSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h_min = cv2.getTrackbarPos("Hue Min", "TrackBars") # 得到滑动条的数值
h_max = cv2.getTrackbarPos("Hue Max", "TrackBars")
s_min = cv2.getTrackbarPos("Sat Min", "TrackBars")
s_max = cv2.getTrackbarPos("Sat Max", "TrackBars")
v_min = cv2.getTrackbarPos("Val Min", "TrackBars")
v_max = cv2.getTrackbarPos("Val Max", "TrackBars")
print(h_min, h_max, s_min, s_max, v_min, v_max)
lower = np.array([h_min, s_min, v_min])
upper = np.array([h_max, s_max, v_max])
#lower指的是图像中低于这个lower的值,图像值变为0
#upper指的是图像中高于这个upper的值,图像值变为0
#而在lower~upper之间的值变成255
#这样就是二值图像了
mask = cv2.inRange(imgHSV, lower, upper)
# 按位和进行计算
imgResult = cv2.bitwise_and(img, img, mask=mask)
#将图像叠加在一起
imgStack = stackImages(0.6, ([img, imgHSV], [mask, imgResult]))
cv2.imshow("Stacked Images", imgStack)
cv2.waitKey(1)
当上述操作完成后,我们就可以通过启动摄像头,然后拿出自己的彩色笔,通过调制彩色笔,让中意的颜色,显示出来:
4.启动摄像头的代码:
cap = cv2.VideoCapture(0)
while True:
success, img = cap.read()
#左右镜像交换
img = cv2.flip(img, 1)
cv2.imshow("Result", img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
5.当启动摄像头后,把上述代码综合在一起运行:
录制红色
显示绿色
其代码是:
import cv2
import numpy as np
'如果单纯地使用numpy的函数进行堆叠,对不同大小和不同通道的图像是无法进行堆叠的,所以我们需要自己实现一个堆叠方法,下面就是模板,可以实现我们想要的效果'
def stackImages(scale,imgArray):
rows = len(imgArray)
cols = len(imgArray[0])
rowsAvailable = isinstance(imgArray[0], list)
width = imgArray[0][0].shape[1]
height = imgArray[0][0].shape[0]
if rowsAvailable:
for x in range ( 0, rows):
for y in range(0, cols):
if imgArray[x][y].shape[:2] == imgArray[0][0].shape [:2]:
imgArray[x][y] = cv2.resize(imgArray[x][y], (0, 0), None, scale, scale)
else:
imgArray[x][y] = cv2.resize(imgArray[x][y], (imgArray[0][0].shape[1], imgArray[0][0].shape[0]), None, scale, scale)
if len(imgArray[x][y].shape) == 2: imgArray[x][y]= cv2.cvtColor( imgArray[x][y], cv2.COLOR_GRAY2BGR)
imageBlank = np.zeros((height, width, 3), np.uint8)
hor = [imageBlank]*rows
hor_con = [imageBlank]*rows
for x in range(0, rows):
hor[x] = np.hstack(imgArray[x])
ver = np.vstack(hor)
else:
for x in range(0, rows):
if imgArray[x].shape[:2] == imgArray[0].shape[:2]:
imgArray[x] = cv2.resize(imgArray[x], (0, 0), None, scale, scale)
else:
imgArray[x] = cv2.resize(imgArray[x], (imgArray[0].shape[1], imgArray[0].shape[0]), None,scale, scale)
if len(imgArray[x].shape) == 2: imgArray[x] = cv2.cvtColor(imgArray[x], cv2.COLOR_GRAY2BGR)
hor= np.hstack(imgArray)
ver = hor
return ver
def empty(a):
pass
cap=cv2.VideoCapture(0)
'============ 颜色检测(对想要的颜色进行检测)============='
cv2.namedWindow("TrackBars") # 创建一个窗口
cv2.resizeWindow("TrackBars", 640, 240) # 改变窗口的形状
cv2.createTrackbar("Hue Min", "TrackBars", 0, 180, empty) # 绑定滑动条和窗口,定义滚动条的数值
cv2.createTrackbar("Hue Max", "TrackBars", 0, 255, empty) # 第三个参数是默认值(打开图像的默认值),第四个参数是滑条的最大值
cv2.createTrackbar("Sat Min", "TrackBars", 0, 255, empty)
cv2.createTrackbar("Sat Max", "TrackBars", 0, 255, empty)
cv2.createTrackbar("Val Min", "TrackBars", 0, 255, empty)
cv2.createTrackbar("Val Max", "TrackBars", 0, 255, empty)
while True:
success,image=cap.read()
img = image
# 色调(H)、饱和度(S)和明度(V)
imgHSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h_min = cv2.getTrackbarPos("Hue Min", "TrackBars") # 得到滑动条的数值
h_max = cv2.getTrackbarPos("Hue Max", "TrackBars")
s_min = cv2.getTrackbarPos("Sat Min", "TrackBars")
s_max = cv2.getTrackbarPos("Sat Max", "TrackBars")
v_min = cv2.getTrackbarPos("Val Min", "TrackBars")
v_max = cv2.getTrackbarPos("Val Max", "TrackBars")
print(h_min, h_max, s_min, s_max, v_min, v_max)
lower = np.array([h_min, s_min, v_min])
upper = np.array([h_max, s_max, v_max])
#lower指的是图像中低于这个lower的值,图像值变为0
#upper指的是图像中高于这个upper的值,图像值变为0
#而在lower~upper之间的值变成255
#这样就是二值图像了
mask = cv2.inRange(imgHSV, lower, upper)
# 按位和进行计算
imgResult = cv2.bitwise_and(img, img, mask=mask)
#将图像叠加在一起
imgStack = stackImages(0.6, ([img, imgHSV], [mask, imgResult]))
cv2.imshow("Stacked Images", imgStack)
cv2.waitKey(1)
当上述操作都完成后,我们就可以记录自己调制的参数值,如代码:
#自己通过定义的hsv值进行设置
myColors = [[15,182,84,245,255,255],
[112,104,133,255,255,255]]
6.接下来就是opencv常用的寻找轮廓,绘制轮廓,绘制矩形框,添加文本的常用操作。
①先定义了一个寻找轮廓的函数,通过这个来返回一个坐标:
def getContours(img):
#img是二值图像
contours,hierarchy = cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
x,y,w,h = 0,0,0,0
for cnt in contours:
#轮廓的面积
area = cv2.contourArea(cnt)
if area>200:
#cv2.drawContours(imgResult, cnt, -1, (255, 0, 0), 3)
peri = cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,0.02*peri,True)
x, y, w, h = cv2.boundingRect(approx)
return x+w//2,y
②然后定义一个寻找颜色的函数,通过这个函数就可以过滤出只剩我们想要颜色的视频,并通过索引将其rgb的值放入我们后续画的圈的参数中:
def findColor(img,myColors,myColorValues):
#将图片转换成hsv格式
imgHSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
count = 0
newPoints=[]
for color in myColors:
lower = np.array(color[0:3])
upper = np.array(color[3:6])
mask = cv2.inRange(imgHSV,lower,upper)#获取掩膜
x,y=getContours(mask)#进行轮廓寻找
cv2.circle(imgResult,(x,y),25,myColorValues[count],cv2.FILLED)
if x!=0 and y!=0:
newPoints.append([x,y,count])
count +=1
#cv2.imshow(str(color[0]),mask)
if newPoints==[]:
print('没有发现绘画笔')
else:
print(newPoints)
return newPoints
③通过坐标点在图像上绘制带有颜色的实心圆圈:
def drawOnCanvas(myPoints,myColorValues):
for point in myPoints:
#对我们的点进行
cv2.circle(imgResult, (point[0], point[1]), 25, myColorValues[point[2]], cv2.FILLED)
※※完成上述操作后,就可以实现整个项目的基本功能了,其效果如下:
整体效果
整个项目的代码如下:
import cv2
import numpy as np
#设置视频的宽和高
frameWidth = 1080
frameHeight = 640
cap = cv2.VideoCapture(0)
#改变视频的宽和高
cap.set(3, frameWidth)
cap.set(4, frameHeight)
#自己通过定义的hsv值进行设置
myColors = [[15,182,84,245,255,255],
[112,104,133,255,255,255]]
#颜色的值
myColorValues = [[0,255,0],[147,20,255]]
myPoints = [] ## [x , y , colorId ]
#通过获取对应的轮廓,然后获得纵标,在坐标处标记,并将这个点的坐标存储
def findColor(img,myColors,myColorValues):
#将图片转换成hsv格式
imgHSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
count = 0
newPoints=[]
for color in myColors:
lower = np.array(color[0:3])
upper = np.array(color[3:6])
mask = cv2.inRange(imgHSV,lower,upper)#获取掩膜
x,y=getContours(mask)#进行轮廓寻找
cv2.circle(imgResult,(x,y),25,myColorValues[count],cv2.FILLED)
if x!=0 and y!=0:
newPoints.append([x,y,count])
count +=1
#cv2.imshow(str(color[0]),mask)
if newPoints==[]:
print('没有发现绘画笔')
else:
print(newPoints)
return newPoints
#通过寻找轮廓,然后返回最小轮廓处理后的坐标值
def getContours(img):
#img是二值图像
contours,hierarchy = cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
x,y,w,h = 0,0,0,0
for cnt in contours:
#轮廓的面积
area = cv2.contourArea(cnt)
if area>200:
#cv2.drawContours(imgResult, cnt, -1, (255, 0, 0), 3)
peri = cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,0.02*peri,True)
x, y, w, h = cv2.boundingRect(approx)
return x+w//2,y
#对我们存储的点按照我们的颜色表进行标记
def drawOnCanvas(myPoints,myColorValues):
for point in myPoints:
#对我们的点进行
cv2.circle(imgResult, (point[0], point[1]), 25, myColorValues[point[2]], cv2.FILLED)
while True:
success, img = cap.read()
#左右镜像交换
img = cv2.flip(img, 1)
imgResult = img.copy()
newPoints = findColor(img, myColors,myColorValues)
#将标记的点放入列表中
if len(newPoints)!=0:
for newP in newPoints:
myPoints.append(newP)
#对相应的点输入进我们上面定义好的函数
if len(myPoints)!=0:
drawOnCanvas(myPoints,myColorValues)
cv2.imshow("Result", imgResult)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
仅供学习参考,如有不足,敬请指正!