Python + OpenCV で雑コラ動画を作成する③ 雑コラ動画作成

  • 20
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

0. はじめに

前回(その② 静止画コラ作成)からの続きです。
前回行った静止画の顔へのオーバレイを動画に適用します。
以下のサイトを参考にしました。
【シリーズ】「pythonとOpenCVを用いたCVプログラミング 」第9回: OpenCV-python② 動画の入出力とhighGUIでのマウス、キーボードコールバック

1. 動画の入出力

まず始めに動画の入出力をしてみます。
単純に動画を読み込んでそのまま出力するだけです。
以下がコードです

export_movie.py
#coding=utf-8

import cv2

def export_movie():


    # 入力する動画と出力パスを指定。
    target = "target/test_input.mp4"
    result = "result/test_output.m4v" 

    # 動画の読み込みと動画情報の取得
    movie = cv2.VideoCapture(target) 
    fps    = movie.get(cv2.CAP_PROP_FPS)
    height = movie.get(cv2.CAP_PROP_FRAME_HEIGHT)
    width  = movie.get(cv2.CAP_PROP_FRAME_WIDTH)

    # 形式はMP4Vを指定
    fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')

    # 出力先のファイルを開く
    out = cv2.VideoWriter(result, int(fourcc), fps, (int(width), int(height)))

    # 最初の1フレームを読み込む
    if movie.isOpened() == True:
        ret,frame = movie.read()
    else:
        ret = False

    # フレームの読み込みに成功している間フレームを書き出し続ける
    while ret:

        # 読み込んだフレームを書き込み
        out.write(frame)

        # 次のフレームを読み込み
        ret,frame = movie.read()


if __name__ == '__main__':
    export_movie()

超簡単

2. 結果

容量が3倍近くなりましたが出力された動画を再生できました。
この辺はきちんとコーデックを導入しなければならないようです。
あと音は入っていません。

3. 雑コラ動画作成

動画の入出力には成功したのでいよいよ雑コラ動画を作成します。
と言っても動画の出力前に顔認識、オーバレイをするだけです。
動画ファイルから読み込んだフレームは画像ファイルと同じように扱えます。
あと、overlayOnPart関数の最後が戻り値の画像にアルファチャンネル(透過)を含まないように変わっています。
以下、コードです。

overlay_movie.py
#coding=utf-8

import cv2
import datetime
import numpy as np
from PIL import Image

def overlay_movie():

    # 入力する動画と出力パスを指定。
    target = "target/test_input.mp4"
    result = "result/test_output.m4v"  #.m4vにしないとエラーが出る

    # 動画の読み込みと動画情報の取得
    movie = cv2.VideoCapture(target) 
    fps    = movie.get(cv2.CAP_PROP_FPS)
    height = movie.get(cv2.CAP_PROP_FRAME_HEIGHT)
    width  = movie.get(cv2.CAP_PROP_FRAME_WIDTH)

    # 形式はMP4Vを指定
    fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')

    # 出力先のファイルを開く
    out = cv2.VideoWriter(result, int(fourcc), fps, (int(width), int(height)))

    # カスケード分類器の特徴量を取得する
    cascade_path = "haarcascades/haarcascade_frontalface_alt.xml"
    cascade = cv2.CascadeClassifier(cascade_path)

    # オーバーレイ画像の読み込み
    ol_imgae_path = "target/warai_otoko.png"    
    ol_image = cv2.imread(ol_imgae_path,cv2.IMREAD_UNCHANGED)

    # 最初の1フレームを読み込む
    if movie.isOpened() == True:
        ret,frame = movie.read()
    else:
        ret = False

    # フレームの読み込みに成功している間フレームを書き出し続ける
    while ret:

        # グレースケールに変換
        frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        # 顔認識の実行
        facerecog = cascade.detectMultiScale(frame_gray, scaleFactor=1.1, minNeighbors=1, minSize=(1, 1))

        if len(facerecog) > 0:
            # 認識した顔に画像を上乗せする
            for rect in facerecog:

                # 認識範囲にあわせて画像をリサイズ
                resized_ol_image = resize_image(ol_image, rect[2], rect[3])

                # オーバレイ画像の作成
                frame = overlayOnPart(frame, resized_ol_image, rect[0],rect[1])

        # 読み込んだフレームを書き込み
        out.write(frame)

        # 次のフレームを読み込み
        ret,frame = movie.read()

        # 経過を確認するために100フレームごとに経過を出力
        if movie.get(cv2.CAP_PROP_POS_FRAMES)%100 == 0:
            date = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")
            print(date + '  現在フレーム数:'+str(int(movie.get(cv2.CAP_PROP_POS_FRAMES))))

        # 長いので途中のフレームまでで終了する
#        if movie.get(cv2.CAP_PROP_POS_FRAMES) > 1000:
#            break

    print("完了")


def resize_image(image, height, width):

    # 元々のサイズを取得
    org_height, org_width = image.shape[:2]

    # 大きい方のサイズに合わせて縮小
    if float(height)/org_height > float(width)/org_width:
        ratio = float(height)/org_height
    else:
        ratio = float(width)/org_width

    resized = cv2.resize(image,(int(org_height*ratio),int(org_width*ratio)))

    return resized    

# PILを使って画像を合成
def overlayOnPart(src_image, overlay_image, posX, posY):

    # オーバレイ画像のサイズを取得
    ol_height, ol_width = overlay_image.shape[:2]

    # OpenCVの画像データをPILに変換
    # BGRAからRGBAへ変換
    src_image_RGBA = cv2.cvtColor(src_image, cv2.COLOR_BGR2RGB)
    overlay_image_RGBA = cv2.cvtColor(overlay_image, cv2.COLOR_BGRA2RGBA)

    # PILに変換
    src_image_PIL=Image.fromarray(src_image_RGBA)
    overlay_image_PIL=Image.fromarray(overlay_image_RGBA)

    # 合成のため、RGBAモードに変更
    src_image_PIL = src_image_PIL.convert('RGBA')
    overlay_image_PIL = overlay_image_PIL.convert('RGBA')

    # 同じ大きさの透過キャンパスを用意
    tmp = Image.new('RGBA', src_image_PIL.size, (255, 255,255, 0))
    # 用意したキャンパスに上書き
    tmp.paste(overlay_image_PIL, (posX, posY), overlay_image_PIL)
    # オリジナルとキャンパスを合成して保存
    result = Image.alpha_composite(src_image_PIL, tmp)

    # COLOR_RGBA2BGRA から COLOR_RGBA2BGRに変更。アルファチャンネルを含んでいるとうまく動画に出力されない。
    return  cv2.cvtColor(np.asarray(result), cv2.COLOR_RGBA2BGR)

if __name__ == '__main__':
    overlay_movie()

だんだん長くなってきました。

4. 結果


2016/03/11 21:57:04現在フレーム数:100.0
2016/03/11 21:57:16現在フレーム数:200.0
2016/03/11 21:57:28現在フレーム数:300.0



2016/03/11 22:08:59現在フレーム数:7100.0
完了


フレームサイズ718×480、24fps、5:00の動画に対してCore i5-4670 3.40GHz メモリ16GBの環境で実行しました。
結構時間がかかりましたが、雑コラ動画を出力できました。
動画認識結果.JPG

5. 最後に

が、これでは不完全です。
複数の顔があると全てに上書きしたり、
複数人.JPG

誤って認識した顔にも手当たり次第上書きしてしまいます。
誤認識.JPG

次回はこの問題の解決を目指します。
Python + OpenCV で雑コラ動画を作成する④ 課題への対処