手ブレを検出する
環境
- Python 3.9.13
- opencv-python == 4.7.0.68
サンプルコード
import cv2
THRESHOLD = 150
img = cv2.imread('./test.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 一定サイズにしないと、画像ごとに判定結果が変わってしまう
resized = cv2.resize(img, dsize=(1920, 1080))
# 輪郭検出
laplacian = cv2.Laplacian(resized, cv2.CV_64F)
# どれくらい輪郭を検出できたか?
print(laplacian.var())
if laplacian.var() > THRESHOLD:
# 書き込むのは元ファイル
cv2.imwrite("res.jpg", img)
print("write res.jpg")
手順
- 1.輪郭抽出する(ラプラシアンフィルタ)。
- 2.どの程度輪郭を検出できたか判定する。
- 3.しきい値以下ならNG。
- 4.OKな画像だけを別フォルダに保存する。
(サンプルA) 正確に輪郭検出できている
(サンプルB) 手ブレしている写真
解説
THRESHOLD = 150
しきい値です。今回の手法ではある程度様子を見ながら、手動で調節します。
今回はTHRESHOLD = 150位が良さそうです。(どれくらいまで許容するかによります)
# 一定サイズにしないと、画像ごとに判定結果が変わってしまう
resized = cv2.resize(img, dsize=(1920, 1080))
輪郭検出できた面積で判定しているため、画像サイズが大きくなると面積も増えてしまいます。
そのため画像サイズをリサイズしています。
今回は16:9の比率である(1920 * 1080)に設定しましたが、他の比率の場合は変更してください。
フルHD程度の画質があれば、十分に手ブレ判定出来るかと思います。
# 輪郭検出
laplacian = cv2.Laplacian(resized, cv2.CV_64F)
# どれくらい輪郭を検出できたか?
print(laplacian.var())
laplacian.var()に結果の数値が入ります。
あとはif文でしきい値と比較し、判定します。
しきい値を下回ったとは、手ブレしているということになります。
輪郭検出とは
cv2.Laplacian()
関数は、
画像の中の物体の、エッジ(輪郭)を擬似的に検出するものです。
プラシアンエッジフィルタと呼ばれる技術を用いて、画像内のエッジを強調します。
ラプラシアンフィルタは画像の二次微分を取ることでエッジを検出します。
簡単に言うと、画像の明るさが急激に変化したところを検出します。
“ボケている”画像とは、画像の中の物体の、エッジ(輪郭)がハッキリしていない
とも捉えられますよね。
この事を利用して今回のプログラムで、”ボケている”画像を選別することができます。
動機
フォトグラメトリという技術があります。
ぐるっと360度写真を撮って、ソフトに写真を読み込むと、自動で3D化してくれます。
ですが、
何枚も写真を撮る以上、やはり手ブレする事があります。
もちろんカメラ側でシャッタースピードを早くして、手ブレを抑える事はできますが、
光量が少ない環境だと、どうしてもシャッタースピードを早くできないこともあるかと思います。
もし手ブレ写真をそのままフォトグラメトリソフトに入力すると、特徴点のマッチがうまくいかないことが多いです。
ソフト側が自動でフォトグラメトリに向かない写真を除外してくれることもありますが、
私の体感、まぁまぁに手ブレしている位だと、そのまま処理が進んでしまい、結果メッシュが破綻していたり、テクスチャのクオリティに問題が起きる可能性があります。
数百枚ある写真を手動で分類するのは骨が折れます。
今回はかなりシンプルな手法で手ブレを検出できる方法がありますのでPythonで実装してみました。
フォトグラメトリとPython
フォトグラメトリにおいてPythonの活用シーンはかなり大きいと思います、
例えば、
- 大量の画像のリサイズ
- 自動でフォトグラメトリを実行し、夜間等にまとめて処理しておく
- フォトグラメトリをするソフト自体を改造する
などです。
ロケ等で、素材の写真を撮影したなら
画像が数千枚以上に達することもあると思います。
手作業で作業するのは大変です。
また寝ている間に自動でフォトグラメトリ処理をしておくのは、
かなり有用な活用方法だと思います。
応用アイディア
- 複数枚対応
フォルダ内の画像を全取得して、ループで回し、自動化しましょう。
→glob.glob(“*.jpg”) 的なコードでできます。 - しきい値を上回った場合、そのファイルを移動する。
大量 && 大きいファイルサイズの場合この実装のほうが早いです。
コメント