用讓新海誠本人驚訝的 AI 模型製作屬於你的動漫視訊

soulteary發表於2022-06-05

本文將介紹如何使用 GAN 模型來生成屬於你自己的動漫風格的視訊,為自己、喜歡的菇涼或者調皮可愛孩子生成一個別具一格的動漫風格的視訊。

本文操作難度較低,適合想要試玩了解 GAN 模型的同學。可以同時使用 CPU / GPU (包括 ARM M1)來完成。

寫在前面

這陣子在翻閱和學習一些模型相關的內容,當我看到大名鼎鼎的《你的名字》、《秒速五釐米》、《天氣之子》等經典作品的導演新海誠,因為一組對比圖發出了下面的感嘆的時候,我的好奇心一下子就上來了。

讓新海誠本人驚訝並轉發的模型效果

圖片中新海誠的感嘆內容翻譯過來的大概意思是:“很有趣,我感覺到了各種可能性。如果這是由美術工作人員提出的,電影將會重拍。笑”

在我看來,能夠得到如新海誠這樣的大佬翻牌子,或許這類模型值得一玩。於是,我試著用這類模型將我的結婚紀念視訊進行了風格轉換,發現效果還行啊。

一些經過模型處理過的視訊鏡頭

接著,我又針對一些以往的照片試了試這個“濾鏡”,發現針對部分照片來說,效果真的很不錯。

兩頭毛孩子進食

一大把香蕉

常見的街道綠植

獨樂樂不如眾樂樂,如果你也有計劃為喜歡的人、或者孩子,甚至是自己做一些卡通風格化的視訊/照片留念的話,那麼跟著我一起來折騰下本篇內容吧。

本篇將依次介紹兩種模型的使用,關於這兩個模型的簡單介紹,在本文結尾有提到,如果你感興趣的話,可以跳轉閱讀。

好了,我們先來進行一些準備工作吧。

準備工作

在開始為我們的視訊或者照片“加濾鏡”之前,我們需要先進行環境準備。

本篇文章為了更簡單一些,就不再在文章中展開如何進行模型的 Docker 映象封裝了,感興趣的同學可以自行翻閱上篇《使用 Docker 來執行 HuggingFace 海量模型》文章內容,來學習相關內容。

使用 Conda 簡化 Python 程式環境準備工作

和上篇文章一樣,我推薦使用 Conda 完成基礎的程式執行環境的安裝。

讓新海誠本人驚訝並轉發的模型

你可以從Conda 官方網站下載合適的安裝程式(安裝包比較大,大概 500M左右,需要一些耐心)。

接下來,我們先以 Mac 和 Ubuntu 為例,來演示如何完成環境的準備。

從上面的地址下載好 Conda 安裝檔案之後,一行命令完成程式安裝即可。

# 先進行 conda 的安裝
bash Anaconda3-2022.05.(你的安裝檔名稱).sh 

如果你在使用 Ubuntu,和我一樣,需要經常測試不同的模型和專案,可以在安裝完畢之後,執行下面的命令,讓 conda shell 環境常駐。

# 在完成安裝之後,可以考慮hi
eval "$(~/anaconda3/bin/conda shell.bash hook)"

至於 Mac,我更推薦什麼時候使用,什麼時候手動啟用 conda shell。比如在安裝完畢 conda,並初始化好了某一個程式的專用環境之後,我們可以通過執行 conda activate [環境名稱] 命令來啟用這個環境所需要的 shell。(關於如何初始化環境,請耐心往下看)

國內使用者,推薦在使用 Conda 時,先進行軟體源配置操作。這樣可以減少在下載軟體包過程中造成的不必要時間浪費。

編輯 vi ~/.condarc 檔案,在其中加入下面的內容(以“清華源”為例):

channels:
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
  - defaults
show_channel_urls: true

當我們完成了 ~/.condarc 的內容修改之後,先重啟 Shell,接著使用 conda info 就可以檢查軟體源是否配置成功了:

(base) soulteary@ubuntu:~# conda info

     active environment : base
...
           channel URLs : https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/linux-64
                          https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/noarch
                          https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/linux-64
                          https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/noarch
                          https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/linux-64
                          https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/noarch
                          https://repo.anaconda.com/pkgs/main/linux-64
                          https://repo.anaconda.com/pkgs/main/noarch
                          https://repo.anaconda.com/pkgs/r/linux-64
                          https://repo.anaconda.com/pkgs/r/noarch
