[小脚本] 基于opencv 的绿幕抠图

您所在的位置:网站首页 抠图人物边缘 [小脚本] 基于opencv 的绿幕抠图

[小脚本] 基于opencv 的绿幕抠图

2023-09-12 02:13| 来源: 网络整理| 查看: 265

网上有一些 基于 opencv-python 的绿幕抠图算法,大多比较简单,只写明了最简单的原理,比如就是选择指定范围的颜色,然后在这个范围内的就抠掉。

但是简单的这样有一些问题,就是比如: 1)有些区域会抠出洞 2)边缘扣不干净,而且会存在锯齿状结果。

解决方案: 1)使用图像闭运算 2)需要求出一个离散数值作为抠图通道权重,而非 0 / 1。 可以用颜色的距离作为这个权重,这样权重在边缘应该会呈现过渡分布。

但是解决方案1),会带来一个问题,就是如果绿幕正好是个小洞(比如OK的手指),这样会带来问题。

最终变做实验边写代码,就有了下面我整理的代码,其实也是作为参考,可能对于不同的任务还得继续调 代码中主要看 get_mask_v3 思路是找到绿幕分割边界地方(mask_b),这部分应该选择过度更平滑的get_mask,容易去掉绿色,也不容易出现锯齿。具体实现是使用三级mask检测,具体可以看代码。

