【PaddleWeekly33期】胡桃桃那么可爱,那就“手绘”个胡桃摇吧~

本期PaddleWeekly将为大家介绍一下来自七年期限Mr.郑先生_两位大佬合作开源项目:线画稿生成器ExtractLine

该项目中,我们会在介绍其实现原理之外,再对其进行一个简要的封装,你可以轻松的在AI Studio上完成多种方式的图像->线画稿、视频->视频线画稿操作。

当然如果要在本地使用,可点击右上角的一键运行 按钮,进入项目后即可在左侧的文件夹中找到exe安装包。也可点击此处链接或扫描下方二维码加入QPT项目交流Q群与大家一起交流玩法与创意!

项目实操-欢迎一键三连

关注哔哩哔哩 GT_老张 每周五更新~

有奖活动

PaddleWeekly的初衷还是想将飞桨开发者优秀开源项目/作品展现给更多志同道合的人,如在屏幕前的你喜欢本次的项目:线画稿生成器ExtractLine,欢迎在GitHub上为原作者送出一颗🌟Star🌟以表支持。
当然,你也可以使用本项目在不限于哔哩哔哩、抖音等创作平台进行二次创作,简介中记得贴上本项目的AI Studio链接就更好啦。

最终我们将会在一周年从为原作者送出🌟Star🌟和二次创作的开发者中抽取3-5名作为本期欧气开发者,送出神秘飞桨周边一个~

高清版"胡桃摇"来源:哔哩哔哩-海螺张

Warmup

入门难度

仅做使用

难度:★☆☆☆☆
技术栈:无特殊要求
由于该项目已上传至AI Studio,本地同样提供了exe安装包,故难度极低且无需技术栈,有手就行。

二次开发

难度:★☆☆☆☆
技术栈:Python基础能力
项目整体与PaddleHub集成,API简单易用,二次开发时无需我们过多关心深度学习方向的技术能力。

算法开发

难度:★★★☆☆
技术栈:Python基础能力、飞桨PaddlePaddle或任一深度学习框架。
虽然该类型任务入门简单,甚至用简单的边缘检测算子即可做出看起来还不错的线条,但实际上这些线条与“素描”做对比仍有较大差距。
要想做出一个效果不错的线稿提取模型,在数据集、深度学习模型建模方面仍有一定考验。

相关资料

  1. U-Net实现
  2. Canny算法

如果只是想了解使用方式,可滚动页面至最底部的开始使用一节

算法建模

任务目标

由于我们想要做到的是从图片中提取人物的线稿,那么在模型建模时确定的输入输出就应是任一图片 -> 线稿图片。
即:

[N, C, H, W] -> [N, 1, H, W]

算法建模

既然这种类型的输入输出,是不是又可以联想到Pix2Pix的网络结构呢?

那么也就意味着,如果我们想建模一个极其简易的线稿提取模型,U-Net这样的简单模型结构即可在前期时进行简要验证。

在线画稿生成器ExtractLine中,尽管作者没有提供model相关的组网文件,但我们可以通过使用VisualDL对其模型文件中的__model__文件进行一个可视化操作,最终我们可以看到其使用的就是一个形如U-Net的简要Pix2Pix结构,并无太大难度。

由于VisualDL可视化后的模型尺寸较大,如需查看模型结构可在Fork本项目后在左侧文件夹中找到“模型可视化.PNG”文件进行查阅。

传统边缘检测是否可行?

当然,可能会有同学会问:线稿提取不就是简单的边缘检测吗?我用传统的边缘检测算子是不是就能解决这个问题?

当然可以,这种方式不仅轻便,而且也可以在没有线稿数据集的情况下拿到一个不错的效果,但该方案存在一个缺点:提取的结果可能带有杂音

接下来我们将会使用一个非常简单好用的边缘检测项目进行测试,该项目来源于肖佬EdgeOPs

