当前位置:AIGC资讯 > AIGC > 正文

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

仅供学习参考,如有不足,敬请指正!

更新时间 2023-11-30