草庐IT

【深度学习】AI一键换天

逝缘~ 2023-06-05 原文

目录

1.实验目标

2.案例内容介绍

3.实验步骤

3.1安装和导入依赖包

3.2设定算法参数 

3.3预览一下原视频

     预览一下要替换的天空图片

3.4定义SkyFilter类 

3.5开始处理视频 

3.6对比原视频和处理后的视频

3.7 生成你自己的换天视频

1.实验目标

1.了解图像分割的基本应用;

2.了解运动估计的基本应用;

3.了解图像混合的基本应用。

2.案例内容介绍

案例链接OBS - JupyterLab (huaweicloud.com)

无论是拍人拍景或是其他,“天空”都可以说是摄像中的关键元素。比如,一张平平无奇的景色图加上落日余晖的天空色调,是不是有内味了?(随手就可以变换出各种天空效果:晴天、彩虹、晚霞、暮光、夕阳等等)

当然,自然的天空还不是最酷炫的,今天给大家介绍一款基于原生视频的AI处理方法,不仅可以一键切置换天空背景,还可以打造任意“天空之城”。比如,《星际迷航》等科幻电影中经常出现的浩瀚星空、宇宙飞船,也可以利用这项技术融入随手拍的视频中,路人拍摄的公路片也能秒变科幻片,画面毫无违和感。好像只要脑洞够大,利用这项AI技术,可以创作无限种玩法。

基于视觉的视频天空替换和协调方法,该方法可以在具有可控风格的视频中自动生成逼真的天空背景。与以前的天空编辑方法专注于静态照片或需要集成在智能手机中的惯性测量装置拍摄视频不同,该方法完全基于视觉,对捕获设备没有任何要求,并且可以很好地应用于在线或离线处理场景。

算法流程大致可以分为三个步骤:

(1) 天空抠图

这一步主要是通过对蒙版数据集进行训练,将图片中的天空和其它物体进行像素级的划分,将天空部分从图片中分离。

(2) 运动估计

对图片中物体的位移情况进行分析,预估相机的移动方向,使替换后的天空和之前的天空位移一致。

(3) 图像混合

将去掉天空的原视频和要替换后的天空视频进行融合,同时对非天空的部分采用色彩叠加,使天空和其它物体的视觉效果相近,使视频效果更加逼真。

 

最后,算法使用数据增强的方法模拟出同一张图片在不同光照和天气的情况下的图片,使算法具有更强的适应性。

3.实验步骤

3.1安装和导入依赖包

import os
import moxing as mox

file_name = 'SkyAR'
if not os.path.exists(file_name):
    mox.file.copy('obs://modelarts-labs-bj4-v2/case_zoo/SkyAR/SkyAR.zip', 'SkyAR.zip')
    os.system('unzip SkyAR.zip')
    os.system('rm SkyAR.zip')
mox.file.copy_parallel('obs://modelarts-labs-bj4-v2/case_zoo/SkyAR/resnet50-19c8e357.pth', '/home/ma-user/.cache/torch/checkpoints/resnet50-19c8e357.pth')
!pip uninstall opencv-python -y
!pip uninstall opencv-contrib-python -y
!pip install opencv-contrib-python==4.5.3.56
cd SkyAR/
import time
import json
import base64
import numpy as np
import matplotlib.pyplot as plt
import cv2
import argparse
from networks import *
from skyboxengine import *
import utils
import torch
from IPython.display import clear_output, Image, display, HTML

%matplotlib inline

# 如果存在GPU则在GPU上面运行
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

3.2设定算法参数 

SkyAR算法提供了以下五个参数来调整换天的效果:

skybox_center_crop: 天空体中心偏移

auto_light_matching: 是否自动亮度匹配

relighting_factor: 补光

recoloring_factor: 重新着色

halo_effect: 是否开启光环效应

且提供了 datadir 和 skybox 两个参数来指定待处理的原视频和要替换的天空图片,通过路径进行指定即可,如下所示:

parameter = {
  "net_G": "coord_resnet50",
  "ckptdir": "./checkpoints_G_coord_resnet50",

  "input_mode": "video",
  "datadir": "./test_videos/sky.mp4",  # 待处理的原视频路径
  "skybox": "sky.jpg",  # 要替换的天空图片路径

  "in_size_w": 384,
  "in_size_h": 384,
  "out_size_w": 845,
  "out_size_h": 480,

  "skybox_center_crop": 0.5,
  "auto_light_matching": False,
  "relighting_factor": 0.8,
  "recoloring_factor": 0.5,
  "halo_effect": True,

  "output_dir": "./jpg_output",
  "save_jpgs": False
}

str_json = json.dumps(parameter)

3.3预览一下原视频

video_name = parameter['datadir']

def arrayShow(img):
    img = cv2.resize(img, (0, 0), fx=0.25, fy=0.25, interpolation=cv2.INTER_NEAREST)
    _,ret = cv2.imencode('.jpg', img)
    return Image(data=ret)

