虚拟画笔 - OpenCV


  • title: 虚拟画笔 - 基于OpenCV
  • date: 2022-02-27
  • categories: - python - OpenCV
  • tags: [OpenCV, 虚拟画笔,计算机视觉]
  • index_img: https://pic1.zhimg.com/v2-32c4194eadd450868842042b010d3efd_720w.jpg?source=172ae18bt

文章目录

  • 一、画面获取
  • 二、颜色检测
  • 三、轮廓获取
  • 四、绘制图案
  • 五、总结

本项目用到的核心技术是颜色检查以及轮廓/形状检查

一、画面获取

​ 对于这个项目,我们首先需要找到我们摄像头画面中的颜色,将RGB通道转换为HSV颜色模型并获取图像的轮廓。然后我们可以在图像轮廓中心放置绘制点,从而进行绘制。不论是哪一种颜色,都可以找到其轮廓,并放置绘制点进行图案的绘制。

​ 因此, 我们首先需要的是通过OpenCV库函数获取连接笔记本的摄像头,并将获取的画面进行三通道分离,从而检测出特定颜色的轮廓.

获取摄像头画面的代码

cap = cv2.VideoCapture(0)  # 这里将视频的路径填入摄像头ID即可, 0使用默认摄像头(笔记本/内置)
cap.set(3,640)   #宽, 编号为3   3:宽度, 4:高度 10:亮度, 11:对比度
cap.set(4, 480)  #高, 编号为4
cap.set(10,100)  #亮度100, 编号为10
while True:
     success, img = cap.read()
     cv2.imshow("Video", img) 
     if(cv2.waitKey(1) & 0xFF == ord('q')):
         break
        

二、颜色检测

​ 获取画面之后,接下来我们要做的便是找到画面中特定的颜色(期望识别的颜色,通过设置操纵杆自己调)

定义一个函数来找到特定的颜色

def findColor(img):
    imgHSV = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
    lower = np.array([h_min,s_min,v_min])
    upper = np.array([h_max,s_max,v_max])
    mask = cv2.inRange(imgHSV,lower,upper)
    cv2.imshow("img",mask)

这里我们可以定义一个数组来存放不同颜色的HSV值以及灰度。再改写并调用此函数,从而可以实现多个颜色的画笔进行绘制。

首先通过颜色检测来获取不同的颜色HSV值:

import cv2
import numpy as np

frameWidth = 640*0.5
frameHeight = 480*0.5
cap = cv2.VideoCapture(0)
cap.set(3, frameWidth)
cap.set(4, frameHeight)
cap.set(10,150)

def empty(a):
    pass

cv2.namedWindow("HSV")   ########## 初始化
cv2.resizeWindow("HSV",640,240)
cv2.createTrackbar("HUE Min","HSV",0,179,empty)
cv2.createTrackbar("SAT Min","HSV",0,255,empty)
cv2.createTrackbar("VALUE Min","HSV",0,255,empty)
cv2.createTrackbar("HUE Max","HSV",179,179,empty)
cv2.createTrackbar("SAT Max","HSV",255,255,empty)
cv2.createTrackbar("VALUE Max","HSV",255,255,empty)

while True:

    o, img = cap.read()
    imgHsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
# 添加数值杆, 让希望的颜色变为白色(以所期望的颜色作为笔头)    颜色越鲜艳越好
    h_min = cv2.getTrackbarPos("HUE Min","HSV")
    h_max = cv2.getTrackbarPos("HUE Max", "HSV")
    s_min = cv2.getTrackbarPos("SAT Min", "HSV")
    s_max = cv2.getTrackbarPos("SAT Max", "HSV")
    v_min = cv2.getTrackbarPos("VALUE Min", "HSV")
    v_max = cv2.getTrackbarPos("VALUE Max", "HSV")
    print(h_min)

    lower = np.array([h_min,s_min,v_min])
    upper = np.array([h_max,s_max,v_max])
    mask = cv2.inRange(imgHsv,lower,upper)
    result = cv2.bitwise_and(img,img, mask = mask)

    mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
    hStack = np.hstack([img,mask,result])
    #cv2.imshow('Original', img)
    #cv2.imshow('HSV Color Space', imgHsv)
    #cv2.imshow('Mask', mask)
   #cv2.imshow('Result', result)
    cv2.imshow('Horizontal Stacking', hStack)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

以上代码运行后就可以通过调节HSV操作杆来识别镜头中的颜色(期望识别的颜色)

虚拟画笔 - OpenCV_第1张图片

虚拟画笔 - OpenCV_第2张图片

如果颜色识别很多噪点,是因为光线太暗。也不要在有颜色的灯光下进行识别,否则只能在调节的时候所处的环境下才能够成功识别。当然,最好是在特定的灯光下,如果是变化的光线比如白天,调节得到的值下次用还得调(亲身经历)

识别好颜色后, 通过记录操作杆的数值,并将其放入myColors数组中,进行后续操作。

myColors = [[30,108,114,179,255,255] # 绿色
           ] # 颜色数组,如果有其他颜色,可以添加 


# 画笔的颜色
myColorValues = [[51,153,255],    ##BGR not RGB
                 [255,0,255],   
                 [0,255,0]]

通过获取到特定的颜色HSV值,接下来可以进行颜色检测以及轮廓的获取了

注意:为了固定识别颜色,这里是通过获取到的HSV值,来识别新开镜头中的颜色,从而对其轮廓进行识别,读者切勿混淆上述手动获取的值和接下来的自动检测

  • 颜色检测