...

可以看到,日誌輸出中包含了我們剛剛填寫的“清華源”。

接下來,我們來通過 conda 命令,來快速建立我們所需要的模型應用程式所需要的執行環境:

conda create -n my-anime-video python=3.10

上面的命令執行完畢之後,將會建立一個名為 my-anime-video 的基於 Python 3.10 的基礎執行環境;如果你需要其他版本的 Python 來執行自己的模型,可以調整命令中的版本號。

當環境建立完畢之後,我們需要先執行命令,將環境啟用(用過 GVM、NVM 的同學應該會很熟悉)。

conda activate my-anime-video

當環境啟用之後,我們的 shell 命令列提示的開始處將會出現這個環境名稱提示,告訴我們現在正位於這個環境之下:

(my-anime-video) # your shell command here...

當完成環境啟用之後,我們同樣先來完成軟體源的切換(上篇文章有詳細介紹,感興趣可以自行閱讀):

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

接著來完成 PyTorch 常用依賴的安裝:

pip install torch torchvision torchaudio 

最後安裝一個用來“偷懶”的工具 Towhee

pip install towhee

至於這個工具能省多大事,我先賣個關子不提,看到後面你就知道了。

安裝 ffmpeg 來處理多媒體素材

如果你只是希望處理圖片,那麼這部分可以跳過,如果你希望處理的內容包含視訊,我們需要藉助 ffmpeg 這個工具的力量。

在 macOS 中,我們可以通過 brew 來進行安裝:

brew install ffmpeg

在 Linux 環境中,比如 Ubuntu,我們可以通過 apt 來進行安裝:

apt install ffmpeg -y

在 ffmpeg 安裝就緒之後,我們來針對視訊進行處理,將視訊轉換為待處理的圖片。

比如,我們要將當前目錄的視訊 wedding-video.mp4 按照每秒 25 幀拆解為圖片,並將圖片儲存在當前目錄的 images 目錄下,可以執行下面的命令:

ffmpeg -i ./wedding-video.mp4 -vf fps=25 images/video%d.png

當命令執行完畢之後,我們將得到滿滿一個資料夾的圖片,它們都將以命令中的 video 為字首,數字為結尾。我選擇的視訊檔案接近 15 分鐘,當轉換完畢之後,得到了兩萬多張圖片。

ls images/| wc -l
21628

當上面的準備工作都就緒之後,我們來看看如何使用兩種模型來生成屬於我們自己的卡通/動漫風格的視訊。

先來看看第一個模型:CartoonGAN

CartoonGAN

關於這個模型,我找到了一個 HuggingFace 上的線上試玩地址:https://huggingface.co/spaces/akiyamasho/AnimeBackgroundGAN

這個線上工具來自一位日本開發者,在它的另外一個專案 AnimeBackgroundGAN-Shinkai (新海誠風格)中,我們能夠找到一個預訓練模型(關聯專案中還有宮崎駿、細田守、今敏的風格),而在 GitHub 中,我們能夠找到這個專案對應的程式碼倉庫 venture-anime/anime-background-gan-hf-space

在進行上文提到的視訊素材(大量圖片)處理之前,我們先來試著在本地執行起來一個同款的 Web 工具,來驗證模型程式碼是否能夠正確執行。

驗證專案模型效果

這個專案可以使用 CPU 或 GPU 執行,如果你手頭有 macOS、Ubuntu 的話,可以直接執行。

不過原始專案對於最新版本的 PyTorch 支援有一些問題,使用 GPU 執行會報錯,並且不支援指定顯示卡執行,以及專案依賴有一些問題,所以我做了一個 fork 版本:https://github.com/soulteary/anime-background-gan-hf-space

我們使用 Git 下載專案,然後切換到專案目錄中:

git clone https://github.com/soulteary/anime-background-gan-hf-space.git
cd anime-background-gan-hf-space

在進一步進行之前,我們需要確認已經用 conda 將之前準備的 Python 環境啟用,如果你不確定或者還沒有進行切換,可以再次執行下面的命令(重複執行沒有副作用):

