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

news/2024/9/29 9:57:55/

文章目录

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

前言

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

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

色度抠图技术(Chroma Keying)

基本原理

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

数据准备

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

代码实现

原图
在这里插入图片描述

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

python">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)

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

掩码反转背景替换

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

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

python">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)

在这里插入图片描述

第一版代码

python">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中可能还有更快的方式,欢迎大佬来指点),但是如果我们处理的是一个视频流或直播,需要一种更加高效的方式处理视频流,来提高效率或者保证视频帧的稳定产生。

代码优化

废话不多说直接上代码

python">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将两个图片进行融合。其实就是或操,图片中被舍弃的部分是“黑色”,两张图片中取非“黑色”部分

python"># 使用掩码提取前景,只保留非绿色的部分
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一些’膨胀’、’腐蚀‘的形态学操作,还可以使用一些 ’高斯模糊‘,’边缘检测‘等方式

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

在这里插入图片描述


http://www.ppmy.cn/news/1531874.html

相关文章

深入理解 Java 中的 Switch 语句

深入理解 Java 中的 Switch 语句 在 Java 编程中,switch 语句是一种强大的控制结构,能够根据表达式的值选择执行不同的代码块。本文将详细介绍 switch 的基本语法、使用案例、注意事项以及与 if 语句的选择。 基本语法 switch 语句的基本语法如下&#…

【Java SE】初遇Java,数据类型,运算符

🔥博客主页🔥:【 坊钰_CSDN博客 】 欢迎各位点赞👍评论✍收藏⭐ 1. Java 概述 1.1 Java 是什么 Java 是一种高级计算机语言,是一种可以编写跨平台应用软件,完全面向对象的程序设计语言。Java 语言简单易学…

计算机毕业设计之:微信小程序的校园闲置物品交易平台(源码+文档+讲解)

博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…

基于微信小程序的网上商城+ssm(lw+演示+源码+运行)

摘 要 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,微信小程序被用户普遍使用,为方便用户能够可以…

在C#中使用JSON

JSON简介 1. 什么是 JSON? JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。它的语法基于 JavaScript 对象表示法,简单、易读,同时被许多编程语言支持。尽管它来源于 JavaScript,但它并不依…

python 实现harmonic series调和级数算法

harmonic series调和级数算法介绍 调和级数(Harmonic Series)是一个在数学中非常重要的级数,其形式为无穷级数: H n ∑ k 1 n 1 k H_n\sum_{k1}^{n}\frac{1}{k} Hn​k1∑n​k1​ 其中, 𝑛 n 是正整数。…

linux命令之firewall-cmd用法

firewall-cmd Linux上新用的防火墙软件,跟iptables差不多的工具 补充说明 firewall-cmd 是 firewalld的字符界面管理工具,firewalld是centos7的一大特性,最大的好处有两个:支持动态更新,不用重启服务;第…

Font Awesome 手势图标

Font Awesome 手势图标 Font Awesome 是一个广泛使用的图标库,它为网页设计师和开发者提供了一系列高质量的图标。这些图标涵盖了从基本的网页元素到复杂的符号和手势,可以轻松地集成到各种网页和应用中。在本文中,我们将重点介绍 Font Awesome 中的手势图标,探讨它们的应…