from _chj.comm.pic import * from _chj.comm.video import * os.chdir( os.path.split( os.path.realpath( sys.argv[0] ) )[0] ) # rbg: rgb(0, 216, 0) def main(): f1_base() def f1_base(): cls_w_rgb = Cls_video() cls_w_a = Cls_video() cls_w_rgb.task_comm( 1, ["tmp/res_rgb", 30, None] ) cls_w_a.task_comm( 1, ["tmp/res_a", 30, None] ) cls_read = Cls_video_read().init_files(["a.mp4"]) while True: st, imgs = cls_read.read_imgs() if st: break img = imgs[0] mask = get_mask_v3(img) #cv.imwrite("tmp/m.jpg", mask) img = get_mask_img(img, mask, 0) exit() cls_w_rgb.task_comm(2, img) cls_w_a.task_comm(2, mask) cls_w_rgb.ffmpeg_aug="-shortest -b:v 2000k" cls_w_a.ffmpeg_aug="-shortest -b:v 200k" cls_w_rgb.task_comm(3) cls_w_a.task_comm(3) print(122) def img_open(img, size=5, it=3): kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (size, size)) # 矩形结构 open = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel, iterations=it) return open def img_close(img, size=5, it=3): kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (size, size)) # 矩形结构 close = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel, iterations=it) return close def img_dilate(img, size=5, it=3): k = np.ones((size, size), dtype=np.uint8) return cv2.dilate(img, k, it) def img_erode(img, size=5, it=3): k = np.ones((size, size), dtype=np.uint8) return cv2.erode(img, k, it) # 非绿幕为1 def get_mask(img, mmin, mmax, minGreen): #, minGreen = np.array([0, 255, 0]), maxGreen = np.array([0, 255, 0]) #hsv = cv2.cvtColor(opencv, cv2.COLOR_RGB2HSV) #mask = cv2.inRange(img, minGreen, maxGreen) dis = np.sqrt( np.power(img - minGreen, 2).mean(-1) ) #dis = np.power(img - minGreen, 2).sum(-1) #.mean(-1) #dis = np.abs(img - minGreen).sum(-1) #.mean(-1) #mmin, mmax = 5, 400 # 后面这个参数越大,绿幕越小,但是实际上内部已经被平滑了 dis[ dis1 ] = 1 ids=dis*255 #print(dis[50:60, 50:60]) mask = (dis*255).astype(np.uint8) #showimg(mask) #mask = img_open( mask, 5, 2 ) ##mask = img_close( mask, 3, 3 ) #showimg(mask) return mask def get_mask_img(img, mask, isshow=0): mr = mask / 255.0 img = mr[...,np.newaxis] * img + (1-mr)[...,np.newaxis] #img[ mask.astype(np.bool_) ] = 0 img = img.astype(np.uint8) if isshow: showimg(img) return img # 调试 v1 时想到的 def get_mask_v2_1(img): color = np.array([0, 255, 0] ) mask = get_mask( img, 5, 400, color ) # 需要调的 mask = img_open( mask, 5, 2 ) mask4 = get_mask( img, 5, 20, color ) # 真正要的参数 mask4 = img_open( mask4, 3, 2 ) # 去掉一些噪声 mask1 = img_erode(mask4, 5, 5) mask2 = img_dilate(mask4, 5, 5) mask3 = mask1 != mask2 # 找到边界的地方,这些地方用更加缓和的mask for e in [mask4, mask, mask1, mask2, mask3.astype(np.uint8)*255]: get_mask_img(img, e, 1) #mask5 = img_close( mask, 5, 2 ) mask4[mask3] = mask[mask3] #mask4[mask1==255] = mask5[ mask1==255 ] #mask4 = img_close( mask4, 3, 2 ) # 防止中间有洞 get_mask_img(img, mask4, 1) #exit() return mask4 # 1)要考虑过渡 # 2)要想使得能检测出更多的绿色范围,那么 get_mask 的第三个参数非常重要 # 3)考虑到第三个参数太大虽然检测的绿色多,但是容易引起错误, # 因此需要确定一个边界,对于边界上的用第三个值大些的, 而边界通过 膨胀与腐蚀确定,但是应该时通过第三个值小的确定, # 但是值太小,有些绿色检测不出来,所里这里是个矛盾点。 # 可能更合理的是采用三个 mask,即第三个值分级 def get_mask_v2(img): color = np.array([0, 255, 0] ) mask = get_mask( img, 3, 150, color ) # 需要调的 mask = img_open( mask, 5, 2 ) mask4 = get_mask( img, 3, 30, color ) # 真正要的参数 mask4[mask4!=255] = 0 mask4 = img_open( mask4, 3, 2 ) # 去掉一些噪声 mask1 = img_erode(mask4, 7, 5) mask2 = img_dilate(mask4, 7, 5) mask3 = mask1 != mask2 # 找到边界的地方,这些地方用更加缓和的mask for e in [mask4, mask, mask1, mask2, mask3.astype(np.uint8)*255]: get_mask_img(img, e, 1) mask5 = img_close( mask, 5, 2 ) # 这个可选 mask4[mask3] = mask[mask3] mask4[mask1==255] = mask5[ mask1==255 ] #mask4 = img_close( mask4, 3, 2 ) # 防止中间有洞 get_mask_img(img, mask4, 1) #exit() return mask4 def get_mask_v3(img): color = np.array([0, 255, 0] ) mask1 = get_mask( img, 3, 150, color ) # 用于检测更多的边缘绿色 mask2 = get_mask( img, 3, 50, color ) # 用于确定边界 mask3 = get_mask( img, 3, 20, color ) # 把纯绿色的去掉 mask1 = img_open( mask1, 5, 2 ) mask2 = img_open( mask2, 5, 2 ) mask3 = img_open( mask3, 5, 2 ) #mask2[mask2!=255] = 0 # 可选 maskb1 = img_erode(mask2, 7, 5) maskb2 = img_dilate(mask2, 7, 5) maskb = maskb1 != maskb2 #for e in [mask4, mask, mask1, mask2, mask3.astype(np.uint8)*255]: # get_mask_img(img, e, 1) mask5 = img_close( mask1, 5, 2 ) # 这个可选 mask3[maskb] = mask1[maskb] mask3[maskb1==255] = mask5[ maskb1==255 ] #mask4 = img_close( mask4, 3, 2 ) # 防止中间有洞 get_mask_img(img, mask3, 1) #exit() return mask3 if __name__=="__main__": main()


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3