conda activate my-anime-video

在切換到 my-anime-video 這個環境之後,我們使用 Python 啟動專案:

python app.py

在命令執行之後(可能需要稍等片刻),我們將得到類似下面的日誌:

/Users/soulteary/anaconda3/envs/my-anime-video/lib/python3.10/site-packages/gradio/deprecation.py:40: UserWarning: `optional` parameter is deprecated, and it has no effect
  warnings.warn(value)
Running on local URL:  http://127.0.0.1:7860/

To create a public link, set `share=True` in `launch()`.

然後開啟瀏覽器,輸入上面日誌中提示的地址 http://127.0.0.1:7860/,就能夠看到一個線上的 Web 工具介面啦。

Hugging Face 同款網頁應用介面

要進行模型和應用程式碼驗證也很簡單,點選左邊的圖片上傳區域,上傳一個你想要測試的圖片,然後點選“提交”即可。

以星爺帥照進行測試

可以看到,一張頗有卡通感的圖片出現在了右側。這裡如果使用 CPU 執行,一般需要 3~10s 來處理一張圖,如果使用 GPU,則一般需要 1s 左右。

在完成了程式和模型的驗證之後,我們來編寫批量處理圖片的程式。

編寫模型呼叫程式進行批量圖片處理

完整程式,我已經上傳到了 GitHub https://github.com/soulteary/have-fun-with-AnimeGAN/blob/main/CartoonGAN/app.py 方便大家自取。

原始的專案中,並不支援批量讀取圖片,並且預設會載入四個模型,比較浪費資源,所以我這裡進行了一些功能完善。

import argparse
import glob, os
import time
from pathlib import Path
from PIL import Image

import torch
import numpy as np
import torchvision.transforms as transforms
from torch.autograd import Variable
from network.Transformer import Transformer
from huggingface_hub import hf_hub_download

import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def parse_args():
    desc = "CartoonGAN CLI by soulteary"
    parser = argparse.ArgumentParser(description=desc)
    parser.add_argument('--model', type=str, default='Shinkai', help='Shinkai / Hosoda / Miyazaki / Kon')
    parser.add_argument('--input', type=str, default='./images', help='images directory')
    parser.add_argument('--output', type=str, default='./result/', help='output path')
    parser.add_argument('--resize', type=int, default=0,
                        help='Do you need a program to adjust the image size?')
    parser.add_argument('--maxsize', type=int, default=0,
                        help='your desired image output size')
    """
    If you want to resize, you need to specify both --resize and --maxsize
    """
    return parser.parse_args()

def prepare_dirs(path):
    Path(path).mkdir(parents=True, exist_ok=True)


arg = parse_args()


enable_gpu = torch.cuda.is_available()

if enable_gpu:
    # If you have multiple cards,
    # you can assign to a specific card, eg: "cuda:0"("cuda") or "cuda:1"
    # Use the first card by default: "cuda"
    device = torch.device("cuda")
else:
    device = "cpu"

def get_model(style):
    # Makoto Shinkai
    if style == "Shinkai":
        MODEL_REPO_SHINKAI = "akiyamasho/AnimeBackgroundGAN-Shinkai"
        MODEL_FILE_SHINKAI = "shinkai_makoto.pth"
        model_hfhub = hf_hub_download(repo_id=MODEL_REPO_SHINKAI, filename=MODEL_FILE_SHINKAI)
    # Mamoru Hosoda
    elif style == "Hosoda":
        MODEL_REPO_HOSODA = "akiyamasho/AnimeBackgroundGAN-Hosoda"
        MODEL_FILE_HOSODA = "hosoda_mamoru.pth"
        model_hfhub = hf_hub_download(repo_id=MODEL_REPO_HOSODA, filename=MODEL_FILE_HOSODA)
    # Hayao Miyazaki
    elif style == "Miyazaki":
        MODEL_REPO_MIYAZAKI = "akiyamasho/AnimeBackgroundGAN-Miyazaki"
        MODEL_FILE_MIYAZAKI = "miyazaki_hayao.pth"
        model_hfhub = hf_hub_download(repo_id=MODEL_REPO_MIYAZAKI, filename=MODEL_FILE_MIYAZAKI)
    # Satoshi Kon
    elif style == "Kon":
        MODEL_REPO_KON = "akiyamasho/AnimeBackgroundGAN-Kon"
        MODEL_FILE_KON = "kon_satoshi.pth"
        model_hfhub = hf_hub_download(repo_id=MODEL_REPO_KON, filename=MODEL_FILE_KON)

    model = Transformer()
    model.load_state_dict(torch.load(model_hfhub, device))
    if enable_gpu:
        model = model.to(device)
    model.eval()
    return model

