中秋佳节,基于华为云AI制作属于自己的月亮!

本文档介绍了一个基于华为云AI的开源项目,用于在中秋节期间创造个性化的月亮视频。通过视频天空替换和协调方法,能够自动识别并替换视频中的天空,实现逼真的换天效果。实验详细阐述了模型原理、实验环境、步骤以及如何生成自己的换天视频。用户只需上传视频和天空图片,即可生成具有魔法换天效果的中秋视频。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

农历八月十五处于仲秋,节近秋分,故称“中秋节”,周代也有“秋分祭月”的习俗,中秋节自古便与月亮结下了不解之缘,故又称月夕、拜月节等。时值中秋,举家齐团圆,共望皎洁月,可曾想到中秋与那天边月有哪些历史的秘密呢?

中秋节之名源自于节令,吴子牧云:“八月十五日中秋节,此日三秋恰半,故谓之中秋。此夜月色倍明于常时,又谓之月夕。”中秋一词最早可见于《周礼》,于隋唐时正式定为节日。大约从宋代开始,中秋节越来越流行,终于到明清时期开始与春节齐名,成了我国传统节日中最重要的两个节日之一。

二、结果展示

三、模型简介

Github有一个开源项目SkyAR修改而成,可以自动识别天空,然后将天空从图片中切割出来,再将天空替换成目标天空,从而实现魔法换天。

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

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

  1. 天空抠图:这一步主要是通过对蒙版数据集进行训练,将图片中的天空和其它物体进行像素级的划分,将天空部分从图片中分离。
  2. 运动估计:对图片中物体的位移情况进行分析,预估相机的移动方向,使替换后的天空和之前的天空位移一致。
  3. 图像混合:将去掉天空的原视频和要替换后的天空视频进行融合,同时对非天空的部分采用色彩叠加,是天空和其它物体的视觉效果相近,是视频效果更加逼真。

四、实验环境

本案例使用AI框架:PyTorch-1.4,在CPU和GPU下面均可运行,CPU环境运行预计花费9分钟,GPU环境运行预计花费2分钟

CPU:8核
内存:64GB
GPU:nvidia-p100(32GB) * 1
架构:x86_64
规格:modelarts.vm.gpu

五、实验步骤

代码基于 ModelArts jupyterLab 运行

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
!pip install ipywidgets==7.7.1
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")

2、参数设置

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

  "input_mode": "video",
  "datadir": "./test_videos/preview.mp4",  # 待处理的原视频路径
  "skybox": "supermoon.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)

其中,

  • skybox_center_crop:天空体中心偏移
  • auto_light_matching: 是否自动亮度匹配
  • relighting_factor: 补光
  • recoloring_factor: 重新着色
  • halo_effect: 是否开启光环效应
  • datadir: 待处理的原视频和要
  • skybox: 替换的天空图片

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)

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

5、处理视频并与原视频对比

sf = SkyFilter(args)
sf.process_video()
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()

六、生成自己的换天视频

  1. 在自己本地电脑上准备好一个待处理的mp4视频文件和一张天空特效图片,注意视频文件必须满足白天拍摄、有蓝天白云天空背景、镜头水平缓慢移动、横屏四个条件,否则天空换背景的效果不佳;
  2. 将视频文件和图片文件拖动到左侧文件浏览窗口,分别上传到ModelArts JupyterLab的SkyAR/test_videos目录和SkyAR/skybox目录下;
  3. 修改 “设定算法参数” 中datadir 和 skybox 两个参数的路径为你刚上传的视频和图片路径;
  4. 重新运行步骤2~5。

七、参考文献

ModelArts开发者:魔幻黑科技,可换天造物,秒变科幻大片!

Castle in the Sky: Dynamic Sky Replacement and Harmonization in Videos.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

非鱼子焉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值