目录

前言

环境依赖

核心代码

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动图以及视频卡通化,两脚踢碎次元壁 | 机器学习相关推荐

  1. Python 实现 GIF 动图以及视频卡通化,两脚踢碎次元壁

    作者 | 剑客阿良_ALiang 来源 | CSDN博客 前言 今天我继续魔改一下,让该模型可以支持将gif动图或者视频,也做成卡通化效果.毕竟一张图可以那就带边视频也可以,没毛病.所以继给次元壁来了 ...

  2. python怎么变成动图_python可以做动图吗

    ImageMagick 是一套功能强大.稳定而且开源的工具集和开发包,可以用来读.写和处理超过200种基本格式的图片文件,包括PNG,JPEG,GIF,HEIC,TIFF,DPX,EXR,WebP,P ...

  3. Python生成gif动图

    Python生成gif动图 调用一个 python 库 imageio 可以轻松实现该功能 安装 pip/pip3 install imageio 步骤 读取静态图到列表中 ,作为 GIF 动图的每一 ...

  4. python怎么变成动图_教你用 Python 生成 GIF 动图 !

    最近啊 ,微信订阅号改变频繁 ,很多读者后台说 :小詹啊 ,我总是容易错过你公号的消息 ,现在没有置顶功能很难过啊 ! 不止你们难过 ,订阅号的作者恐怕更难过 !现在人人公众号时代 ,大家关注的公众号 ...

  5. python 加载动图_在浏览器中使用TensorFlow.js和Python构建机器学习模型(附代码)...

    大数据文摘授权转载自数据派THU 作者:MOHD SANAD ZAKI RIZVI 本文主要介绍了: TensorFlow.js (deeplearn.js)使我们能够在浏览器中构建机器学习和深度学习 ...

  6. python有趣的简单代码_简单代码一学就会,Python生成GIF动图

    文/IT可达鸭 图/IT可达鸭.网络 前言 最近在写技术文档的时候,发现一个问题.对于每个技术步骤,都需要一个截图,这样下来整篇文档都是截图,显得不是特别的专业. 为了解决这个问题,我想到一个方法,就 ...

  7. 使用Python合成gif动图

    下载地址: 链接:https://pan.baidu.com/s/1F_UcK-LUzorw182MzYh6pw 提取码:al3m 通常来说,图片比文字更有说服力,所以论文中图片有非常重要的地位. 然 ...

  8. 如何用Python制作学术动图?(数据+代码)

    0.前言 2019年年初,某厂年会的一个视频火爆全网,里面说出了无数职场人士的心声: 干活的累死累活,到头来干不过写PPT的! 也有网友表示:写好PPT和做好PPT在职场上就是一种能力,一份好的PPT ...

  9. python制作gif动图_短短几行Python代码制作的GIF动图

    前言 想知道怎么用Python代码与动画结合起来,制作GIF动图,并展示给其他人看呢?gifmaze可以帮助你很好的制作GIF动图,效果超棒, 比inter,pyglet和pyqt同比之下,还要好一点 ...

最新文章

  1. CNN结构设计技巧-兼顾速度精度与工程实现
  2. 用ipython 写spark
  3. Windows10熄屏自动断开WiFi连接解决方法
  4. Nodejs进阶:使用DiffieHellman密钥交换算法
  5. linux fork函数的精辟解说
  6. 你被这些网络迷题难倒过吗?
  7. 玩转java并发工具_玩Java并发
  8. [工具类]泛型集合转换为DataTable
  9. 2017年5月13日 恒生电子笔试题
  10. 如何找到局域网内所有主机ip
  11. python移动文件(非文件夹)
  12. 前后端分离的跨域问题
  13. Navicat删除注册表
  14. SpringBoot 发送邮件和附件
  15. python控制屏幕亮度,如何使用cron python定期调整屏幕亮度?
  16. 【那些年,我们一起追的女孩】第十四章
  17. Altium Designer 18中的System–Design Insight
  18. android短信接收器的实现,可以实现自动填写短信验证码功能
  19. Github Markdown 指定图片在光亮或暗黑模式展示
  20. css文字怎么显示在一起,css如何让文字成排显示

热门文章

  1. 温州市学习摩托车驾照攻略
  2. 迅视财经 压延法制备荧光玻璃的首创
  3. 计算机网络实验二 路由器的配置和静态路由
  4. Android,JCVideoPlayerStandard,节操,视频播放
  5. 复购几乎为0的产品,怎么做线上推广?
  6. Java: Integer比较127, 128
  7. 漫步太空和 .net
  8. 进阶篇:3.1.6)注塑件-注射模具与设备
  9. LIS.LCS.LCIS相关问题
  10. 最好用的Android反编译、重新编译和签名工具-AntiDroid V1.3 发布了~