def inference(img, model):
    # load image
    input_image = img.convert("RGB")
    input_image = np.asarray(input_image)
    # RGB -> BGR
    input_image = input_image[:, :, [2, 1, 0]]
    input_image = transforms.ToTensor()(input_image).unsqueeze(0)
    # preprocess, (-1, 1)
    input_image = -1 + 2 * input_image

    if enable_gpu:
        logger.info(f"CUDA found. Using GPU.")
        # Allows to specify a card for calculation
        input_image = Variable(input_image).to(device)
    else:
        logger.info(f"CUDA not found. Using CPU.")
        input_image = Variable(input_image).float()

    # forward
    output_image = model(input_image)
    output_image = output_image[0]
    # BGR -> RGB
    output_image = output_image[[2, 1, 0], :, :]
    output_image = output_image.data.cpu().float() * 0.5 + 0.5

    return transforms.ToPILImage()(output_image)


prepare_dirs(arg.output)

model = get_model(arg.model)

enable_resize = False
max_dimensions = -1
if arg.maxsize > 0:
    max_dimensions = arg.maxsize
    if arg.resize :
        enable_resize = True

globPattern = arg.input + "/*.png"

for filePath in glob.glob(globPattern):
    basename = os.path.basename(filePath)
    with Image.open(filePath) as img:
        if(enable_resize):
            img.thumbnail((max_dimensions, max_dimensions), Image.Resampling.LANCZOS)

        start_time = time.time()
        inference(img, model).save(arg.output + "/" + basename, "PNG")
        print("--- %s seconds ---" % (time.time() - start_time))

上面這段一百來行的程式大概做了這麼幾件事,會根據傳遞引數來動態的載入必要的模型,而不是所有的模型。預設情況下,它會讀取當前目錄 images 子目錄的所有圖片,並依次呼叫 CPU / GPU 來進行圖片處理,並將處理結果儲存在 result 子目錄中。如果你希望對輸出圖片進行尺寸調整,可以傳遞引數來做限制。

我們來試著執行下這個程式:

python app.py --model=Shinkai

當命令開始執行後,我們就能夠批量處理視訊圖片啦,如果不出意外,你將會看到類似下面的日誌:

...
--- 1.5597078800201416 seconds ---
INFO:__main__:Image Height: 1080, Image Width: 1920
--- 0.44031572341918945 seconds ---
INFO:__main__:Image Height: 1080, Image Width: 1920
--- 1.5004260540008545 seconds ---
INFO:__main__:Image Height: 1080, Image Width: 1920
--- 1.510758876800537 seconds ---
INFO:__main__:Image Height: 1080, Image Width: 1920
--- 1.362170696258545 seconds ---
INFO:__main__:Image Height: 1080, Image Width: 1920
...

這裡需要處理的時間可能會比較久,如果我們有 GPU 會極大的提升速度,或者適當調整輸出圖片的大小,也能夠在使用 CPU 進行渲染的情況下,得到比較快的處理速度。

當程式執行結束之後,我們在 result 目錄中,就能夠得到經過 AI 模型處理的圖片內容了,這時我們只要再次使用 ffmpeg 將圖片轉換為視訊就好啦:

ffmpeg -f image2 -r 25 -i result/video%d.jpg -vcodec libx264 -crf 18  -pix_fmt yuv420p result.mp4

接下來,我們先來聊聊如何使用 GPU 進行提速,然後再來看看有沒有更通用的、低成本的提速方案。

使用 GPU 進行模型執行提速

如果你沒有 GPU,想用最小成本來玩,那麼可以直接閱讀下一小節內容。

