Python实现GIF动图以及视频卡通化,两脚踢碎次元壁 | 机器学习
目录
前言
环境依赖
核心代码
gif动图卡通化
视频卡通化
总结
前言
接着我的上一篇文章:Python实现照片卡通化,一拳打破次元壁 | 机器学习_阿良的博客-CSDN博客
我继续魔改一下,让该模型可以支持将gif动图或者视频,也做成卡通化效果。毕竟一张图可以那就带边视频也可以,没毛病。所以继给次元壁来了一拳,我在加两脚。
项目github地址:github地址
环境依赖
除了上一篇文章中的依赖,还需要加一些其他依赖,requirements.txt如下:
其他环境不太清楚的,可以看我前言链接地址的文章,有具体说明。
核心代码
不废话了,先上gif代码。
gif动图卡通化
实现代码如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/5 18:10
# @Author : 剑客阿良_ALiang
# @Site :
# @File : gif_cartoon_tool.py
# !/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/5 0:26
# @Author : 剑客阿良_ALiang
# @Site :
# @File : video_cartoon_tool.py# !/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/4 22:34
# @Author : 剑客阿良_ALiang
# @Site :
# @File : image_cartoon_tool.pyfrom PIL import Image, ImageEnhance, ImageSequence
import torch
from torchvision.transforms.functional import to_tensor, to_pil_image
from torch import nn
import os
import torch.nn.functional as F
import uuid
import imageio# -------------------------- hy add 01 --------------------------
class ConvNormLReLU(nn.Sequential):def __init__(self, in_ch, out_ch, kernel_size=3, stride=1, padding=1, pad_mode="reflect", groups=1, bias=False):pad_layer = {"zero": nn.ZeroPad2d,"same": nn.ReplicationPad2d,"reflect": nn.ReflectionPad2d,}if pad_mode not in pad_layer:raise NotImplementedErrorsuper(ConvNormLReLU, self).__init__(pad_layer[pad_mode](padding),nn.Conv2d(in_ch, out_ch, kernel_size=kernel_size, stride=stride, padding=0, groups=groups, bias=bias),nn.GroupNorm(num_groups=1, num_channels=out_ch, affine=True),nn.LeakyReLU(0.2, inplace=True))class InvertedResBlock(nn.Module):def __init__(self, in_ch, out_ch, expansion_ratio=2):super(InvertedResBlock, self).__init__()self.use_res_connect = in_ch == out_chbottleneck = int(round(in_ch * expansion_ratio))layers = []if expansion_ratio != 1:layers.append(ConvNormLReLU(in_ch, bottleneck, kernel_size=1, padding=0))# dwlayers.append(ConvNormLReLU(bottleneck, bottleneck, groups=bottleneck, bias=True))# pwlayers.append(nn.Conv2d(bottleneck, out_ch, kernel_size=1, padding=0, bias=False))layers.append(nn.GroupNorm(num_groups=1, num_channels=out_ch, affine=True))self.layers = nn.Sequential(*layers)def forward(self, input):out = self.layers(input)if self.use_res_connect:out = input + outreturn outclass Generator(nn.Module):def __init__(self, ):super().__init__()self.block_a = nn.Sequential(ConvNormLReLU(3, 32, kernel_size=7, padding=3),ConvNormLReLU(32, 64, stride=2, padding=(0, 1, 0, 1)),ConvNormLReLU(64, 64))self.block_b = nn.Sequential(ConvNormLReLU(64, 128, stride=2, padding=(0, 1, 0, 1)),ConvNormLReLU(128, 128))self.block_c = nn.Sequential(ConvNormLReLU(128, 128),InvertedResBlock(128, 256, 2),InvertedResBlock(256, 256, 2),InvertedResBlock(256, 256, 2),InvertedResBlock(256, 256, 2),ConvNormLReLU(256, 128),)self.block_d = nn.Sequential(ConvNormLReLU(128, 128),ConvNormLReLU(128, 128))self.block_e = nn.Sequential(ConvNormLReLU(128, 64),ConvNormLReLU(64, 64),ConvNormLReLU(64, 32, kernel_size=7, padding=3))self.out_layer = nn.Sequential(nn.Conv2d(32, 3, kernel_size=1, stride=1, padding=0, bias=False),nn.Tanh())def forward(self, input, align_corners=True):out = self.block_a(input)half_size = out.size()[-2:]out = self.block_b(out)out = self.block_c(out)if align_corners:out = F.interpolate(out, half_size, mode="bilinear", align_corners=True)else:out = F.interpolate(out, scale_factor=2, mode="bilinear", align_corners=False)out = self.block_d(out)if align_corners:out = F.interpolate(out, input.size()[-2:], mode="bilinear", align_corners=True)else:out = F.interpolate(out, scale_factor=2, mode="bilinear", align_corners=False)out = self.block_e(out)out = self.out_layer(out)return out# -------------------------- hy add 02 --------------------------def handle(gif_path: str, output_dir: str, type: int, device='cpu'):_ext = os.path.basename(gif_path).strip().split('.')[-1]if type == 1:_checkpoint = './weights/paprika.pt'elif type == 2:_checkpoint = './weights/face_paint_512_v1.pt'elif type == 3:_checkpoint = './weights/face_paint_512_v2.pt'elif type == 4:_checkpoint = './weights/celeba_distill.pt'else:raise Exception('type not support')os.makedirs(output_dir, exist_ok=True)net = Generator()net.load_state_dict(torch.load(_checkpoint, map_location="cpu"))net.to(device).eval()result = os.path.join(output_dir, '{}.{}'.format(uuid.uuid1().hex, _ext))img = Image.open(gif_path)out_images = []for frame in ImageSequence.Iterator(img):frame = frame.convert("RGB")with torch.no_grad():image = to_tensor(frame).unsqueeze(0) * 2 - 1out = net(image.to(device), False).cpu()out = out.squeeze(0).clip(-1, 1) * 0.5 + 0.5out = to_pil_image(out)out_images.append(out)# out_images[0].save(result, save_all=True, loop=True, append_images=out_images[1:], duration=100)imageio.mimsave(result, out_images, fps=15)return resultif __name__ == '__main__':print(handle('samples/gif/128.gif', 'samples/gif_result/', 3, 'cuda'))
代码说明:
1、主要的handle方法入参分别为:gif地址、输出目录、类型、设备使用(默认cpu,可选cuda使用显卡)。
2、类型主要是选择模型,最好用3,人像处理更生动一些。
执行验证一下
下面是我准备的gif素材
执行结果如下
看一下效果
哈哈,有点意思哦。
视频卡通化
实现代码如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/5 0:26
# @Author : 剑客阿良_ALiang
# @Site :
# @File : video_cartoon_tool.py# !/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/4 22:34
# @Author : 剑客阿良_ALiang
# @Site :
# @File : image_cartoon_tool.pyfrom PIL import Image, ImageEnhance
import torch
from torchvision.transforms.functional import to_tensor, to_pil_image
from torch import nn
import os
import torch.nn.functional as F
import uuid
import cv2
import numpy as np
import time
from ffmpy import FFmpeg# -------------------------- hy add 01 --------------------------
class ConvNormLReLU(nn.Sequential):def __init__(self, in_ch, out_ch, kernel_size=3, stride=1, padding=1, pad_mode="reflect", groups=1, bias=False):pad_layer = {"zero": nn.ZeroPad2d,"same": nn.ReplicationPad2d,"reflect": nn.ReflectionPad2d,}if pad_mode not in pad_layer:raise NotImplementedErrorsuper(ConvNormLReLU, self).__init__(pad_layer[pad_mode](padding),nn.Conv2d(in_ch, out_ch, kernel_size=kernel_size, stride=stride, padding=0, groups=groups, bias=bias),nn.GroupNorm(num_groups=1, num_channels=out_ch, affine=True),nn.LeakyReLU(0.2, inplace=True))class InvertedResBlock(nn.Module):def __init__(self, in_ch, out_ch, expansion_ratio=2):super(InvertedResBlock, self).__init__()self.use_res_connect = in_ch == out_chbottleneck = int(round(in_ch * expansion_ratio))layers = []if expansion_ratio != 1:layers.append(ConvNormLReLU(in_ch, bottleneck, kernel_size=1, padding=0))# dwlayers.append(ConvNormLReLU(bottleneck, bottleneck, groups=bottleneck, bias=True))# pwlayers.append(nn.Conv2d(bottleneck, out_ch, kernel_size=1, padding=0, bias=False))layers.append(nn.GroupNorm(num_groups=1, num_channels=out_ch, affine=True))self.layers = nn.Sequential(*layers)def forward(self, input):out = self.layers(input)if self.use_res_connect:out = input + outreturn outclass Generator(nn.Module):def __init__(self, ):super().__init__()self.block_a = nn.Sequential(ConvNormLReLU(3, 32, kernel_size=7, padding=3),ConvNormLReLU(32, 64, stride=2, padding=(0, 1, 0, 1)),ConvNormLReLU(64, 64))self.block_b = nn.Sequential(ConvNormLReLU(64, 128, stride=2, padding=(0, 1, 0, 1)),ConvNormLReLU(128, 128))self.block_c = nn.Sequential(ConvNormLReLU(128, 128),InvertedResBlock(128, 256, 2),InvertedResBlock(256, 256, 2),InvertedResBlock(256, 256, 2),InvertedResBlock(256, 256, 2),ConvNormLReLU(256, 128),)self.block_d = nn.Sequential(ConvNormLReLU(128, 128),ConvNormLReLU(128, 128))self.block_e = nn.Sequential(ConvNormLReLU(128, 64),ConvNormLReLU(64, 64),ConvNormLReLU(64, 32, kernel_size=7, padding=3))self.out_layer = nn.Sequential(nn.Conv2d(32, 3, kernel_size=1, stride=1, padding=0, bias=False),nn.Tanh())def forward(self, input, align_corners=True):out = self.block_a(input)half_size = out.size()[-2:]out = self.block_b(out)out = self.block_c(out)if align_corners:out = F.interpolate(out, half_size, mode="bilinear", align_corners=True)else:out = F.interpolate(out, scale_factor=2, mode="bilinear", align_corners=False)out = self.block_d(out)if align_corners:out = F.interpolate(out, input.size()[-2:], mode="bilinear", align_corners=True)else:out = F.interpolate(out, scale_factor=2, mode="bilinear", align_corners=False)out = self.block_e(out)out = self.out_layer(out)return out# -------------------------- hy add 02 --------------------------def handle(video_path: str, output_dir: str, type: int, fps: int, device='cpu'):_ext = os.path.basename(video_path).strip().split('.')[-1]if type == 1:_checkpoint = './weights/paprika.pt'elif type == 2:_checkpoint = './weights/face_paint_512_v1.pt'elif type == 3:_checkpoint = './weights/face_paint_512_v2.pt'elif type == 4:_checkpoint = './weights/celeba_distill.pt'else:raise Exception('type not support')os.makedirs(output_dir, exist_ok=True)# 获取视频音频_audio = extract(video_path, output_dir, 'wav')net = Generator()net.load_state_dict(torch.load(_checkpoint, map_location="cpu"))net.to(device).eval()result = os.path.join(output_dir, '{}.{}'.format(uuid.uuid1().hex, _ext))capture = cv2.VideoCapture(video_path)size = (int(capture.get(cv2.CAP_PROP_FRAME_WIDTH)), int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT)))print(size)videoWriter = cv2.VideoWriter(result, cv2.VideoWriter_fourcc(*'mp4v'), fps, size)cul = 0with torch.no_grad():while True:ret, frame = capture.read()if ret:print(ret)image = to_tensor(frame).unsqueeze(0) * 2 - 1out = net(image.to(device), False).cpu()out = out.squeeze(0).clip(-1, 1) * 0.5 + 0.5out = to_pil_image(out)contrast_enhancer = ImageEnhance.Contrast(out)img_enhanced_image = contrast_enhancer.enhance(2)enhanced_image = np.asarray(img_enhanced_image)videoWriter.write(enhanced_image)cul += 1print('第{}张图'.format(cul))else:breakvideoWriter.release()# 视频添加原音频_final_video = video_add_audio(result, _audio, output_dir)return _final_video# -------------------------- hy add 03 --------------------------
def extract(video_path: str, tmp_dir: str, ext: str):file_name = '.'.join(os.path.basename(video_path).split('.')[0:-1])print('文件名:{},提取音频'.format(file_name))if ext == 'mp3':return _run_ffmpeg(video_path, os.path.join(tmp_dir, '{}.{}'.format(uuid.uuid1().hex, ext)), 'mp3')if ext == 'wav':return _run_ffmpeg(video_path, os.path.join(tmp_dir, '{}.{}'.format(uuid.uuid1().hex, ext)), 'wav')def _run_ffmpeg(video_path: str, audio_path: str, format: str):ff = FFmpeg(inputs={video_path: None},outputs={audio_path: '-f {} -vn'.format(format)})print(ff.cmd)ff.run()return audio_path# 视频添加音频
def video_add_audio(video_path: str, audio_path: str, output_dir: str):_ext_video = os.path.basename(video_path).strip().split('.')[-1]_ext_audio = os.path.basename(audio_path).strip().split('.')[-1]if _ext_audio not in ['mp3', 'wav']:raise Exception('audio format not support')_codec = 'copy'if _ext_audio == 'wav':_codec = 'aac'result = os.path.join(output_dir, '{}.{}'.format(uuid.uuid4(), _ext_video))ff = FFmpeg(inputs={video_path: None, audio_path: None},outputs={result: '-map 0:v -map 1:a -c:v copy -c:a {} -shortest'.format(_codec)})print(ff.cmd)ff.run()return resultif __name__ == '__main__':print(handle('samples/video/981.mp4', 'samples/video_result/', 3, 25, 'cuda'))
代码说明
1、主要的实现方法入参分别为:视频地址、输出目录、类型、fps(帧率)、设备类型(默认cpu,可选择cuda显卡模式)。
2、类型主要是选择模型,最好用3,人像处理更生动一些。
3、代码设计思路:先将视频音频提取出来、将视频逐帧处理后写入新视频、新视频和原视频音频融合。
关于如何视频提取音频可以参考我的另一篇文章:python 提取视频中的音频 | Python工具类_阿良的博客-CSDN博客
关于如何视频融合音频可以参考我的另一篇文章:
Python 视频添加音频(附代码) | Python工具_阿良的博客-CSDN博客
4、视频中间会产生临时文件,没有清理,如需要可以修改代码自行清理。
验证一下
下面是我准备的视频素材截图,我会上传到github上。
执行结果
看看效果截图
还是很不错的哦。
总结
这次可不是没什么好总结的,总结的东西蛮多的。首先我说一下这个开源项目目前模型的一些问题。
1、我测试了不少图片,总的来说对亚洲人的脸型不能很好的卡通化,但是欧美的脸型都比较好。所以还是训练的数据不是很够,但是能理解,毕竟要专门做卡通化的标注数据想想就是蛮头疼的事。所以我建议大家在使用的时候,多关注一下项目是否更新了最新的模型。
2、视频一但有字幕,会对字幕也做处理。所以可以考虑找一些视频和字幕分开的素材,效果会更好一些。
总的来说,这个项目还是很不错的,两天的学习也让我收获很多。
分享:
你跳不出这个世界,是因为你不知道这个世界有多大,一旦你知道了,你就超出了它。
——《悟空传》
如果本文对你有用的话,请点个赞吧,谢谢!
Python实现GIF动图以及视频卡通化,两脚踢碎次元壁 | 机器学习相关推荐
- Python 实现 GIF 动图以及视频卡通化,两脚踢碎次元壁
作者 | 剑客阿良_ALiang 来源 | CSDN博客 前言 今天我继续魔改一下,让该模型可以支持将gif动图或者视频,也做成卡通化效果.毕竟一张图可以那就带边视频也可以,没毛病.所以继给次元壁来了 ...
- python怎么变成动图_python可以做动图吗
ImageMagick 是一套功能强大.稳定而且开源的工具集和开发包,可以用来读.写和处理超过200种基本格式的图片文件,包括PNG,JPEG,GIF,HEIC,TIFF,DPX,EXR,WebP,P ...
- Python生成gif动图
Python生成gif动图 调用一个 python 库 imageio 可以轻松实现该功能 安装 pip/pip3 install imageio 步骤 读取静态图到列表中 ,作为 GIF 动图的每一 ...
- python怎么变成动图_教你用 Python 生成 GIF 动图 !
最近啊 ,微信订阅号改变频繁 ,很多读者后台说 :小詹啊 ,我总是容易错过你公号的消息 ,现在没有置顶功能很难过啊 ! 不止你们难过 ,订阅号的作者恐怕更难过 !现在人人公众号时代 ,大家关注的公众号 ...
- python 加载动图_在浏览器中使用TensorFlow.js和Python构建机器学习模型(附代码)...
大数据文摘授权转载自数据派THU 作者:MOHD SANAD ZAKI RIZVI 本文主要介绍了: TensorFlow.js (deeplearn.js)使我们能够在浏览器中构建机器学习和深度学习 ...
- python有趣的简单代码_简单代码一学就会,Python生成GIF动图
文/IT可达鸭 图/IT可达鸭.网络 前言 最近在写技术文档的时候,发现一个问题.对于每个技术步骤,都需要一个截图,这样下来整篇文档都是截图,显得不是特别的专业. 为了解决这个问题,我想到一个方法,就 ...
- 使用Python合成gif动图
下载地址: 链接:https://pan.baidu.com/s/1F_UcK-LUzorw182MzYh6pw 提取码:al3m 通常来说,图片比文字更有说服力,所以论文中图片有非常重要的地位. 然 ...
- 如何用Python制作学术动图?(数据+代码)
0.前言 2019年年初,某厂年会的一个视频火爆全网,里面说出了无数职场人士的心声: 干活的累死累活,到头来干不过写PPT的! 也有网友表示:写好PPT和做好PPT在职场上就是一种能力,一份好的PPT ...
- python制作gif动图_短短几行Python代码制作的GIF动图
前言 想知道怎么用Python代码与动画结合起来,制作GIF动图,并展示给其他人看呢?gifmaze可以帮助你很好的制作GIF动图,效果超棒, 比inter,pyglet和pyqt同比之下,还要好一点 ...
最新文章
- CNN结构设计技巧-兼顾速度精度与工程实现
- 用ipython 写spark
- Windows10熄屏自动断开WiFi连接解决方法
- Nodejs进阶:使用DiffieHellman密钥交换算法
- linux fork函数的精辟解说
- 你被这些网络迷题难倒过吗?
- 玩转java并发工具_玩Java并发
- [工具类]泛型集合转换为DataTable
- 2017年5月13日 恒生电子笔试题
- 如何找到局域网内所有主机ip
- python移动文件(非文件夹)
- 前后端分离的跨域问题
- Navicat删除注册表
- SpringBoot 发送邮件和附件
- python控制屏幕亮度,如何使用cron python定期调整屏幕亮度?
- 【那些年,我们一起追的女孩】第十四章
- Altium Designer 18中的System–Design Insight
- android短信接收器的实现,可以实现自动填写短信验证码功能
- Github Markdown 指定图片在光亮或暗黑模式展示
- css文字怎么显示在一起,css如何让文字成排显示