メモリに収まりきらない複数枚の画像の中央値を計算するのに困っています。
同じサイズの画像2~100枚を重ね合わせて、画素ごとの中央値を算出することでコンポジット処理のような画像処理をしたいです。
例えば、風景に通行人が写ってしまった画像を複数枚用意し、通行人は移動しているため画素ごとに中央値などの処理をすることで風景のみを取り出す、というようなことをしたいです。
中央値ではなく、平均値ならば随時処理が可能であり、メモリに画像全てを載せる必要がないのですが、平均値ですと外れ値に強く影響されてしまうので(上の例だと、派手な色の服をきた人が通った部分は、その色に強く影響される)、外れ値にあまり影響されずなおかつメモリに全ての画像を保持しなくてもいいような合成方法を探しています。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答4件
0
各画像を左上、中央上、右上、左中段、中央、…のように分割して一度ファイルに出力して、左上だけで中央値計算をする、のようにしてはいかがでしょうか? (9分割の必然性はないです。)
投稿2020/01/27 07:05
総合スコア1720
0
真の中央値をどうにかして求める方法に関する回答にはなりませんが…
話の用途的には,
画像群{1枚目,2枚目,3枚目 ..., 99枚目, 100枚目}のすべてを使わずとも,適当に間引いてもよいのではないかと思います.
例えば,{20,40,60,80,100}の5枚だけでやるとか.
もちろん,どの程度間引くかはデータ次第(例えばこの例だと「20枚な時間周期で人が往復している」シーンを相手にすれば,人の画素値を算出してしまう箇所が生じる)ですが,
単純に間引くのではなくて20枚ごとの平均画像を用いるだとか,工夫の余地はあるかと思います.
[追記]
背景の画素値が撮影期間内において概ね一様であると仮定できるならば,
100枚分の画素値を正直に100データとして保持せずともランレングス的なデータで保持することで略一様な期間のデータを小さく持つことができるのではないかな…とか.
このとき,「レングス」が短すぎるデータは保持せずに捨ててよいハズ.
すると,100枚程度の期間なら,最終的には画素あたり多くても数個程度のデータになるのではないかな,とか思う.
結果としてデータが1個ならばそのデータの画素値を背景とできるし,複数個なら(その中で画素値が近いやつは1つにまとめて)一番時間が長いデータをやはり背景とできる…と思う.
計算量を気にしなくてよいなら,前記のランレングス的データは,
「時間方向の輝度値の変動をある誤差内で直線で近似可能な期間」のデータみたいな?
(直線の「傾き」がでかいデータは捨ててよい,とか)
投稿2020/01/27 07:27
編集2020/01/27 09:16総合スコア11658
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
ベストアンサー
画素ごとに 0 ~ 255 の256個のビンによるヒストグラムを作成し、各フレームごとに逐次カウントしていき、最後にヒストグラムから中央値を求めればよいのではないでしょうか。
この場合、画素ごとにヒストグラムを持っておけばいいので、計算に必要な配列の大きさは (画像の高さ, 幅, 256) で済みます。
ヒストグラムが与えられたデータから,中央値を求める方法|数学|苦手解決Q&A|進研ゼミ高校講座|ベネッセコーポレーション
追記
最初に書いたヒストグラムの案もそのサイズだと厳しいので、「ブロックごとに中央値を直接計算する」または「ブロックごとにヒストグラムを計算して、中央値をあとから計算する (枚数が256枚以上ならこちらのが省メモリ)」になるかと思います。
コード
OpenCV のサンプル動画 vtest.avi をフレームごとに切り出して保存した画像で試しました。
python
1from pathlib import Path 2 3import cv2 4import matplotlib.pyplot as plt 5import numpy as np 6 7# 画像のパス一覧を取得する。 8input_dir = Path("input") # 画像があるディレクトリ 9img_paths = list(input_dir.glob("*.png")) 10 11# 1枚だけ読み込み画像の幅、高さを取得する。 12img = cv2.imread(str(img_paths[0])) 13h, w = img.shape[:2] 14 15 16median_img = np.empty_like(img) 17 18stride_y, stride_x = 300, 300 # ブロックの大きさ 19for y in range(0, h, stride_y): 20 for x in range(0, w, stride_x): 21 # ブロックごとに中央値を計算する。 22 values = [] 23 for path in img_paths: 24 img = cv2.imread(str(path)) 25 values.append(img[y : y + stride_y, x : x + stride_x]) 26 median = np.median(values, axis=0).astype(np.uint8) 27 median_img[y : y + stride_y, x : x + stride_x] = median 28 29# 結果を可視化する。 30fig, ax = plt.subplots() 31ax.imshow(median_img[..., ::-1]) 32 33plt.show()
投稿2020/01/27 07:05
編集2020/01/27 08:37総合スコア21956
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/01/27 07:11
2020/01/27 07:41 編集
2020/01/27 07:31
2020/01/27 07:36
2020/01/27 08:10
2020/01/27 08:17
2020/01/27 08:24
2020/01/27 08:42
2020/01/27 08:46
2020/01/27 09:03 編集
2020/01/27 09:06
2020/01/27 09:14
2020/01/27 09:19
2020/01/27 09:19
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/01/27 08:18
2020/01/28 02:56