言歸正傳,假設我們要處理上萬甚至十萬張圖片,最好的方式便是使用 GPU 進行資料處理。考慮到當前一塊能高效跑 AI 模型的具備大視訊記憶體的顯示卡至少一萬起步,我更推薦使用按量付費的雲主機,大概每小時 10~20 塊錢,跑個個把小時,就能拿到你想要的結果啦。我個人測試下來,幾個雲廠商帶有顯示卡的主機都差不多,大家根據自己情況和喜歡的方式選擇就行,如果是學生身份,或許有些平臺還能有教育折扣。

為了能夠相對快速的得到結果,我選擇了一臺雙卡雲主機,在不做程式的深度優化前,能夠直接讓資料處理時間減半。

想要讓兩張顯示卡各處理一半的資料,我們可以選擇修改上面的程式,也可以用 Linux Shell 把資料直接分為兩堆兒。作為一個懶人,我們選擇後者。(如果你只有一張卡,可以跳過這個步驟)

先來確認當前待處理的資料總量,有兩萬多張圖片。

ls images/| wc -l
21628

接著使用組合命令,將一半的圖片挪到新建立的資料夾中:

mkdir images2
ls | sort | head -10814 | xargs -I {} mv {} images2/

在準備完資料之後,我們同樣需要對程式做一點小的調整,讓程式分別使用兩張不同的顯示卡裝置,我們可以選擇像程式讀取輸入輸出資料夾使用引數相同的方式,來讓使用者指定顯示卡裝置;也可以選擇更簡單的方式,將上面的程式複製一份,調整原來的程式和新程式中要呼叫的顯示卡裝置名稱,比如,將 device = torch.device("cuda") 調整為 device = torch.device("cuda:0")device = torch.device("cuda:1")

當完成上述調整和準備之後,分別使用 python 執行兩個程式即可:python app1.pypython app2.py。如果你不能保證你通過 SSH 連線到伺服器的會話穩定,那麼可以試試使用 screentmux 這類工具。

程式開始執行之後,接下來就是漫長的等待,在處理過程中,我們可以使用 nvidia-smi 檢視顯示卡狀態,可以看到如果這臺“電腦”擱家裡,應該是蠻費電的,而且大概率會很吵。

nvidia-smi 

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 450.102.04   Driver Version: 450.102.04   CUDA Version: 11.0     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Tesla V100-SXM2...  On   | 00000000:00:08.0 Off |                    0 |
| N/A   54C    P0   263W / 300W |  25365MiB / 32510MiB |    100%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   1  Tesla V100-SXM2...  On   | 00000000:00:09.0 Off |                    0 |
| N/A   55C    P0   265W / 300W |  25365MiB / 32510MiB |    100%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|    0   N/A  N/A     61933      C   python                          25363MiB |
|    1   N/A  N/A     62081      C   python                          25363MiB |
+-----------------------------------------------------------------------------+

當程式處理完所有圖片之後,會自動退出。這個時候,我們可以再次使用 ffmpeg 將圖片還原為視訊:

time ffmpeg -f image2 -r 25 -i result/video%d.png -vcodec libx264 -crf 18  -pix_fmt yuv420p result.mp4

當程式執行完畢,我們將看到類似下面的日誌:

Output #0, mp4, to 'result.mp4':
  Metadata:
    encoder         : Lavf58.29.100
    Stream #0:0: Video: h264 (libx264) (avc1 / 0x31637661), yuv420p, 1920x1080, q=-1--1, 25 fps, 12800 tbn, 25 tbc
    Metadata:
      encoder         : Lavc58.54.100 libx264
    Side data:
      cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: -1
