当前位置: 首页 > news >正文

玩转图像处理:Python与OpenCV实现高效绿幕背景替换

文章目录

  • 前言
  • 色度抠图技术(Chroma Keying)
    • 基本原理
  • 数据准备
  • 代码实现
  • 性能分析
  • 代码优化
  • 优化后的速度

前言

现阶段绿幕抠图有很多种方式,比如色度抠图(Chroma Keying)、亮度抠图(Luma Keying)、色差抠图(Color Difference Keying),甚至还有绿幕抠图的模型。不同方式的绿幕抠图效果和效率各有差异,在使用时应该根据的自己的业务需求进行选择。

本文主要介绍色度抠图技术
文末有完整代码以及优化后的代码

色度抠图技术(Chroma Keying)

基本原理

色度抠图技术的工作原理基于颜色空间的差异。它通常将输入的图像从RGB(红、绿、蓝)色彩空间转换到更适合于颜色分离的色彩空间,如HSV(色调、饱和度、亮度)或YUV(亮度、色度U、色度V)。然后,系统分析图像中的每个像素,并将其与预设的“键控颜色”(通常是绿色或蓝色)进行比较。通过计算像素和预设颜色的距离或者相似度来分割前景和后景。(后续的代码实践里面会讲解)

数据准备

准备的绿幕图片前景中不能出现和绿幕背景相同的颜色,不然会被当作背景替换掉。
拍摄绿幕图片时,尽量避免绿幕反光导致人物边缘出现绿光的现象。
背景的颜色均匀且无杂物

代码实现

原图
在这里插入图片描述

图片色彩空间转换,前景和后景的分离(掩码图片)

import cv2
import time
import numpy as np
image_path="图片路径"
# 加载图片
image = cv2.imread(image_path)  # image的数据类型是numpy.ndarray,其实就是将每一像素点转换成了[b,g,r]
# 转换到HSV颜色空间
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)   # 后续需要根据这个进行分离
# 如果抠图不理想,可以调整一下色彩的范围
lower_green = np.array([35, 43, 46])    # hsv 绿色最小值
upper_green = np.array([77, 255, 255])  # hsv 绿色最大值# 根据给定的颜色范围创建掩码图片
mask = cv2.inRange(hsv_image, lower_green, upper_green)  
# 显示掩码图片,缩放展示cv2.resize(mask.copy(), (image.shape[1] // 2, image.shape[0] // 2))
cv2.imshow('mask', mask)

掩码图片
白色的部分就是绿幕的部分,黑色的部分就是其他的颜色
在这里插入图片描述

掩码反转背景替换

# 反转掩码,因为我们最后需要的是黑色的部分,所以需要将黑色和白色进行交换
mask_inv = cv2.bitwise_not(mask)  
# 使用掩码提取前景
fg = cv2.bitwise_and(image, image, mask=mask_inv)

在这里插入图片描述
背景替换

background_path = '背景图片路径'
background = cv2.imread(background_path)
# 调整背景大小以匹配前景
bg = cv2.resize(background, (image.shape[1], image.shape[0]))
result = bg.copy()  # 复制一份,也可以不复制,直接在原图上进行修改
# 利用numpy的向量加速,其实就是将前景所对应的像素点替换到背景所对应的像素点上
result[mask_inv == 255] = fg[mask_inv == 255]
cv2.imshow('Green Screen Keying', result)

在这里插入图片描述

第一版代码

