前回の記事で動画の中から人の顔を検出してモザイクをかけて表示することをやりました。
その後、動画を保存しようとする際の設定が複雑であることが分かったので、自分用の備忘録もかねてまとめておこうと思います。
最終的なコードはこちらになります。
import cv2
def mosaic(img, mosaic_rate):
w = img.shape[1]
h = img.shape[0]
img = cv2.resize(img, (int(w * mosaic_rate), int(h * mosaic_rate)))
img = cv2.resize(img, (w, h), interpolation = cv2.INTER_NEAREST)
return img
cascade_file= cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
clas = cv2.CascadeClassifier(cascade_file)
cap = cv2.VideoCapture("movie.mp4")
#動画を保存
fmt = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
fps = 20.0
size = (640, 360)
writer = cv2.VideoWriter('./test.mp4', fmt, fps, size)
while (cap.isOpened()):
ret, frame = cap.read()
if not ret:
break
#フレームのサイズを保存するサイズに合わせる
frame=cv2.resize(frame, size)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
face_list = clas.detectMultiScale(gray, scaleFactor = 1.1, minNeighbors = 3, minSize=(30,30))
for x, y, w, h in face_list:
frame[y : y+h, x : x+w] = mosaic(frame[y : y + h, x : x + w], 0.05)
#1フレームずつ画像を書き込む
writer.write(frame)
cv2.imshow("frame", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
writer.release()
cap.release()
cv2.destroyAllWindows()
動画保存の形式を指定する
この4行が重要。でも分かりにくい。
fmt = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
fps = 20.0
size = (640, 360)
writer = cv2.VideoWriter('./test.mp4', fmt, fps, size)
簡単に言うと
fmt = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
fps = 20.0
size = (640, 360)
この3行で必要な引数を変数にそれぞれ代入し・・・
cv2.VideoWriter('./test2.mp4', fmt, fps, size)
この部分で3つの変数を引数としてVideoWriterに渡しています('./test.mp4'は動画を保存するディレクトリと保存する動画の名前)。そしてそれをwriter変数に代入、という流れです。
cv2.VideoWriter_fourcc
自分的に最初わからなかったのが
cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
この部分です。公式リファレンスを探してもはっきり書いてある記事が見当たらない気がします。情報がないながらもヒントになったのがOpenCV-Python チュートリアルのこの記事と、OpenCVのドキュメントのこのページでした。
動画ファイルとして保存するために VideoWriter 型のオブジェクトを生成します.第1引数は保存する動画名(例: output.avi)を指定します.第2~第4引数は FourCC コード(詳細は次の段落で説明)を指定し,動画の再生速度(fps)と解像度を設定します.最後の引数は isColor フラグを指定します.このフラグが True であればカラーの動画,そうでなければグレースケールの動画として保存されます.
FourCC は動画のコーデックを指定するための4バイトのコードです.
MJPG用のFourCC コードは cv2.VideoWriter_fourcc('M','J','P','G') もしくは cv2.VideoWriter_fourcc(*'MJPG) です.#Define the codec and create VideoWriter object
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output.avi',fourcc, 20.0, (640,480))
filename:
Name of the output video file.
fourcc:
4-character code of codec used to compress the frames. For example, VideoWriter::fourcc('P','I','M','1') is a MPEG-1 codec, VideoWriter::fourcc('M','J','P','G') is a motion-jpeg codec etc. List of codes can be obtained at Video Codecs by FOURCC page. FFMPEG backend with MP4 container natively uses other values as fourcc code: see ObjectType, so you may receive a warning message from OpenCV about fourcc code conversion.
fps:
Framerate of the created video stream.
frameSize:
Size of the video frames.
isColor:
If it is not zero, the encoder will expect and encode color frames, otherwise it will work with grayscale frames (the flag is currently supported on Windows only).
cv2.VideoWriter_fourcc() のパラメータとしてFourCCというのを指定しなければならないらしい。じゃあFourCCって何よ?というので調べてみたら、Microsoftのサイトに解説がありました。
FOURCC コードは、4 つの ASCII 文字を連結して作成される 32 ビット符号なし整数です。 たとえば、YUY2 ビデオの FOURCC コードは 'YUY2' です。
これらの記事を全部噛み砕いて理解すると、私が使おうとしているmp4ファイルの場合は「MP4V」になるらしい。そしてcv2.VideoWriter_fourcc()のパラメータとして使うには↓のどちらかになるらしい。
cv2.VideoWriter_fourcc('M','P','4','V')
cv2.VideoWriter_fourcc(*'MP4V)
・・・という経緯で現在のコード↓
cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
に至ったという次第です。
fps
fpsは"frames per second"の略で、1秒間の動画が何枚の画像で構成されているかを示すの単位のこと。このサイトに教えてもらいました。我々が一般的に使用する動画では24fpsでよいのではないかと思います。
size
sizeは解像度ということになると思うのですが、これも調べ始めると奥が深くてキリがないので、とりあえずPC画面上で扱いやすい(640, 360)にしておきます。
cv2.VideoWriter_fourcc()にパラメータを与える
それぞれの値を代入した変数をcv2.VideoWriter_fourcc()の変数として与えたのが
writer = cv2.VideoWriter('./test2.mp4', fmt, fps, size)
・・・ということになります。
実際に読み込んだ動画のフレームサイズと保存する動画のサイズを合わせる
frame = cv2.resize(frame, size)
この1行で、実際に読み込んだ動画のフレームサイズと保存する動画のサイズを同じにしています。ここで指定しないと、mp4のファイルは作成されますが開くことができません。
1フレームずつ書き込む
writer.write(frame)
whileループの中で1フレームずつ書き込んでいってます。これも1フレームずつ書き込まれていってるので、最終的にパラパラ漫画の要領で動画に見える。
リソースを解放
writer.release()
この1行でリソースを解放します。
・・・という感じで、ちょっと理解するまでに時間がかかりましたが、加工した画像を出力して保存することができました。