frame=21628 fps= 75 q=-1.0 Lsize= 1135449kB time=00:14:25.00 bitrate=10753.3kbits/s speed=2.99x    
video:1135185kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.023257%
[libx264 @ 0x55779059b380] frame I:253   Avg QP:15.48  size:194386
[libx264 @ 0x55779059b380] frame P:6147  Avg QP:17.74  size: 95962
[libx264 @ 0x55779059b380] frame B:15228 Avg QP:20.43  size: 34369
[libx264 @ 0x55779059b380] consecutive B-frames:  4.7%  2.9%  4.0% 88.4%
[libx264 @ 0x55779059b380] mb I  I16..4: 16.0% 47.1% 36.9%
[libx264 @ 0x55779059b380] mb P  I16..4:  7.9% 13.9%  6.1%  P16..4: 31.2% 18.5%  9.3%  0.0%  0.0%    skip:13.0%
[libx264 @ 0x55779059b380] mb B  I16..4:  1.6%  1.6%  0.5%  B16..8: 34.3%  9.6%  2.7%  direct:11.8%  skip:37.9%  L0:42.2% L1:42.3% BI:15.5%
[libx264 @ 0x55779059b380] 8x8 transform intra:48.1% inter:59.5%
[libx264 @ 0x55779059b380] coded y,uvDC,uvAC intra: 47.2% 66.3% 35.9% inter: 30.7% 35.6% 1.6%
[libx264 @ 0x55779059b380] i16 v,h,dc,p: 39% 32%  6% 23%
[libx264 @ 0x55779059b380] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 27% 20% 26%  4%  5%  4%  5%  4%  5%
[libx264 @ 0x55779059b380] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 33% 20% 16%  5%  6%  6%  5%  5%  4%
[libx264 @ 0x55779059b380] i8c dc,h,v,p: 50% 21% 21%  8%
[libx264 @ 0x55779059b380] Weighted P-Frames: Y:24.2% UV:17.1%
[libx264 @ 0x55779059b380] ref P L0: 49.1% 14.5% 22.1% 12.4%  1.9%
[libx264 @ 0x55779059b380] ref B L0: 81.3% 14.0%  4.7%
[libx264 @ 0x55779059b380] ref B L1: 93.6%  6.4%
[libx264 @ 0x55779059b380] kb/s:10749.30

real    4m49.667s
user    78m36.497s
sys    0m44.806s

當我開啟處理完畢的視訊,就個人觀感而言,光線充足的場景下,模型的發揮還是比較好的。這裡由於上傳圖片壓縮,效果沒有本地看到的驚豔,感興趣的同學不妨試試。

部分視訊中的畫面截圖

或許你想知道,有沒有什麼不用 GPU 也能進行一些加速效果的方案呢?答案是有的。

使用平行計算和流式處理來加速圖片處理

Python 3 支援 concurrent API,來做平行計算。不過處理併發計算,一般還需要折騰佇列,涉及到多執行緒排程,涉及到流式 IO 等等一坨麻煩事。而且併發程式碼除錯也比較噁心,尤其是在 Python 中...(個人觀點)

還記得我在上文中曾經 pip installtowhee 麼,這個軟體包裡包含了一些快捷的工具方法,能夠簡化上面一系列麻煩事情,讓我們處理相同的事情,花更少的代價,因為很多時候,一些基本的帶有複雜度的麻煩事,應該交給基礎工具來解決,而不是都由開發者吭哧吭哧的填。

我們以上文中一百多行的模型應用程式碼舉例,如果換用 Towhee 做一個“平替”程式,那麼可以這樣來實現:

import towhee

towhee.glob('./*.png') \
    .image_decode() \
    .img2img_translation.cartoongan(model_name = 'Shikai') \
    .save_image(dir='./result')
    .to_list()

幾行程式碼,就完成了核心邏輯:批量讀取圖片、將圖片進行 RGB 編碼轉換,然後將圖片資料傳遞給模型進行處理,最後進行模型計算結果儲存到某個資料夾裡。

接下來,我們再對上面的程式碼進行一些調整,來完成上面提到的麻煩事,平行計算。其實需要的改動很少,只需要我們在“處理圖片編碼轉換”之前,加上一行 .set_parallel(5) 來告訴程式,我們要使用多個執行緒來搞事情就行啦。

當程式執行起來的時候,就會預設呼叫 5 個併發來執行計算,所以即使沒有 GPU,只使用 CPU 的情況下,我們也能夠獲取更高的執行效率。

import towhee

towhee.glob('./*.png') \
    .set_parallel(5) \
    .image_decode() \
    .img2img_translation.cartoongan(model_name = 'Shikai') \
    .save_image(dir='./result')
    .to_list()

在玩 Towhee 的過程中,我也遇到了一些小問題。比如 Towhee 這個工具的設計初衷是為了科學計算,所以像是儲存圖片、折騰視訊檔案都不是它的主業,預設情況下,它會以 UUID 的形式來儲存處理後的圖片。

