opencv项目6----AI绘画(隔空绘画)
想不想实现不触碰电脑屏幕,只是在空中拿着彩笔绘画,就可以把对应的颜色画到屏幕中去?
今天这个项目就可以帮助你实现该功能,只需要你会使用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
仅供学习参考,如有不足,敬请指正!
codecreateappsatnumpyide摄像头canvacanvasvst图片转换图像处理pythonurl