38
37

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-03-12

##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 で雑コラ動画を作成する④ 課題への対処

38
37
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
37

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?