百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

Python-OpenCV开发总结替换视频绿幕并重新调整大小和位置

toyiye 2024-06-21 11:55 14 浏览 0 评论

在Python中,使用OpenCV(cv2)替换视频的绿幕背景为新的图片,同时还需要调整透明的视频的大小和位置,首先需要解决抠图的问题,因为是替换视频绿幕,所以视频帧抠图后,还需要确保合成的视频的清晰度,同时需要将原始视频的声音合并到新视频中。


.

1 主要的处理步骤

-

为了实现以上功能,处理过程的主要步骤为:

(1)通过cv2.VideoCapture逐帧读取源视频为图片;

(2)先增加alpha通道,再将图片中的绿幕抠出来,;

(3)使用cv2提供的方法将抠出绿幕的透明图片粘贴到新的背景图片上;

(4)将合成后的图片写入视频流,形成新的视频文件。

(5)通过moviepy读取原视频的声音,添加到新的视频文件中。


.

2 优化图片抠像的效果

-

以上步骤中,第2步是抠图效果的关键,容易踩的坑有以下两个,以及正确的避坑方法如下:


2.1 增加alpha通道

如果没有给图片增加alpha通道,直接做绿幕抠图,结果是绿幕没有了,但是被黑色填充,可谓“才脱虎口,又入狼窝”。为图片增加alpha通道的代码如下:

def add_alpha_channel(self, frame):

    #print('增加alpha通道前大小:', frame.shape)

    b_channel, g_channel, r_channel = cv2.split(frame)

    alpha_channel = np.ones(b_channel.shape, dtype=b_channel.dtype) * 255

    # 最小值为0,代表图像透明(不可见)
    alpha_channel[:, :int(b_channel.shape[0] / 2)] = 255

    frame = cv2.merge((b_channel, g_channel, r_channel, alpha_channel))

    #print('增加alpha通道后大小:', frame.shape)

    return frame


2.2 矩阵计算拼图的坑

抠出绿幕后,为了获得剩下的透明图片,使用numpy的矩阵计算,通过像素点的值为0的条件取值(numpy.where函数),会造成抠出的图形出现穿透等异常情况。


#frame002为视频中的帧,测试的时候也可以通过cv2.imread读取一张图片

def green_screen_matting(self, frame002):
    #将帧的颜色空间由RGB转HSV
    hsv_img = cv2.cvtColor(frame002, cv2.COLOR_BGR2HSV)
    
    #标准的HSV中的绿色的区间
    lower_g = (35, 43, 46)
    upper_g = (77, 255, 255)
    
    #转换后的HSV空间的图片hsv_img,选择绿幕的部分,非绿幕的部分就被剔除了
    mask_of_green = cv2.inRange(hsv_img, lower_g, upper_g)
    #cv2.imshow('mask', mask_of_green)
    #cv2.waitKey()
    
    #通过 bitwise_not 反转mask_of_green这个mask,这样绿幕的那部分 就没有了
    frame_without_green = cv2.bitwise_not(mask_of_green)
    
    #将HSV的mask作为掩码,通过bitwise_and合成到原图片上,则只保留mask有内容的那部分,即抠图完成
    final = cv2.bitwise_and(frame002, frame002, mask=frame_without_green)

    return final

final = green_screen_matting(frame002)
    
#这里是将抠掉绿幕的图片合成到背景图片上,bg_img_cv是一张背景图片,通过imread读取进来
#此方法是最后一步,也是问题所在,不能这么处理   
final = np.where(final == 0, bg_img_cv, final)



2.3 优化后的矩阵计算贴图

正确的处理过程的代码如下:


def overlay_transparent(self, background_img, img_to_overlay_t, x, y, overlay_size=None):
    """
    @brief      Overlays a transparant PNG onto another image using CV2

    @param      background_img    The background image
    @param      img_to_overlay_t  The transparent image to overlay (has alpha channel)
    @param      x                 x location to place the top-left corner of our overlay
    @param      y                 y location to place the top-left corner of our overlay
    @param      overlay_size      The size to scale our overlay to (tuple), no scaling if None

    @return     Background image with overlay on top
    """

    bg_img = background_img.copy()

    if overlay_size is not None:
        img_to_overlay_t = cv2.resize(img_to_overlay_t.copy(), overlay_size)

    # Extract the alpha mask of the RGBA image, convert to RGB
    b, g, r, a = cv2.split(img_to_overlay_t)
    overlay_color = cv2.merge((b, g, r))

    # Apply some simple filtering to remove edge noise
    mask = cv2.medianBlur(a, 5)

    h, w, _ = overlay_color.shape
    roi = bg_img[y:y + h, x:x + w]

    # Black-out the area behind the logo in our original ROI
    img1_bg = cv2.bitwise_and(roi.copy(), roi.copy(), mask=cv2.bitwise_not(mask))

    # Mask out the logo from the logo image.
    img2_fg = cv2.bitwise_and(overlay_color, overlay_color, mask=mask)

    # Update the original image with our new ROI
    bg_img[y:y + h, x:x + w] = cv2.add(img1_bg, img2_fg)

    return bg_img


#指定final的位置和大小,贴图
frame002 = overlay_transparent(bg_img_cv, final, x_offset, y_offset,
                                          (final_width, final_height))


.

3 优化视频比特率

-


3.1 读取源视频的过程