# 打开一个视频流
cap = cv2.VideoCapture(video_name)

frame_id = 0
while True:
    try:
        clear_output(wait=True) # 清除之前的显示
        ret, frame = cap.read() # 读取一帧图片
        if ret:
            frame_id += 1
            if frame_id > 200:
                break
            cv2.putText(frame, str(frame_id), (5, 15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)  # 画frame_id
            tmp = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 转换色彩模式
            img = arrayShow(frame)
            display(img) # 显示图片
            time.sleep(0.05) # 线程睡眠一段时间再处理下一帧图片
        else:
            break
    except KeyboardInterrupt:
        cap.release()
cap.release()

   预览一下要替换的天空图片

img= cv2.imread(os.path.join('./skybox', parameter['skybox']))
img2 = img[:, :, ::-1]
plt.imshow(img2)

3.4定义SkyFilter类 

class Struct:
    def __init__(self, **entries):
        self.__dict__.update(entries)
        
def parse_config():
    data = json.loads(str_json)
    args = Struct(**data)

    return args

args = parse_config()
class SkyFilter():

    def __init__(self, args):

        self.ckptdir = args.ckptdir
        self.datadir = args.datadir
        self.input_mode = args.input_mode

        self.in_size_w, self.in_size_h = args.in_size_w, args.in_size_h
        self.out_size_w, self.out_size_h = args.out_size_w, args.out_size_h

        self.skyboxengine = SkyBox(args)

        self.net_G = define_G(input_nc=3, output_nc=1, ngf=64, netG=args.net_G).to(device)
        self.load_model()

        self.video_writer = cv2.VideoWriter('out.avi',
                                            cv2.VideoWriter_fourcc(*'MJPG'),
                                            20.0,
                                            (args.out_size_w, args.out_size_h))
        self.video_writer_cat = cv2.VideoWriter('compare.avi',
                                                cv2.VideoWriter_fourcc(*'MJPG'),
                                                20.0,
                                                (2*args.out_size_w, args.out_size_h))

        if os.path.exists(args.output_dir) is False:
            os.mkdir(args.output_dir)

        self.output_img_list = []

        self.save_jpgs = args.save_jpgs


    def load_model(self):
        # 加载预训练的天空抠图模型
        print('loading the best checkpoint...')
        checkpoint = torch.load(os.path.join(self.ckptdir, 'best_ckpt.pt'),
                                map_location=device)
        self.net_G.load_state_dict(checkpoint['model_G_state_dict'])
        self.net_G.to(device)
        self.net_G.eval()


    def write_video(self, img_HD, syneth):

        frame = np.array(255.0 * syneth[:, :, ::-1], dtype=np.uint8)
        self.video_writer.write(frame)

        frame_cat = np.concatenate([img_HD, syneth], axis=1)
        frame_cat = np.array(255.0 * frame_cat[:, :, ::-1], dtype=np.uint8)
        self.video_writer_cat.write(frame_cat)

        # 定义结果缓冲区
        self.output_img_list.append(frame_cat)


    def synthesize(self, img_HD, img_HD_prev):

        h, w, c = img_HD.shape

        img = cv2.resize(img_HD, (self.in_size_w, self.in_size_h))

        img = np.array(img, dtype=np.float32)
        img = torch.tensor(img).permute([2, 0, 1]).unsqueeze(0)

        with torch.no_grad():
            G_pred = self.net_G(img.to(device))
            G_pred = torch.nn.functional.interpolate(G_pred,
                                                     (h, w),
                                                     mode='bicubic',
                                                     align_corners=False)
            G_pred = G_pred[0, :].permute([1, 2, 0])
            G_pred = torch.cat([G_pred, G_pred, G_pred], dim=-1)
            G_pred = np.array(G_pred.detach().cpu())
            G_pred = np.clip(G_pred, a_max=1.0, a_min=0.0)

        skymask = self.skyboxengine.skymask_refinement(G_pred, img_HD)

        syneth = self.skyboxengine.skyblend(img_HD, img_HD_prev, skymask)

        return syneth, G_pred, skymask


    def cvtcolor_and_resize(self, img_HD):

        img_HD = cv2.cvtColor(img_HD, cv2.COLOR_BGR2RGB)
        img_HD = np.array(img_HD / 255., dtype=np.float32)
        img_HD = cv2.resize(img_HD, (self.out_size_w, self.out_size_h))

        return img_HD
        

    def process_video(self):
        # 逐帧处理视频
        cap = cv2.VideoCapture(self.datadir)
        m_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
        img_HD_prev = None

        for idx in range(m_frames):
            ret, frame = cap.read()
            if ret:
                img_HD = self.cvtcolor_and_resize(frame)

                if img_HD_prev is None:
                    img_HD_prev = img_HD

                syneth, G_pred, skymask = self.synthesize(img_HD, img_HD_prev)

                self.write_video(img_HD, syneth)

                img_HD_prev = img_HD

                if (idx + 1) % 50 == 0:
                    print(f'processing video, frame {idx + 1} / {m_frames} ... ')

            else:  # 如果到达最后一帧
                break

3.5开始处理视频 

sf = SkyFilter(args)
sf.process_video()

3.6对比原视频和处理后的视频

video_name = "compare.avi"

def arrayShow(img):
    _,ret = cv2.imencode('.jpg', img)
    return Image(data=ret)

# 打开一个视频流
cap = cv2.VideoCapture(video_name)

frame_id = 0
while True:
    try:
        clear_output(wait=True) # 清除之前的显示
        ret, frame = cap.read() # 读取一帧图片
        if ret:
            frame_id += 1
            cv2.putText(frame, str(frame_id), (5, 15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)  # 画frame_id
            tmp = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 转换色彩模式
            img = arrayShow(frame)
            display(img) # 显示图片
            time.sleep(0.05) # 线程睡眠一段时间再处理下一帧图片
        else:
            break
    except KeyboardInterrupt:
        cap.release()
cap.release()

3.7 生成你自己的换天视频

三个步骤实现自定义视频的换天效果:

(1)在自己本地电脑上准备好一个待处理的mp4视频文件和一张天空图片;

(2)参考此文档,将视频文件和图片文件分别上传到ModelArts JupyterLab的SkyAR/test_videos目录和SkyAR/skybox目录下;

(3)修改步骤2 “设定算法参数” 中datadir 和 skybox 两个参数的路径为你刚上传的视频和图片路径;

(4)重新运行步骤2~6。

 

有关【深度学习】AI一键换天的更多相关文章

  1. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  2. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  3. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  4. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  5. ruby - 我如何学习 ruby​​ 的正则表达式? - 2

    如何学习ruby​​的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby​​的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/

  6. 深度学习12. CNN经典网络 VGG16 - 2

    深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG

  7. 机器学习——时间序列ARIMA模型(四):自相关函数ACF和偏自相关函数PACF用于判断ARIMA模型中p、q参数取值 - 2

    文章目录1、自相关函数ACF2、偏自相关函数PACF3、ARIMA(p,d,q)的阶数判断4、代码实现1、引入所需依赖2、数据读取与处理3、一阶差分与绘图4、ACF5、PACF1、自相关函数ACF自相关函数反映了同一序列在不同时序的取值之间的相关性。公式:ACF(k)=ρk=Cov(yt,yt−k)Var(yt)ACF(k)=\rho_{k}=\frac{Cov(y_{t},y_{t-k})}{Var(y_{t})}ACF(k)=ρk​=Var(yt​)Cov(yt​,yt−k​)​其中分子用于求协方差矩阵,分母用于计算样本方差。求出的ACF值为[-1,1]。但对于一个平稳的AR模型,求出其滞

  8. Unity Shader 学习笔记(5)Shader变体、Shader属性定义技巧、自定义材质面板 - 2

    写在之前Shader变体、Shader属性定义技巧、自定义材质面板,这三个知识点任何一个单拿出来都是一套知识体系,不能一概而论,本文章目的在于将学习和实际工作中遇见的问题进行总结,类似于网络笔记之用,方便后续回顾查看,如有以偏概全、不祥不尽之处,还望海涵。1、Shader变体先看一段代码......Properties{ [KeywordEnum(on,off)]USL_USE_COL("IsUseColorMixTex?",int)=0 [Toggle(IS_RED_ON)]_IsRed("IsRed?",int)=0}......//中间省略,后续会有完整代码 #pragmamulti_c

  9. ruby-on-rails - 这个 C 和 PHP 程序员如何学习 Ruby 和 Rails? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我来自C、php和bash背景,很容易学习,因为它们都有相同的C结构,我可以将其与我已经知道的联系起来。然后2年前我学了Python并且学得很好,Python对我来说比Ruby更容易学。然后从去年开始,我一直在尝试学习Ruby,然后是Rails,我承认,直到现在我还是学不会,讽刺的是那些打着简单易学的烙印,但是对于我这样一个老练的程序员来说,我只是无法将它

  10. ruby Hash 包括另一个哈希,深度检查 - 2

    进行这种深度检查的最佳方法是什么:{:a=>1,:b=>{:c=>2,:f=>3,:d=>4}}.include?({:b=>{:c=>2,:f=>3}})#=>true谢谢 最佳答案 我想我从那个例子中明白了你的意思(不知何故)。我们检查子哈希中的每个键是否在超哈希中,然后检查这些键的对应值是否以某种方式匹配:如果值是哈希,则执行另一次深度检查,否则,检查值是否相等:classHashdefdeep_include?(sub_hash)sub_hash.keys.all?do|key|self.has_key?(key)&&ifs

随机推荐