元ネタ
手振れ動画を定点カメラっぽく補正した話 OpenCV | デジタルクリエイティブのスーパーソフトウエア東京
風でぶれた定点カメラを補正したかったので検索した所、上記サイトを見つけました。
しかしサンプルコードが見つからなかったのでPythonで書いてみました。
使ったもの
- Python 2.7
- numpy
- OpenCV 3.0.0 beta
コード
エラー処理とかは一切やってません。
あと、動いたから良いだろ、と思っているので、肝心の関数の使い方が間違ってるかもしれないです。
# -*- coding: utf-8 -*-
import cv2
import numpy as np
# FourCC指定
#fourcc = "DIB "
fourcc = "XVID"
# 変換メソッド指定(?)
#warp_type = cv2.MOTION_AFFINE
#warp_type = cv2.MOTION_EUCLIDEAN
#warp_type = cv2.MOTION_TRANSLATION
warp_type = cv2.MOTION_HOMOGRAPHY
# 出力画像を縦横1/2サイズ(面積1/4)に設定
scale = 2
# WarpMatrixと変換関数を設定
if warp_type == cv2.MOTION_HOMOGRAPHY:
warp = np.eye(3,3,dtype=np.float32)
warpTransform = cv2.warpPerspective
else:
warp = np.eye(2,3,dtype=np.float32)
warpTransform = cv2.warpAffine
# 入力動画
cap = cv2.VideoCapture(r'./test.mp4')
# キャプチャの情報取得し出力情報を作成
fps = cap.get(cv2.CAP_PROP_FPS)
size = cap.get(cv2.CAP_PROP_FRAME_WIDTH), cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
size = tuple(map(lambda x: int(x / scale), size))
# 基準となる画像を取得
ret, base = cap.read()
base = cv2.resize(base, size)
base = cv2.cvtColor(base, cv2.COLOR_BGR2GRAY)
# 出力先
video1 = cv2.VideoWriter("./out1.avi", cv2.VideoWriter_fourcc(*fourcc), fps, size)
while True:
# フレーム取得
ret, frame = cap.read()
if not ret:
break
frame = cv2.resize(frame, size)
tmp = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.findTransformECC(tmp, base, warp, warp_type)
# 出力は元画像(カラー画像)
out = warpTransform(frame, warp, size)
# 表示かつAVIに出力
cv2.imshow("", out)
video1.write(out)
k = cv2.waitKey(1)
if k in [27, ord('q')]:
# ESCかQキー押下で終了
break
# 解放
cap.release()
video1.release()
cv2.destroyAllWindows()
追記
wrap_type
をwarp_type
に直して、findTransfomrECC
の引数に入れました。
追記ここまで
嵌まったところ
import cv2.cv
が使えない(?)のでwarpMatrixのためのcv2.cv.CreateMat
のような、cv
パッケージを使ったネットのサンプルソースの部分を置換するときに嵌まりました。
warpMatrix
エラーコード
warpMatrix must be single-channel floating-point matrix in function cv::findTransformECC
みてもサッパリ(フロート指定してんだろ!とずっと思ってた)だったのですが、dtype
指定であっさり。
FourCC
これもネットだとcv2.cv.CV_FOURCC
で指定する方法ばかりで使えませんでした。
Vimの補完プラグインNeoCompleteがVideoWriter_fourcc
を教えてくれなかったらどうなっていたか…
ここは後になって見つけました。
まだ気になっていること
-
findTransformECC
の第1、第2引数の関係が逆の様な気がする- でも
warpAffine
の引数を考えるとあっている?
- でも
追加のひと工夫
上記のソースはリサイズこそしてるものの、画像全体を対象にしているので、動く物体が動画に大きく写っていると上手く補正してくれないことがありました。
なので、findTransformECC
の第1・第2引数に渡す画像を背景の殆ど動かない箇所を選んでbase[:300, :]
, tmp[:300,:]
な感じでクロップ(トリミング?)してやって、warpAffine
, warpPerspective
で変換する時だけもとの画像を使ってやれば劇的に良くなりました。
出力動画もクロップしたものを使えば外側の欠落部分も見えなくなって良い感じになります。
あとは、1日ほっとく場合、最初のフレームじゃなく、直前のフレームを変換したものをベースとしてやれば時間変化に多少強くなるかもしれません。