# Clone EdgeOPs 本地如Clone较慢可使用 https://github.com/QPT-Family/QWebSiteOptimizer 进行更换镜像源
# !git clone https://hub.fastgit.org/jm12138/EdgeOPs
正克隆到 'EdgeOPs'...
remote: Enumerating objects: 13, done.[K
remote: Counting objects: 100% (13/13), done.[K
remote: Compressing objects: 100% (12/12), done.[K
remote: Total 13 (delta 3), reused 0 (delta 0), pack-reused 0[K
展开对象中: 100% (13/13), 完成.
检查连接... 完成。
# 以下代码来源于肖佬的《边缘检测系列1:传统边缘检测算子》
# https://aistudio.baidu.com/aistudio/projectdetail/2512704
import os
import cv2
import numpy as np
import paddle

from EdgeOPs.edgeops.paddle import EdgeOP
from PIL import Image

def test_edge_det(kernel, img_path='inp.jpg'):
    img = cv2.imread(img_path, 0)
    print(img.shape)
    img_tensor = paddle.to_tensor(img, dtype='float32')[None, None, ...]
    op = EdgeOP(kernel)
    all_results = []
    for mode in range(4):
        results = op(img_tensor, mode=mode)
        all_results.append(results.numpy()[0])
    
    results = op(img_tensor, mode=4)
    for result in results.numpy()[0]:
        all_results.append(result)
    return all_results, np.concatenate(all_results, 1)

roberts_kernel = np.array([
    [[
        [1,  0],
        [0, -1]
    ]],
    [[
        [0, -1],
        [1,  0]
    ]]
])

_, concat_res = test_edge_det(roberts_kernel)
im = Image.fromarray(concat_res)
im = im.resize((im.size[0]//4, im.size[1]//4))
import PIL.ImageOps
PIL.ImageOps.invert(im)
(812, 916)

在这里插入图片描述

我们可以看到,传统边缘检测效果也是很不错的。

但该方案是无差别进行的边缘检测,这样也就会导致目标的轮廓线深浅程度变得“违和”。

例如在Robert算子当中,我们显然可以看到在没有层次的区域(眼睛与面部轮廓)的轮廓线却呈现出有深有浅的效果。

同样,Scharr算子完全避免了该问题,但面部显然有了我们不需要的颜色填充,即使是表现不错的Prewitt算子,面部依旧受类似的影响

当然,这是建立在没有标签数据的基础上制作的,如果要用深度学习方法来完成线稿提取,那么我们还需准备标签数据,这将意味着在没有标签数据的情况下边缘检测不失为一个不错的选择。

深度学习方案效果如何?

相较传统边缘检测方案,深度学习方法的可拓展性也会有一定的提升,例如我们可以将“显著目标提取” 模型ExtractLine进行结合,使得其可以只提取图片的主要目标线稿,而非周围杂音。

显著目标提取效果
有无显著目标提取对比

可见下图中的“弹幕”在有显著目标提取后消失了。

若不了解显著目标检测,可参考张牙舞爪大佬的显著目标检测研究方向与Paddle处理CV任务时的代码技巧分享

开始使用

这里,我们使用PaddleHub中封装好的ExtractLine方案来“手绘”个线稿版胡桃摇吧~

使用流程

  1. 在左侧文件夹中上传一张图片
  2. 将图片文件名修改为inp.jpg
  3. 点击下方的开始按钮即可执行,程序完成后可在左侧output文件夹中找到我们生成的图片,右键即可看到下载按钮。
import paddlehub as hub

Extract_Line_Draft_test = hub.Module(name="Extract_Line_Draft")
test_img = "./inp.jpg"

Extract_Line_Draft_test.ExtractLine(test_img)
print("图片已保存至output/output.png")

# 读取线稿并展示
from PIL import Image
im = Image.open("output/output.png")
im = im.resize((im.size[0]//4, im.size[1]//4))
im

进阶玩法

如需制作为翻页画的形式,可使用下方代码将视频文件以抽帧的形式将目标呈现在A4纸大小的画布上,打印+裁剪后即可成为翻页画。

使用流程

  1. 上传一个mp4格式的视频文件。
  2. 修改文件名为inp.mp4。
  3. 点击下方运行按钮,运行代码块。(如需使用GPU,请修改use_gpu = True
  4. 下载out_a4.jpg文件,对其进行打印+裁剪+装订即可做成翻页画。

尽管其并不具有很极高的商业价值,但在授权外还请勿用于商业目的。

%set_env CUDA_VISIBLE_DEVICES=0
import cv2
import paddlehub as hub
from PIL import Image

Extract_Line_Draft_test = hub.Module(name="Extract_Line_Draft")

videoFile = "inp.mp4"
cache_path = "./cache.jpg"
use_gpu = False

a4_image = Image.new(mode="RGB", size=(2480, 3508), color="white")


cap = cv2.VideoCapture(videoFile)
count = 0
mod_x = 256 + 256
mod_y = 256 + 10

im_list = list()
while (cap.isOpened()):
    ret, frame = cap.read()
    if frame is None:
        break
    # 抽个帧
    if count % 2 == 0:
        print(f"正在处理第{count}帧")
        # 由于这个Module封装的太狠了,只能先保存再做读取
        cache = cv2.imwrite(cache_path, frame)
        Extract_Line_Draft_test.ExtractLine(cache_path, use_gpu=use_gpu)
        im = Image.open("output/output.png")
        im = im.resize((256, 256))
        im_list.append(im)
    count += 1

count = 0
im_count = len(im_list)
for x in range(2480-256 // mod_x):
    for y in range(3508 // mod_y):
        im = im_list[count % im_count]
        a4_image.paste(im, (80 + (x * mod_x), y * mod_y))
        count += 1

a4_image.save("out_a4.jpg")
cap.release()
print("文件生成完毕")

About

  1. 部分表情包来源网络,侵删。
  2. 高清版"胡桃摇"来源:哔哩哔哩-海螺张 再次感谢。
Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