import cv2
import time
import numpy as npdef green_screen_keying(image_path, background_path):# 读取图像image = cv2.imread(image_path)background = cv2.imread(background_path)t1 = time.time()# 转换到HSV颜色空间hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)print(type(hsv_image))lower_green = np.array([35, 43, 46])upper_green = np.array([77, 255, 255])mask = cv2.inRange(hsv_image, lower_green, upper_green)cv2.imshow('mask', cv2.resize(mask.copy(), (image.shape[1] // 2, image.shape[0] // 2)))cv2.imwrite("mask.jpg", mask)# 反转掩码mask_inv = cv2.bitwise_not(mask)# 使用掩码提取前景fg = cv2.bitwise_and(image, image, mask=mask_inv)# 调整背景大小以匹配前景cv2.imwrite("fg.jpg", fg)bg = cv2.resize(background, (image.shape[1], image.shape[0]))result = bg.copy()# print(mask_inv)  # 掩膜只有黑色和白色# 利用numpy的向量加速result[mask_inv == 255] = fg[mask_inv == 255]# 显示结果t2 = time.time()print(t2 - t1)cv2.imshow('Green Screen Keying', cv2.resize(result.copy(), (image.shape[1] // 3, image.shape[0] // 3)))cv2.imwrite("result.jpg", result)cv2.waitKey(0)cv2.destroyAllWindows()# 使用示例
green_screen_keying('../img/girl.png', '../img/7869190.jpg')

性能分析

电脑配置
系统:window 10
CPU:AMD Ryzen 5 4600H with Radeon Graphics 3.00 GHz
内存:32G,DDR4 3200MHz
电脑配置比较旧,新款的电脑测出的速度应该要更快。

将上面的代码一些无关的显示和存储操作删除以后,经过多次的测试发现替换一张图片1080x1920的图片大概需要0.078s,其中result[mask_inv == 255] = fg[mask_inv == 255]这一步占了0.055s,占耗时的70%,是一个拖慢效率主主要的步骤。

result[mask_inv == 255] = fg[mask_inv == 255]实际上这个操作已经避免了循环遍历,利用了numpy本身的特性,效率已经算是够快了(numpy中可能还有更快的方式,欢迎大佬来指点),但是如果我们处理的是一个视频流或直播,需要一种更加高效的方式处理视频流,来提高效率或者保证视频帧的稳定产生。

代码优化

废话不多说直接上代码

import time
import cv2
import numpy as npdef green_screen_keying(image_path, background_path):image = cv2.imread(image_path)background = cv2.imread(background_path)t1 = time.time()bg = cv2.resize(background, (image.shape[1], image.shape[0]))hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)lower_green = np.array([36, 25, 25])upper_green = np.array([70, 255, 255])# 创建掩码mask = cv2.inRange(hsv_image, lower_green, upper_green)# 反转掩码mask_inv = cv2.bitwise_not(mask)# 使用掩码提取前景,只保留非绿色的部分fg = cv2.bitwise_and(image, image, mask=mask_inv)# 使用掩码提取背景,只保留原图片中绿色部分background_masked = cv2.bitwise_and(bg, bg, mask=mask)# 将两个图片进行或操作分别保留前景和背景中存在的部分,result = cv2.bitwise_or(fg, background_masked)t2 = time.time()print(t2-t1)green_screen_keying('../img/girl.png', '../img/7869190.jpg')```

前面关于图片掩码的处理都是一样的,主要的区别就是后面前景图片和后景图片融合的部分。
主要改变就是下面的部分,我们利用掩码和反转掩码分别在前景图片和后景图片中取出需要保留的部分,然后在利用opencvbitwise_or将两个图片进行融合。其实就是或操,图片中被舍弃的部分是“黑色”,两张图片中取非“黑色”部分

# 使用掩码提取前景,只保留非绿色的部分
fg = cv2.bitwise_and(image, image, mask=mask_inv)
# 使用掩码提取背景,只保留原图片中绿色部分
background_masked = cv2.bitwise_and(bg, bg, mask=mask) # 
# 将两个图片进行或操作,分别保留前景和背景中存在的部分,
result = cv2.bitwise_or(fg, background_masked)

优化后的速度

一张1080x1920的图片替换绿幕所花费的时间是0.013s,处理一张图片的耗时大概是原来的16.7%,速度是原来的6倍,优化的效果算是比较理想的。