input_video_cv = cv2.VideoCapture(input_video_path)



#视频相关的参数说明: https://docs.opencv.org/3.4/d4/d15/group__videoio__flags__base.html
input_video_width = int(input_video_cv.get(cv2.CAP_PROP_FRAME_WIDTH))
input_video_height = int(input_video_cv.get(cv2.CAP_PROP_FRAME_HEIGHT))

input_video_fps = 30  # 设置想要写成的视频的帧率
input_video_fps = int(input_video_cv.get(cv2.CAP_PROP_FPS))
input_video_frame_count = int(input_video_cv.get(cv2.CAP_PROP_FRAME_COUNT))
input_video_bit_rate = int(input_video_cv.get(cv2.CAP_PROP_BITRATE))

print('视频帧率:%d 比特率:%d 合计帧数:%d' % (input_video_fps, input_video_bit_rate, input_video_frame_count))


3.2 循环读取视频帧


接下来循环读取视频帧,并在处理好后写入到新的视频文件中,过程如下:

#fourcc = cv2.VideoWriter_fourcc(*"mp4v")  # 设置编码参数

output_video_cv = cv2.VideoWriter(output_video_file002,
                           cv2.VideoWriter_fourcc(*"mp4v"),
                           input_video_fps, (output_video_width, output_video_height))

#设置视频质量 0 ~ 100
output_video_cv.set(cv2.VIDEOWRITER_PROP_QUALITY, 100)
#用于并行编码的条纹数
output_video_cv.set(cv2.VIDEOWRITER_PROP_NSTRIPES, -1)


while True:
    ret, frame = input_video_cv.read()

    #logger.info(counter)

    if ret:

        if frame.any() == None:
            break

        counter += 1

        #具体的抠图过程,见上文,不赘述。
        #...
        #得到frame002

        output_video_cv.write(frame002)

    else:
        input_video_cv.set(cv2.CAP_PROP_POS_FRAMES, 0)
        break

    if cv2.waitKey(5) == ord('q'):
        break

input_video_cv.release()
output_video_cv.release()  #写入了  output_video_file002

logger.info('视频更换背景成功,合计帧数:' + str(counter))

cv2.destroyAllWindows()


3.3 新视频文件的比特率

视频文件的比特率,决定了视频的清晰度,比特率越大,视频越清晰,同时,对应的视频文件也就越大。以上写视频文件的方法的主要问题是没有办法主动控制目标视频的比特率,因此需要引入包“vidgear”,通过其提供的方法,控制ffmpeg的参数。优化后的代码如下:

from vidgear.gears import WriteGear


output_params = {"-vcodec": "libx264", "-crf": 0, \
                 "-preset": "fast"}  # define (Codec,CRF,preset) FFmpeg tweak parameters for writer

output_video_cv = WriteGear(output=output_video_file002, compression_mode=True, logging=True,
                   **output_params)  # Define writer with output filename 'Output.mp4'


while True:
    ret, frame = input_video_cv.read()

    #logger.info(counter)

    if ret:

        if frame.any() == None:
            break

        counter += 1

        #具体的抠图过程,见上文,不赘述。
        #...
        #得到frame002

        output_video_cv.write(frame002)

    else:
        input_video_cv.set(cv2.CAP_PROP_POS_FRAMES, 0)
        break

    if cv2.waitKey(5) == ord('q'):
        break

input_video_cv.release()
output_video_cv.close()  #写入了  output_video_file002

logger.info('视频更换背景成功,合计帧数:' + str(counter))

cv2.destroyAllWindows()

同时,也有另一种不使用包“vidgear”,直接通过管道调用ffmpeg的方法,参考的代码如下,同上面代码的原理,控制帧率的参数为“-vcodec”。

# import packages
from PIL import Image
from subprocess import Popen, PIPE
from imutils.video import VideoStream
from imutils.object_detection import non_max_suppression
from imutils import paths
import cv2
import numpy as np
import imutils

# ffmpeg setup
p = Popen(['ffmpeg', '-y', '-f', 'image2pipe', '-vcodec', 'mjpeg', '-r', '24', '-i', '-', '-vcodec', 'h264', '-qscale', '5', '-r', '24', 'video.mp4'], stdin=PIPE)

video = cv2.VideoCapture('videos.mp4')

while True:
    ret, frame = video.read()
    if ret:
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        im = Image.fromarray(frame)
        im.save(p.stdin, 'JPEG')
    else:
        break

p.stdin.close()
p.wait()
video.release()
cv2.destroyAllWindows()



4 moviepy合成声音

以上处理过程中,因为是处理视频帧的,重新合成的视频文件没有声音,我们需要将源视频文件的声音合成到新的视频文件中,这个过程我们使用moviepy来完成。

from moviepy import editor
from moviepy.video.io.VideoFileClip import VideoFileClip
from moviepy.editor import VideoFileClip, CompositeVideoClip
from moviepy.audio.io.AudioFileClip import AudioFileClip


logger.info('开始合成声音')

audio = AudioFileClip(input_video_path)    # 分离声轨
clip002 = VideoFileClip(output_video_file002)
videoclip = clip002.set_audio(audio)  # 写入声轨

videoclip.write_videofile(output_video_file, audio=True,
                          fps=input_video_fps,
                          bitrate=str(input_video_bit_rate) + 'k' )

logger.info('运行结束')

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码