Inpaintingを動画で試してみた【光学迷彩】

AI実装

Pixel 6の消しゴムマジックみたいに写真の中のモノを消してしまう「Inpainting」というAI技術があります。前にPythonをつかって実装してみる記事を書きましたが↓のようにきれいに消すことができます。

オリジナル/Inpainting

Inpaintingの詳細は↓をご覧ください。

1枚の画像のInpainting処理にかかる時間も短かったので動画でもできそう😎

ということで動画で試してみました👍

消えた!?というより光学迷彩みたいな感じになりました!

No03_GSHELL_HD_B_02
(c)1995 士郎正宗/講談社・バンダイビジュアル・MANGA ENTERTAINMENT

光学迷彩と言えば攻殻機動隊ですよね。最初の劇場版アニメの映画が公開されたのが1995年なので、もう25年以上昔の作品なのですが、ようやく少し追いついてきた感じでしょうか。面白い作品ですので見たことない方は是非見て下さい。

それでは、やり方を説明していきます👍

マスク画像について

Inpainting処理を実施するためにはマスク画像を用意する必要があります。

オリジナル/mask

動画にするとなるとマスク画像を大量に準備する必要があるのですが、これを準備するのは面倒です。

そこで今回は物体検出モデルであるYOLOv5のバウンディングボックスをうまいことつかってみます。物体検出したエリアをマスク画像とするスクリプトをつくってみる!ということです👍

YOLOv5にはバウンディングボックスの位置を出力する機能があります。YOLOv5で物体検出する際に、「–save-txt」を付けるだけで位置情報の.txtを出力することができます。↓の感じです。

python detect.py --source ./data/images/ --weights yolov5s.pt --conf 0.4 --save-txt

YOLOv5って!?という方は↓の記事をご参照ください。

バウンディングボックスからマスク画像をつくる

  • YOLOから出力される位置情報について

–save-txtを有効にて実行すると./runs/detect/exp/labelsディレクトリ下へ大量の.txtファイルが出力されると思います。その.txtファイルにどのように位置情報が格納されているか説明していきます。

適当に.txtファイルを開いてみると数値の羅列になっていると思います。これは上図のように左から「class」「x座標」「y座標」「w:幅」「h:高さ」の情報になっています。上図右の絵を見てもらうと分かりやすいと思います。値は正規化されたモノになっています。

この情報を元にマスク画像をつくると↓のようになります。

.txtファイル1つが1フレームに該当しますので、 ./runs/detect/exp/labelsディレクトリ下の全ての.txtファイルでマスク画像をつくればOKです。

Inpainting動画をつくる

マスク画像をつくった後は以下のような作業を実施します。

  • 動画を画像化
  • Inpainting処理
  • Inpainting画像を動画化

の順に実施していけばOKです。

動画を画像化

動画の保存場所や名前を変更して↓のスクリプトを実行してください。

#
import cv2
import os
movie_name = 'Road' #動画の名前
path ='./data/videos/'+movie_name #動画を保存してある場所のパス
movie = path +'.mp4' #動画の拡張子

count = 0
cap = cv2.VideoCapture(movie)
 
while True:
    ret, frame = cap.read()
    if ret == True:
        count += 1
        cv2.imwrite("./data/image/"+ str(count) +'.png', frame) #動画を保存する場所のパス
    else:
        break

画像サイズですが、大きすぎるとInpainting処理が難しくなるので適度にしてください。私は640×360pixelで試しました。

width = 640
hight = 360

files = sorted(glob.glob("./data/image/*.png"), key=natural_keys)
for i, file in enumerate(files):
    img = cv2.imread(file)
    img2 = cv2.resize(img, dsize=(width, hight))
    cv2.imwrite('./data/image/'+str(i)+'.png', img2)

Inpainting処理

次に動画から切り出した画像と、YOLOv5のバウンディングボックスからつくったマスク画像をつかってInpainting処理を実施していきます。前の記事でもやり方を紹介していますが、複数枚の画像を一気にInpainting処理できなかったので、PyTorchをつかったコードで処理しました。

参考にしたコードは↓です。

GitHub - csqiangwen/DeepFillv2_Pytorch
Contribute to csqiangwen/DeepFillv2_Pytorch development by creating an account on GitHub.

使い方は

  • pretrained modelのダウンロード
  • 作業ディレクトリ下へpretrained_modelディレクトリをつくり、ダウンロードしたファイルを移動
  • AnacondaPromptなどで必要なライブラリをインストールし以下のコードを実行
python test.py --baseroot ./image/ --baseroot_mask ./mask/ --results_path ./results --activation elu
#image, maskにはそれぞれ動画から切り出した画像、バウンディングボックスからつくったマスク画像のパスを指定

基本的には前の記事で実装した手法と同じです。

Inpainting画像を動画化

Inpainting処理を実行するとresultsディレクトリへ画像が出力されます。出力された画像を下記スクリプトで動画化していきます。

import glob
movie_name = 'Road_results' #動画の名前
frame_size = 3  #FHD=0, 4K=1, iphone=2, small=3
frame_rate = 30.0  #FPS

### ファイル連番の指定
import re
def atoi(text):
    return int(text) if text.isdigit() else text
def natural_keys(text):
    return [ atoi(c) for c in re.split(r'(\d+)', text) ]
####

images = sorted(glob.glob('./data/Road_results/*.png'), key=natural_keys) #Inpaint画像を指定

### 動画サイズ
if frame_size == 0:
   #FHD動画
   width = 1920
   height = 1080
elif frame_size ==1:
   #4K動画
   width = 3840
   height = 2160
elif frame_size ==2:
   #iphone動画
   width = 1080
   height = 1920
else:
   #small
   width = 640
   height = 360
####

fourcc = cv2.VideoWriter_fourcc('m','p','4','v')
video = cv2.VideoWriter('./data/Road_results/'+movie_name+'.mp4', fourcc, frame_rate, (width, height))
#動画保存先パスを指定
   
print("動画変換中...")

for i in range(len(images)):
    img = cv2.imread(images[i])
    img = cv2.resize(img,(width,height))
    video.write(img) 
       
video.release()
print("動画変換完了")

以上が手順となります。

Inpainting動画を確認する

YOLOで物体検出した場所がうまく消せていると思います。

今回はYOLOv5のバウンディングボックスをマスク画像としましたが、セグメンテーションモデルであるYolactやU-Netの出力をマスク画像にすると、もっと光学迷彩っぽくなるかな?!と思います。機会があれば試してみます👍

セグメンテーションモデルは過去に記事にしています。併せてご覧ください。

コメント

タイトルとURLをコピーしました