# 定义一个检测颜色的函数
def findColor(img,myColors,myColorValues):
    imgHSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    count = 0 # 用count来记录 当前颜色
    newPoints = []


    # 循环检测myColor数组中的颜色
    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),10, myColorValues[count], cv2.FILLED) # 绘制圆点
        if x!= 0 and y != 0:
            newPoints.append([x,y,count])# 这种条件下添加到我们的点集
        count += 1


        #cv2.imshow(str(color[0]),mask)
    return newPoints

三、轮廓获取

# 定义一个函数来获取轮廓
def getContours(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)
        # print(area)

        #给像素点大于500的绘制轮廓
        if area>500: # 500像素
          #  cv2.drawContours(imgResult,cnt,-1,(255,0,0),3) # 蓝色, 厚度3 绘制轮廓
            peri = cv2.arcLength(cnt,True)#求周长
            # print(peri)
            approx = cv2.approxPolyDP(cnt,0.02*peri,True) #  拟合计算拐点 0.02分辨率
            # print(len(approx))
            # objCor = len(approx)
            # #检测边框
            x, y, w, h = cv2.boundingRect(approx) # x,y是起点, w,h 是宽高

    return x+w//2,y  # 返回中心点的最高点
  • 效果
    虚拟画笔 - OpenCV_第3张图片

获取轮廓之后,可以根据实际情况设置特定的一个点来作为笔尖。比如这里可以设置矩形的中心点来作为虚拟画笔的笔尖。为了便于处理,我设置的是图案的顶点作为笔尖
虚拟画笔 - OpenCV_第4张图片

四、绘制图案

总所周知,任意的曲线都可以看作是无数的点连接而成。因此在获得图像中的一个特殊点之后,便可以将这个点记录到一个数组中,通过不断绘制这个数组中的点,就可以达到绘制线条的效果,从而实现虚拟画笔的效果

  • 绘制单个圆点
# 定义绘制画布
def drawOnCanvas(myPoints,myColorValues):
    for point in myPoints:
        cv2.circle(imgResult, (point[0], point[1]), 10, myColorValues[point[2]], cv2.FILLED)  # 绘制圆点选择画笔颜色
  • 循环获取当前画面
while True:
    success, img = cap.read()
    img = cv2.flip(img,240)
    imgResult = img.copy()
    newPoints = findColor(img,myColors, myColorValues)
    if len(newPoints) != 0: # 拆分后放入mypints
        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

  • 效果

虚拟画笔 - OpenCV_第5张图片
虚拟画笔 - OpenCV_第6张图片

五、总结

​ 通过以上的操作,虽然能够实现虚拟画笔的效果,但是仍然有许多不足之处。比如绘制点的频率还没有调节,当画笔移动速度过快,将会导致线条不连续,形成一个个小点。比如下述结果:

虚拟画笔 - OpenCV_第7张图片

​ 此外,只要画笔出现在镜头中,就能够绘制。这样会导致控制何时停何时开始,让绘制图形的操作变得难于控制。并且能够识别多个颜色点,这就导致同意画面能够有多个画笔。因此后续还应该改进这些缺陷,让画笔能够更加人性化。

  • 测试代码(整合)
cap = cv2.VideoCapture(0)  # 这里将视频的路径填入摄像头ID即可, 0使用默认摄像头(笔记本)
cap.set(3,640)   #宽, 编号为3   3:宽度, 4:高度 10:亮度, 11:对比度
cap.set(4, 480)  #高, 编号为4
cap.set(10,150)  #亮度100, 编号为10

# 通过colorPicker.py 找出颜色
# 下面是蓝色,紫色                      [5,107,0,19,255,255],黄色,
# myColors = [ [0,103,118,120,255,255], # 绿色
#             [133,56,0,159,156,255],
#            [57,76,0,100,255,255]]    # 这些是需要检测的颜色
myColors = [[30,108,114,179,255,255]]

# 下面是画笔的颜色
myColorValues = [[51,153,255],    ##BGR  not RGB
                 [255,0,255],
                 [0,255,0]]

#
myPoints =  []   ## [x,y,colorId]



# 定义一个检测颜色的函数
def findColor(img,myColors,myColorValues):
    imgHSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    count = 0 # 用count来记录 当前颜色
    newPoints = []


    # 循环检测mycolor中的颜色数值HSV
    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),5, myColorValues[count], cv2.FILLED) # 绘制圆点
        if x!= 0 and y != 0:
            newPoints.append([x,y,count])# 这种条件下添加到我们的点集
        count += 1


        #cv2.imshow(str(color[0]),mask)
    return newPoints
# 定义一个函数来获取轮廓
def getContours(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)
        # print(area)

        #给像素点大于500的绘制轮廓
        if area>500: # 500像素
          #  cv2.drawContours(imgResult,cnt,-1,(255,0,0),3) # 蓝色, 厚度3 绘制轮廓
            peri = cv2.arcLength(cnt,True)#求周长
            # print(peri)
            approx = cv2.approxPolyDP(cnt,0.02*peri,True) #  拟合计算拐点 0.02分辨率
            # print(len(approx))
            # objCor = len(approx)
            # #检测边框
            x, y, w, h = cv2.boundingRect(approx) # x,y是起点, w,h 是宽高

    return x+w//2,y  # 返回中心点的最高点

# 定义绘制画布
def drawOnCanvas(myPoints,myColorValues):
    for point in myPoints:
        cv2.circle(imgResult, (point[0], point[1]), 10, myColorValues[point[2]], cv2.FILLED)  # 绘制圆点




while True:
    success, img = cap.read()
    img = cv2.flip(img,240)
    imgResult = img.copy()
    newPoints = findColor(img,myColors, myColorValues)
    if len(newPoints) != 0: # 拆分后放入mypints
        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

以上便是本文的全部内容,希望在以后的学习中还能够再不断完善这个项目。

你可能感兴趣的:(杂项,opencv,python,计算机视觉)