关于背景替换后出现边缘锯齿状的情况,有几个解决方向

  • 一个是在拍摄绿幕的时候避免主体内容收到绿光的污染
  • 提高原始素材的分辨率,更大的素材像素点就更多,即使出现锯齿状,缩放图片以后也在一定程度上缓解。
  • 优化算法,使用opencv一些’膨胀’、’腐蚀‘的形态学操作,还可以使用一些 ’高斯模糊‘,’边缘检测‘等方式

各位看官看官还有更优的方式,欢迎指点。如果对你有所帮助,希望您能点个赞支持一下。

在这里插入图片描述

相关文章:

  • 振弦式土压力计:原理、功能与应用
  • Git 工作区、暂存区和版本库
  • Electron 使用 Nodemon 配置自动重启
  • 免费的高质量、美观的甘特图模板
  • 电子数据交换EDI 835 的处理
  • 【Rockchip系列】官方函数:importbuffer_virtualaddr
  • 渗透测试入门学习——编写python脚本实现对网站登录页面的暴力破解
  • Performance Analysis Kit简介
  • 江科大笔记—LED闪烁 LED流水灯 蜂鸣器
  • 【数据结构笔记】2-3-3 单链表的查找
  • Springboot3保存日志到数据库
  • 移动会议:气膜馆的理想选择—轻空间
  • 设计模式 策略模式(Strategy Pattern)
  • SWAP、AquaCrop、FVCOM、Delft3D、SWAT、R+VIC、HSPF、HEC-HMS......
  • 【JAVA高级】 redis分布式双重加锁(业务校验:防止接口并发调用时数据重复)
  • echarts的各种常用效果展示
  • ES2017异步函数现已正式可用
  • Facebook AccountKit 接入的坑点
  • hadoop入门学习教程--DKHadoop完整安装步骤
  • javascript 哈希表
  • JavaScript对象详解
  • leetcode386. Lexicographical Numbers
  • node-sass 安装卡在 node scripts/install.js 解决办法
  • Quartz初级教程
  • Redis 懒删除(lazy free)简史
  • SpiderData 2019年2月13日 DApp数据排行榜
  • Spring Cloud Feign的两种使用姿势
  • 从0搭建SpringBoot的HelloWorld -- Java版本
  • 订阅Forge Viewer所有的事件
  • - 概述 - 《设计模式(极简c++版)》
  • 基于组件的设计工作流与界面抽象
  • 巧用 TypeScript (一)
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • 如何用纯 CSS 创作一个货车 loader
  • #LLM入门|Prompt#1.8_聊天机器人_Chatbot
  • #传输# #传输数据判断#
  • #数据结构 笔记三
  • $.proxy和$.extend
  • (cos^2 X)的定积分,求积分 ∫sin^2(x) dx
  • (el-Date-Picker)操作(不使用 ts):Element-plus 中 DatePicker 组件的使用及输出想要日期格式需求的解决过程
  • (PySpark)RDD实验实战——取一个数组的中间值
  • (草履虫都可以看懂的)PyQt子窗口向主窗口传递参数,主窗口接收子窗口信号、参数。
  • (差分)胡桃爱原石
  • (二)fiber的基本认识
  • (二)十分简易快速 自己训练样本 opencv级联lbp分类器 车牌识别
  • (附表设计)不是我吹!超级全面的权限系统设计方案面世了
  • (精确度,召回率,真阳性,假阳性)ACC、敏感性、特异性等 ROC指标
  • (强烈推荐)移动端音视频从零到上手(下)
  • (原)Matlab的svmtrain和svmclassify
  • (转)使用VMware vSphere标准交换机设置网络连接
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • .gitignore文件设置了忽略但不生效
  • .NET 5种线程安全集合
  • .Net FrameWork总结
  • .NetCore Flurl.Http 升级到4.0后 https 无法建立SSL连接