為了能夠讓圖片結果為我們所用,輸出的圖片能夠按照原始檔名稱進行儲存,讓模型處理完的結果能夠“還原成視訊”。我特別向它的開源倉庫提交了一個 PR (\#1293)來解決這個問題。

當然,我們也需要對上面的幾行程式碼做一些額外的調整,新增一些引數:

import towhee

towhee.glob['path']("./*.png") \
    .set_parallel(5) \
    .image_decode['path','img']() \
    .img2img_translation.cartoongan['img', 'img_new'](model_name = "Shikai") \
    .save_image[('img_new','path'), 'new_path'](dir="./result") \
    .to_list()

不過,截止本文釋出的時候,包含我的 PR 的主幹版本,還沒有被正式釋出,所以暫時還需要安裝 Python PyPI 每日構建的開發版的軟體包來使用這個功能。

pip install -i https://test.pypi.org/simple/ towhee==0.6.2.dev48

如果大家覺得這個偷懶的玩法還不錯,可以代我去官方開源倉庫去提 Issue,催催維護團隊更新版本。

那麼,為啥作為 Towhee 使用者的我不去催呢?其實是因為不好意思。為啥不好意思呢?你接著看下去就知道啦。

AnimeGAN

在聊完 CartoonGAN 之後,我們來試試另外一個模型:AnimeGAN。目前模型有三個版本,除了第三個版本外,都是免費使用的開源專案。

為了方便演示以及一些特別的原因(文末詳述),我這裡選擇開源且較為穩定的第二個版本。

在開始折騰之前,同樣也可以先試試它的線上試玩的 Demo:https://huggingface.co/spaces/akhaliq/AnimeGANv2

還是先來進行本地專案驗證,來驗證模型程式碼是否能夠正確執行。

驗證專案模型效果

考慮到文章篇幅和工程師“美德”(偷懶),我們就不再折騰冗長的模型呼叫程式程式碼了,我選擇繼續用 Python 版 “jQuery” 來 “Write Less, Do More”:

import towhee

arg = parse_args()
towhee.glob['path']("./*.png") \
    .set_parallel(5) \
    .image_decode['path', 'img']() \
    .img2img_translation.animegan['img', 'new_img'](model_name ="Shinkai") \
    .save_image[('new_img', 'path'), 'new_path'](dir="./result", format="png") \
    .to_list()

邏輯和上文中呼叫 CartoonGAN 一樣,唯一的差別是使用了 animegan 的模型。

我們將上面的程式碼儲存為 lazy.py,然後從網上隨便找些圖片放在程式所在的目錄,執行 python lazy.py,稍等片刻就能夠在程式同級目錄發現一個新的名為 result 的資料夾,裡面放著的就是我們用模型處理過的圖片啦。

相關程式碼,我已經上傳到了 https://github.com/soulteary/have-fun-with-AnimeGAN/blob/main/AnimeGAN/lazy.py 感興趣的同學可以自取。

在驗證完畢模型效果之後,我們同樣可以使用這個模型來生成一段卡通風格的視訊。

雖然上文中使用 ffmpeg 拼合視訊的效率非常高,但是畢竟要執行額外的兩條命令,有沒有什麼辦法可以進一步偷懶呢?答案顯然是有的。

編寫模型呼叫程式進行視訊處理

不知道是否還有同學記得,在上篇文章中我提到的 Towhee 核心開發者之一的 @候傑 同學。在我死纏爛打之下,他幫我這個 Python 菜鳥,搞了一個 read_video 方法。

所以,上文中需要先用 ffmpeg 把視訊轉換為圖片,然後在模型處理之後,再把圖片拼合成視訊的繁瑣操作,就可以被幾行程式碼替代掉啦!相比較之前的玩法,新的程式碼行數瞬間縮短到了五行左右(算空行也就六行),節約了原本需要寫的 80% 的程式碼行數。

import towhee

towhee.read_video("./video.mp4") \
    .set_parallel(5) \
    .img2img_translation.animegan['img', 'new_img'](model_name = 'Skinkai') \
    .to_video('./result.mp4', 'x264', 15)

上面這幾行程式碼執行下來,分別會自動套用模型處理視訊的每一幀內容,然後將模型處理結果重新編碼為每秒 15 幀的視訊檔案。實際使用的時候,你可以根據自己的需求進行位元速率調整。

為了方便你的使用,我也將程式碼上傳到了 GitHub,需要的同學可以自取。

針對視訊檔案進行快速預覽

相比較圖片內容處理,相同解析度的視訊的資料量其實會大不少。

所以如果我們想對視訊進行一個快速效果預覽,可以考慮在程式碼中新增一個尺寸調整的功能,將每一幀需要處理的圖片尺寸進行縮放,減少計算量。

一如上文中的偷懶原則,想要搞定這個“需求”的方法也很簡單,只需要加一行 image_resize 在合適的位置:

import towhee

towhee.read_video("./video.mp4") \
    .set_parallel(5) \
    .image_resize(fx=0.2, fy=0.2) \
    .img2img_translation.animegan['img', 'new_img'](model_name = 'Skinkai') \
    .to_video('./result.mp4', 'x264', 15)

這部分的程式碼,我依舊上傳到了 GitHub,希望對你有幫助。

其他

好啦!到這裡為止,我已經講完了:

  • 如何準備一個快速上手的 Python 環境
  • 如何快速上手使用 CartoonGAN、AnimeGAN 兩個模型;
  • 如何用這兩個模型處理圖片或者視訊;
  • 如何使用 Python 模型裡的 “jQuery”:Towhee 來偷懶,少寫程式碼多搞事情。
  • 如何進行 GitHub 上“野生”模型專案的基本調優和封裝

接下來,我們來簡單聊聊文章開頭提到的模型。

關於 AnimeGAN 和 CartoonGAN

關於使用 GAN(生成對抗網路) 模型來對圖片做動漫卡通風格化處理,就國內而言,目前有兩個知名度較高的國產專案。

一個是來自清華大學團隊在 2018 年公開的 CartoonGAN,該模型的論文中選 CVPR 2018,在 GitHub 上有 700 餘顆星星。另外一個是來自湖北科技大學,在 2019 年公開的 AnimeGAN,該模型至今已迭代了三個版本(前兩個版本開源),並在 GitHub 上累計收穫了近 8 千顆星星。

關於兩個模型和論文,媒體都曾進行過報導宣傳:《實景照片秒變新海誠風格漫畫:清華大學提出CartoonGAN》《強烈安利試試這個!效果爆炸的漫畫變身AI,火到伺服器幾度擠爆》,其中 AnimeGAN 處理的圖片結果,甚至得到了新海誠的感嘆。

我個人使用下來,CartoonGAN 和 AnimeGAN 各有優勢和不足,至於模型效果,以及各自適合哪些場景。相信聰明的讀者們,可以通過本文提到的方式,和自己的實踐來找到答案。

這兩個專案的開源倉庫地址:

目前,因為 AnimeGAN v3 正在進行商業化嘗試,並且是閉源釋出,為了不對作者造成影響,這裡就先不做相關模型封裝和嘗試啦。

開源不易,模型專案開源尤為不易,能夠做商業化轉型更是不易,需要社群、需要國人同胞的支援和鼓勵。只有不斷支援和反饋開源生態,國內的開源生態才有可能向好的方向變化,而當生態變好之時,我們這些從業者定然能夠從中獲得更多的益處。

最後

在一週前,我在朋友圈曬過使用模型來處理之前結婚的紀念視訊,有不少朋友點贊和表示好奇如何“折騰自己的照片或視訊”,當時承諾大家會出一篇教程,趁著端午節,趕出了這篇內容,希望大家玩的愉快。

最後,再次感謝被我死纏爛打之下新增了新功能的 @侯傑 同學,同樣感謝為我的 PR 提供了大量建議,幫助我把模型傳遞到 Towhee Hub,解決國內下載模型緩慢問題的、來自彩雲之南的程式媛 @室餘 妹紙。

--EOF


本文使用「署名 4.0 國際 (CC BY 4.0)」許可協議,歡迎轉載、或重新修改使用,但需要註明來源。 署名 4.0 國際 (CC BY 4.0)

本文作者: 蘇洋

建立時間: 2022年06月04日
統計字數: 20370字
閱讀時間: 41分鐘閱讀
本文連結: https://soulteary.io/2022/06/...

相關文章