用 Opencv 做一件“隐身衣”

    技术2023-09-20  108

    文章目录

    项目介绍实现步骤代码说明1、导入需要的库2、定义绘图函数3、导入前景图片和背景图片4、对两张图片进行水平(镜像)翻转5、将前景图片从 BGR 转换到 HSV 空间6、检测红色区域7、处理红色区域8、融合前景和背景 完整代码参考资料

    项目介绍

    在这个项目中,我们会使用 Opencv 来将红色物体变成透明状,以此来达到隐身效果,如下面这张动图所示:

    实现步骤

    找到一个只含有背景的帧;检测红色物体的位置;将红色区域用背景中的相同区域代替。

    代码说明

    我们先用下面这两张图片(前者为背景,后者为前景)来举例说明。 在实现隐身效果之后,得到的图片应该为:

    1、导入需要的库

    import cv2 import numpy as np

    2、定义绘图函数

    def cv_show(name,img): cv2.imshow(name, img) cv2.waitKey(0) cv2.destroyAllWindows()

    3、导入前景图片和背景图片

    background = cv2.imread('background.jpg') img = cv2.imread('foreground.jpg')

    4、对两张图片进行水平(镜像)翻转

    因为调用摄像头之后显示的画面和真实的画面是镜像的,所以这里要先调整一下。

    background = np.flip(background, axis=1) img = np.flip(img, axis=1)

    这里使用的 np.flip 函数中的 axis 指的是将这张图片基于哪个维度翻转,如果 axis=0,那么就是上下翻转;如果 axis=1,那么就是左右翻转;如果 axis=2,那么就是基于 RGB 三通道之间的转变。

    5、将前景图片从 BGR 转换到 HSV 空间

    如果基于 RGB 空间检测红色很困难,因为红色是 RGB 三通道综合起来获得的。所以我们要先将图片转到 HSV 空间。HSV 对颜色的定义更接近人的视觉系统。

    HSV空间各个参数如下:

    色调(Hue):用角度度量,取值范围为 0°-360°。可以认为 0° 对应于红色,120° 对应于绿色,240° 对应于蓝色。饱和度(Saturation):饱和度表示颜色的强度和纯度。明度(Value):表示颜色的明暗程度,取值范围为 0.0 (黑色)~1.0 (白色)。

    颜色仅由色调(Hue)决定。在 OpenCV 中色调不是 0° 到 360°,而是被量化为 0° 到 180°。

    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

    6、检测红色区域

    基于 OpenCV 中的 inRange 函数筛选颜色。其中红色 H 值的范围为 0°-10° 和 170°-180°,以避免发现皮肤为红色。因为红布应该是高度饱和的红色。所以 S 值设定为 120 到 255。V 值设置为 105 到 255。根据以上能够获得红色 H 值的范围为 0°-10° 和 170°-180° 的两个红色区域,然后合并这两个区域。

    lower_red = np.array([0,120,105]) upper_red = np.array([10,255,255]) mask1 = cv2.inRange(hsv, lower_red, upper_red) lower_red = np.array([170,120,105]) upper_red = np.array([180,255,255]) mask2 = cv2.inRange(hsv,lower_red,upper_red) mask = mask1 + mask2

    合并后的区域如下图所示:

    7、处理红色区域

    因为上一步中提取到的红色区域中掺入了少量面部的红色区域,所以这里用形态学操作去掉这些区域。

    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, np.ones((3,3),np.uint8),iterations=2) mask = cv2.dilate(mask,np.ones((3,3),np.uint8),iterations = 1) mask_inverse = cv2.bitwise_not(mask)

    最终得到的红色区域为: 可以看到,经过形态学操作后,面部被识别出的区域已经被去除掉了。

    8、融合前景和背景

    res1 = cv2.bitwise_and(background, background, mask=mask) res2 = cv2.bitwise_and(img, img, mask=mask_no) final_output = cv2.addWeighted(res1, 1, res2, 1, 0)

    res1、res2 和 final_output 的图片分别为:

    完整代码

    import cv2 import numpy as np cap = cv2.VideoCapture(0) # 先获取背景,取摄像头拍到的第 30 帧为背景图 for i in range(30): ret, background = cap.read() background = np.flip(background, axis=1) # 镜像翻转 while True: ret, img = cap.read() if not ret: break count+=1 img = np.flip(img, axis=1) hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) lower_red = np.array([0,120,105]) upper_red = np.array([10,255,255]) mask1 = cv2.inRange(hsv, lower_red, upper_red) lower_red = np.array([170,120,105]) upper_red = np.array([180,255,255]) mask2 = cv2.inRange(hsv, lower_red, upper_red) mask = mask1 + mask2 mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, np.ones((3,3),np.uint8), iterations=2) mask = cv2.dilate(mask, np.ones((3,3),np.uint8), iterations = 1) mask_inverse = cv2.bitwise_not(mask) res1 = cv2.bitwise_and(background, background, mask=mask) res2 = cv2.bitwise_and(img, img, mask=mask_inverse) final_output = cv2.addWeighted(res1, 1, res2, 1, 0) cv2.imshow('Magic !!!',final_output) if cv2.waitKey(1) & 0xff == ord('q'): # 按 q 退出 break cap.release() cv2.destroyAllWindows()

    参考资料

    luohenyueji

    Processed: 0.009, SQL: 10