用ModelScope带你制作小动画

简介: 本文带你利用ModelScope把实际拍摄的视频转换为动画,基本原理是把视频解码成图像,使用人像卡通化模型对视频逐帧进行卡通化,再把多帧图像合并成视频,从而完成动画生成

平台概览

近日阿里发布的ModelScope(https://modelscope.cn/#/models) 平台,意在打造开源的模型即服务共享平台,为泛AI开发者提供灵活、易用、低成本的一站式模型服务产品,让模型应用更简单。  这个平台上模型丰富度还可以,目前一共138个模型,其中55个可以通过在线demo体验效果,4个可以支持finetune(finetune还有待加强)。


卡通化模型介绍

打开模型库,映入眼帘的就是人像卡通化模型,不得不说这页面做的还挺好看的,不知道模型实际效果怎么样,是不是只是表面功夫做的好,那就拿第一个模型来小试牛刀把。

image.png

看模型页面上动图的转换效果还是挺好的,不禁想着,利用你给视频做个卡通化,那我是不是就可以生成动画了?  如果视频中没有人像,背景的卡通化效果是不是很好? 视频中有各种质量参差不齐的帧,正好也可以用来测试下模型的各种corner case。 话不多说,那就让我们开始把。


ModelScope的基本使用和环境搭建我就不重复了,大家自行参考文档:

https://modelscope.cn/#/docs/%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B



视频卡通化

先展示一个大概效果



首先,我们要理解下视频和图片的区别,视频一般是有视频和音轨两部分,视频部分可以理解为是若干张连续图片,如果我们可以吧这些图片提取出来,逐帧利用模型做卡通化生成新的图像,再把生成的图像合成视频,就可以得到一个小动画了。

因此,我们可以把视频卡通化拆解为如下步骤:

  1. 视频解码
  2. 批量图片卡通化
  3. 视频合成
  4. 音轨恢复


下面我们将分步骤介绍实现,并在本文最后给出了完成的可执行python代码


视频解码

首先,我们利用opencv进行视频解码,把解码后的图片存放在frames中

video=cv2.VideoCapture(video_file)
if (video.isOpened() ==False): 
print("Error reading video file")
# Read frameframes= []
i=0while(video.isOpened()):
i+=1# Capture frame-by-frameif(i%10):
print(f'loading {i} frames')
ret, frame=video.read()
ifret==True:
frames.append(frame)
else:
break# When everything done, release the video capture objectvideo.release()
print('loading video done.')


批量图片卡通化

首先,初始化卡通化pipeline

img_cartoon=pipeline('image-portrait-stylization', model='damo/cv_unet_person-image-cartoon_compound-models')

看文档示例,pipeline支持图片文件名输入, 不知道是不是支持图片数据的直接输入, 自己尝试了下OK,  然后又根据 官方pipeline的使用文档(https://modelscope.cn/#/docs/%E6%A8%A1%E5%9E%8B%E7%9A%84%E6%8E%A8%E7%90%86Pipeline) 介绍, 觉得应该是支持多张图片的输入,小心翼翼的尝试后果然也是可以的,为我的小机智感到自豪,也不得不说官方的文档细节做的还是不到位。


最终,采取如下代码完成批量图片卡通化,并把卡通化后的图片存在 result_frames

results=img_cartoon(frames)
result_frames= [r['output_img'] forrinresults]


另外不得不吐槽一下, 多张图片本地gpu推理跑的是真慢啊, 后来发现全是用的cpu。


视频合成

最后,通过opencv把多张图片再合成为视频,这里需要强调一下:

输出图片的尺寸和输入图片不同, 因此再设置输出视频大小的时候不能采用原始输入视频的大小,这个地方把我坑了半个小时debug。

frame_height, frame_width, _=result_frames[0].shapesize= (frame_width, frame_height)
# FutureWarning: `rcond` parameter will change to the default of machine precision times ``max(M, N)`` where M and N are the input matrix dimensions.# To use the future default and silence this warning we advise to pass `rcond=None`, to keep using the old, explicitly pass `rcond=-1`.#  r, _, _, _ = lstsq(X, U)foridxinrange(len(result_frames)):
result_frames[idx] =result_frames[idx].astype(np.uint8)
print(f'saving video to file {out_file}')
out=cv2.VideoWriter(out_file,cv2.VideoWriter_fourcc(*'mp4v'), fps, size)
forfinresult_frames:
out.write(f)
out.release()
print(f'saving video done')

此外,在保存视频的时候还发现另一个问题,有些视频帧转换会打印如下日志,导致输出的图片不再是unint8,而是float32类型,因此加上强制把每帧图片转换为uint8的逻辑

FutureWarning: `rcond` parameter will change to the default of machine precision times ``max(M, N)`` where M and N are the input matrix dimensions.
To use the future default and silence this warning we advise to pass `rcond=None`, to keep using the old, explicitly pass `rcond=-1`.
r, _, _, _ = lstsq(X, U)

音轨还原

opencv没有提取音轨,音轨和视频合成的功能,我们利用MoviePy(https://zulko.github.io/moviepy/)来完成。


首先安装MoviePy

pip install ffmpeg moviepy


利用moviepy提取原始音轨

importmoviepy.editorasmpaudio_file='out.mp3'my_clip=mp.VideoFileClip(video_file)
my_clip.audio.write_audiofile(audio_file)


读取合成视频和原始音轨,生成带有声音的动画

frommoviepy.editorimportVideoFileClip, AudioFileClip# loading video dsa gfg intro videoclip=VideoFileClip(out_tmp_file)
# loading audio fileaudioclip=AudioFileClip(audio_file)
# adding audio to the video clipvideoclip=clip.set_audio(audioclip)
videoclip.write_videofile(out_file)
# save to gif# videoclip.write_gif(out_gif_file)


效果展示

鼓浪屿的树-详细对比

树的这个视频我认为是尝试的几个视频中效果最好的,虽然没有任务,但是效果看起来挺好的,看起来卡通化的背景部分对于纹理比较密集、颜色多样的画面效果会比较好。



大海

大海的效果还行,海鸥也都可以看到,注意这个是没有人像的背景卡通化,看起来和可以接受


沙滩


这个视频转换效果不太好, 原始视频上孩子的脸部没有强光,不知道为什么卡通化后脸上出现了异样。



问题整理

  1. 上传图片后模型一直处于模型加载过程,用户群反馈后已修复

image.png


  1. 看起来一直没有用gpu,虽然显存占用了,但是在用cpu计算,cpu利用率很高

image.png

image.png


  1. 第三个沙滩视频孩子脸部的badcase有待定位解决。



完整代码

使用方法:

修改video_file变量指向你的输入视频路径

修改out_file变量指定输出视频路径

python运行如下代码即可

importcv2importnumpyasnpfrommodelscope.hub.snapshot_downloadimportsnapshot_downloadfrommodelscope.pipelinesimportpipelinefrommoviepy.editorimportVideoFileClip, AudioFileClipimportlogginglogging.basicConfig(level=logging.INFO)
img_cartoon=pipeline('image-portrait-stylization', model='damo/cv_unet_person-image-cartoon_compound-models')
video_file='apps/gulangyu-tree.mp4'out_file='apps/gulangyu-tree_out.mp4'out_tmp_file='video_tmp.mp4'audio_file='audio_tmp.mp3'my_clip=VideoFileClip(video_file)
my_clip.audio.write_audiofile(audio_file)
logging.info('save audio file done')
logging.info(f'load video {video_file}')
video=cv2.VideoCapture(video_file)
fps=video.get(cv2.CAP_PROP_FPS)
if (video.isOpened() ==False): 
logging.info("Error reading video file")
# Read frameframes= []
i=0while(video.isOpened()):
i+=1# Capture frame-by-frameif(i%10):
logging.info(f'loading {i} frames')
ret, frame=video.read()
ifret==True:
# Display the resulting frameframes.append(frame)
else:
break# When everything done, release the video capture objectvideo.release()
logging.info('loading video done.')
results=img_cartoon(frames)
result_frames= [r['output_img'] forrinresults]
# We need to set resolutions for writing video and  convert them from float to integer.frame_height, frame_width, _=result_frames[0].shapesize= (frame_width, frame_height)
# FutureWarning: `rcond` parameter will change to the default of machine precision times ``max(M, N)`` where M and N are the input matrix dimensions.# To use the future default and silence this warning we advise to pass `rcond=None`, to keep using the old, explicitly pass `rcond=-1`.#  r, _, _, _ = lstsq(X, U)foridxinrange(len(result_frames)):
result_frames[idx] =result_frames[idx].astype(np.uint8)
logging.info(f'saving video to file {out_tmp_file}')
out=cv2.VideoWriter(out_tmp_file,cv2.VideoWriter_fourcc(*'mp4v'), fps, size)
forfinresult_frames:
out.write(f)
out.release()
logging.info(f'saving video done')
logging.info(f'merging audio and video')
# loading video dsa gfg intro videoclip=VideoFileClip(out_tmp_file)
# loading audio fileaudioclip=AudioFileClip(audio_file)
# adding audio to the video clipvideoclip=clip.set_audio(audioclip)
videoclip.write_videofile(out_file)
# save to gif# videoclip.write_gif(out_gif_file)logging.info('finished!')


目录
相关文章
|
存储 人工智能 自然语言处理
15.4K Star!Vercel官方出品,零基础构建企业级AI聊天机器人
"基于Next.js 14和AI SDK打造的Chat SDK,让开发者快速构建支持多模态交互、代码执行、文件共享的智能对话系统,5分钟完成全栈部署!" —— Vercel AI Chatbot项目核心宣言
781 5
|
应用服务中间件 开发工具 nginx
Mac M1/M2/M3 芯片环境配置以及常用软件安装-前端
Mac M1/M2/M3 芯片环境配置以及常用软件安装-前端 最近换了台新 Mac,所有的配置和软件就重新安装下,顺便写个文章。
2141 1
|
人工智能 自然语言处理 PyTorch
InspireMusic:阿里通义实验室开源的音乐生成模型,支持文本或音频生成多种风格的音乐
阿里通义实验室开源的音乐生成技术,支持通过简单描述快速生成多种风格的高质量音乐作品。
2747 4
|
安全 搜索推荐 数据挖掘
陪玩系统源码开发流程解析,成品陪玩系统源码的优点
我们自主开发的多客陪玩系统源码,整合了市面上主流陪玩APP功能,支持二次开发。该系统适用于线上游戏陪玩、语音视频聊天、心理咨询等场景,提供用户注册管理、陪玩者资料库、预约匹配、实时通讯、支付结算、安全隐私保护、客户服务及数据分析等功能,打造综合性社交平台。随着互联网技术发展,陪玩系统正成为游戏爱好者的新宠,改变游戏体验并带来新的商业模式。
1071 1
|
数据采集 搜索推荐 算法
|
存储 分布式计算 负载均衡
|
机器学习/深度学习 算法 测试技术
【博士每天一篇文献-算法】iCaRL_ Incremental Classifier and Representation Learning
本文介绍了iCaRL算法,一种增量分类器和表示学习系统,它能够逐步从数据流中学习新概念,通过使用最近均值示例规则、基于牧羊的样本选择和知识蒸馏等方法,在CIFAR-100和ImageNet数据集上展示了其优越的逐步学习能力和对灾难性遗忘的有效抵抗。
709 0
|
计算机视觉 索引 Python
【Python】已解决:ERROR: Could not find a version that satisfies the requirement cv2 (from versions: none)
【Python】已解决:ERROR: Could not find a version that satisfies the requirement cv2 (from versions: none)
1753 0
|
机器学习/深度学习 数据采集 自然语言处理
ModelScope保姆式教程带你玩转语言生成模型
PALM预训练语言生成模型是针对实际场景中常见的文本生成需求所设计的一个模型。模型利用大量无监督数据,通过结合自编码和自回归任务进行预训练,更贴合下游生成任务所同时需要的理解和生成能力。
ModelScope保姆式教程带你玩转语言生成模型

热门文章

最新文章