質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Matplotlib

MatplotlibはPythonのおよび、NumPy用のグラフ描画ライブラリです。多くの場合、IPythonと連携して使われます。

NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Python 2.7

Python 2.7は2.xシリーズでは最後のメジャーバージョンです。Python3.1にある機能の多くが含まれています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

3回答

4015閲覧

計算時間を圧倒的に短くする方法が知りたいです。

退会済みユーザー

退会済みユーザー

総合スコア0

Matplotlib

MatplotlibはPythonのおよび、NumPy用のグラフ描画ライブラリです。多くの場合、IPythonと連携して使われます。

NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Python 2.7

Python 2.7は2.xシリーズでは最後のメジャーバージョンです。Python3.1にある機能の多くが含まれています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

2クリップ

投稿2020/01/16 08:52

30秒の動画において、、ある1ピクセルを指定し、他の全てのピクセルの強度の相関係数を算出し、ヒートマップで可視化する作業を行ってます。ピクセルの個数が300×300=90000なので計算時間がものすごいかかってしまいます。

python

1# -*- coding: utf-8 -*- 2import cv2 3import matplotlib.pyplot as plt 4import numpy as np 5 6cap = cv2.VideoCapture("test.mkv") 7count = cap.get(cv2.CAP_PROP_FRAME_COUNT) 8fps = cap.get(cv2.CAP_PROP_FPS) 9 10# 先に全フレームを読み込む。 11frames = [] 12while True: 13 ret, frame = cap.read() 14 if not ret: 15 break 16 17 18 frames.append(frame) 19 20frames = np.array(frames) 21numE=count 22print(numE) 23######################################################################## 24# 場所を決める 25#点滅の場所 26xpx=int(raw_input("点滅1 X:")) 27ypx=int(raw_input("点滅1 Y:")) 28######################################################################## 29# フレーム [numS,numE] の範囲で各フレームの [ymin, ymax]x[xmin, xmax] の画素の平均を計算する。 30frame_no = np.arange(count) 31print("frame number",count) 32########## read the target pixel intensity 33intGt=np.zeros((0)) # the target intensity 34print("read target position x=%d y=%d intensity"%(xpx,ypx)) 35for i in frame_no: 36 # フレーム frame_no を取得する。 37 cap.set(cv2.CAP_PROP_POS_FRAMES, i) 38 ret, frame = cap.read() 39 if not ret: 40 print('Failed to grab frame.') 41 break 42 # 平均画素値を計算する。 43 G=np.average(frame[xpx,ypx]) 44 intGt=np.append(intGt,G) 45 46print("read the target intensity!") 47 48# read first frame (frame zero) 49# フレーム frame_no を取得する。 50cap.set(cv2.CAP_PROP_POS_FRAMES, 0) 51ret, frame = cap.read() 52XS=np.shape(frame)[0] 53YS=np.shape(frame)[1] 54# show the target pixel intesity 55fig, ax = plt.subplots() 56ax.invert_yaxis() 57#im=ax.imshow(std,cmap="gray") 58ax.set_title("(water)") 59plt.plot(intGt,".") 60plt.ylim([0,255]) 61#plt.imshow(std, cmap="jet",vmin=0,vmax=32) 62#plt.imshow(std, cmap="jet") 63#plt.colorbar() 64plt.show() 65 66 67 68raw_input("stop") 69import time as t 70corrMap=np.zeros((0)) 71 72x=y=0 73for y in range(YS): 74 for x in range(XS): 75 t0=t.time() # start clock 76 intG = np.zeros((0)) # reset intensity 77 for i in frame_no: 78 # フレーム frame_no を取得する。 79 cap.set(cv2.CAP_PROP_POS_FRAMES, i) 80 ret, frame = cap.read() 81 if not ret: 82 print('Failed to grab frame.') 83 break 84 # 平均画素値を計算する。 85 G=np.average(frame[x,y]) 86 intG=np.append(intG,G) 87 # correlation between target and current pixel 88 CC=np.corrcoef(intGt,intG) # correlation coefficient 89 print(np.shape(CC),CC[0,1]) 90 print("now at position x=%d y=%d, cc=%4.2g"%(x,y,CC[0,1])) 91 corrMap=np.append(corrMap,CC[0,1]) 92 print("it took %4.2g secs"%(t.time()-t0)) 93 print("you need %4.2g mins"%((t.time()-t0)*XS*YS/60)) 94 95########### 96corrMap=corrMap.reshape(shape(frame)) 97raw_input("stop") 98# ヒートマップで描画する。 99fig, ax = plt.subplots() 100ax.invert_yaxis() 101im=ax.imshow(corrMap,cmap="gray") 102ax.set_title("(water)") 103#plt.plot(intG,".") 104#plt.imshow(std, cmap="jet",vmin=0,vmax=32) 105#plt.imshow(std, cmap="jet") 106#plt.colorbar() 107plt.show()

上記のようなプログラムを書いて、シミュレーション時間を計算してみましたが多分20日くらい?かかる感じです。強度はRGBの平均値としています。

何か解決策はあるのでしょうか?

ちなみに求めたいヒートマップのイメージは添付の通りです。よろしくお願いします

イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

WathMorks

2020/01/16 11:15

「ピクセルの強度の相関係数」の定義は何ですか?
退会済みユーザー

退会済みユーザー

2020/01/17 05:25

強度は1フレームおきにplotされます。1フレームは30fpsなので0.03秒おきにplotされます。私はこの0.03秒での相関関係を見ています
Q71

2020/01/23 09:52

メモリには、フレーム,高さ,幅,色 の順で並びます。xを全部走査してからy、yの走査が済んでからフレームを変更するようにすると、メモリへのアクセスが良くなります。 ピクセル間の強度とは、1フレームの中で計算するのでしょうか。それともフレーム間で計算するのでしょうか。
guest

回答3

0

何やってるんだろうと思ったら画素単位のループの内側で毎フレームRGB平均とってフレーム間データ取り出して正規化相互相関とってるんですね。そら重いはずですわ。

pythonはあまり詳しくないので画像処理のやり方としての回答になりますが
frame間走査を最も内側にするとメモリアクセスが滅茶苦茶とびとびになるので、各画素でframe間走査するのはやめて画像単位で処理しましょう。
まずframe間操作は一番外側にして各フレームで画像単位でRGB平均を計算し、フレーム間の平均・分散・共分散を計算して正規化相互相関計算を行います。
フレーム間の和・二乗和・相関計算対象との積の和を用意してやれば1回のframe間走査で処理できると思います。

何か勘違いがあったらすいませんが、こんな感じでどうでしょうか。

投稿2020/01/16 14:02

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/01/23 07:58

ご返信ありがとうございます。 画像単位で処理するとはどういうことなのでしょうか?よろしくお願いします
退会済みユーザー

退会済みユーザー

2020/01/23 08:30

画像単位で処理するというのは、ある画像に対して行うべき処理を全て行ってから次の画像の処理に移ることを指しているつもりです 質問者さんのソースでは画像座標を決めてから全フレームを処理しているので同じ画像を少なくとも画像の画素数回数分参照することになってしまいます。 この手の画像処理の速度が遅くなる原因は大抵の場合メモリアクセス効率であり、画像座標固定してフレーム間走査をし、次の座標に移るという方式は非常にアクセス効率が悪いため低速になります。 それよりは、フレーム1の全画素に対して処理を行った後次のフレームに移る方が断然高速になります。 RGB平均は単一画像で完結する処理なので画像単位で処理できますし、相関係数も工夫をすれば計算途中のデータを保持することで画像単位で処理することが可能です。 (分散、共分散を計算するために和、二乗和、積和を計算するというのがそれに該当します)
退会済みユーザー

退会済みユーザー

2020/01/23 08:50

すなわち、画像座標の指定(写真の指定した1ピクセル)は一番最後の段階でやった方がベターってことで大丈夫ですか?
退会済みユーザー

退会済みユーザー

2020/01/23 09:18

最後=内側のループ という認識でしたら大丈夫です。 厳密ではありませんが簡単にその方が良い理由を説明します。 前述の通り物凄く計算が複雑でない限りは大抵は計算時間よりもメモリアクセス時間が大きくなります。 メモリアクセスの回数が減らせば良い訳ですが、CPUはメモリからデータを引っ張ってくる際に、対象データのあるメモリ位置に連続するデータを一緒にまとめて引っ張って来ます。 画像の場合連続するデータは画像X方向に隣接する画素になります(画像右端の場合は次の行に連続) つまり画像X方向のループを一番内側にすれば一緒に引っ張って来たデータが使えるのでメモリアクセスの回数が減るということになります。 同じ理屈でXのループの外側はYのループ、その外側はフレームのループにするのがループの順序としては最適です。
退会済みユーザー

退会済みユーザー

2020/01/23 09:37 編集

多分ですけど他の方の回答に示されてるような画像同士での演算処理をうまく組み合わせればX,Yのループはわざわざ書かなくてもいけるかも知れませんね(もちろん内部的には行われてる) tiitoiさんが別の切り口の速度問題に言及されてるのでので用が足りたらそれだけで十分かもです(pythonの細かい仕様まで把握してなくてすいません)
guest

0

300x300 の30秒ぐらいの動画であれば、多分、全部メモリに乗り切るので、まず全フレームを読み込んで、(フレーム数, 画像の高さ, 画像の幅, 3) の numpy 配列を作りましょう。
numpy 配列を作ったら、mean_imgs = np.mean(imgs, axis=-1) でチャンネルごとの平均をとります。
相関係数計算のところは、numpy.corrcoef() の引数に1次元配列を渡さないといけないので、仕方なく for 文で各画素ごとに計算しています。
下記の環境、コードでだいたい1分ぐらいで計算できました。

質問のコードが遅い原因

  • np.append() は、追加するたびに配列を作り直すので、リストと同じように使うと、とても遅くなります。基本的にリストに append() して、最後に numpy.array() で numpy 配列に変換したほうが早いです。

  • cap.set(cv2.CAP_PROP_POS_FRAMES, i) は遅いので、各フレーム順番にとってく場合は、cap.set(cv2.CAP_PROP_POS_FRAMES, 0) で1回だけ位置を最初のフレームに戻せばよいでしょう。

試した環境

  • 576x768 の動画 795 フレーム分
  • Ubuntu 16.04
  • Intel(R) Core(TM) i7-6800K CPU @ 3.40GHz
  • 66.96 秒

コード

python

1import time 2from urllib.request import urlretrieve 3 4import cv2 5import numpy as np 6 7start = time.time() 8 9# サンプル用動画をダウンロードする。 10video_path = "sample.avi" 11video_url = "https://github.com/opencv/opencv/raw/master/samples/data/vtest.avi" 12urlretrieve(video_url, video_path) 13 14# 動画の全フレームを読み込む。 15cap = cv2.VideoCapture(filepath) 16# 1フレームずつ取得する。 17imgs = [] 18while True: 19 ret, frame = cap.read() 20 if not ret: 21 break 22 imgs.append(frame) 23 24# チャンネルごとに平均をとる。 25mean_imgs = np.mean(imgs, axis=-1) 26print(mean_imgs.shape) # (795, 576, 768) 27 28# ピクセルごとに相関係数を計算する。 29x0, y0 = 10, 10 # 対称とする点 30 31h, w = mean_imgs.shape[1:3] # 画像の幅、高さ 32vec1 = mean_imgs[:, y0, x0] 33 34corr_img = np.empty((h, w)) 35for y in range(h): 36 for x in range(w): 37 vec2 = mean_imgs[:, y, x] 38 # 相関行列を計算する。 39 C = np.corrcoef(vec1, vec2) 40 corr_img[y, x] = C[0, 1] # x, y の相関係数 41 42print(f"Done! {time.time() - start}s") # 66.96258449554443s 43 44fig, ax = plt.subplots() 45ax.imshow(corr_img) 46 47plt.show()

イメージ説明

追記

1フレームの強度の振れ幅がピクセルごとに違うので、正規化を行いたいと思います。

チャンネルごとに平均をとって、(フレーム数, 高さ, 幅) の配列を作ったあと、axis=0 方向、つまり、画素ごとに [-1, 1] の範囲に正規化を行えばよいです。

python

1import time 2from urllib.request import urlretrieve 3 4import cv2 5import matplotlib.pyplot as plt 6import numpy as np 7 8start = time.time() 9 10# サンプル用動画をダウンロードする。 11video_path = "sample.avi" 12video_url = "https://github.com/opencv/opencv/raw/master/samples/data/vtest.avi" 13urlretrieve(video_url, video_path) 14 15# 動画の全フレームを読み込む。 16cap = cv2.VideoCapture(video_path) 17# 1フレームずつ取得する。 18imgs = [] 19while True: 20 ret, frame = cap.read() 21 if not ret: 22 break 23 imgs.append(frame) 24 25# チャンネルごとに平均をとる。 26mean = np.mean(imgs, axis=-1) 27del imgs # メモリ節約のため、不要になったので、削除する。 28print(mean.shape) # (795, 576, 768) 29 30# 範囲を [-1, 1] にする。 31mins, maxs = mean.min(axis=0), mean.max(axis=0) 32 33# メモリ節約のため、演算はインラインで行う。 34mean -= mins 35mean *= 2 / (maxs - mins) 36mean -= 1 37 38# ピクセルごとに相関係数を計算する。 39x0, y0 = 10, 10 # 対称とする点 40 41h, w = mean.shape[1:3] # 画像の幅、高さ 42vec1 = mean[:, y0, x0] 43 44corr_img = np.empty((h, w)) 45for y in range(h): 46 for x in range(w): 47 vec2 = mean[:, y, x] 48 # 相関行列を計算する。 49 C = np.corrcoef(vec1, vec2) 50 corr_img[y, x] = C[0, 1] # x, y の相関係数 51 52print(f"Done! {time.time() - start}s") # 66.96258449554443s 53 54fig, ax = plt.subplots() 55ax.imshow(corr_img) 56 57plt.show()

イメージ説明

投稿2020/01/23 09:08

編集2020/02/03 15:24
tiitoi

総合スコア21956

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/02/03 02:58

1フレームの強度の振れ幅がピクセルごとに違うので、正規化を行いたいと思います。 全ピクセルに2×((y(t)-ymin/ymax-ymin))-1を行って、この計算によって得られた時系列強度を使って、同様corrlation mapに表示させるにはどうしたら良いでしょうか?
tiitoi

2020/02/03 15:24

追記しました。
guest

0

画像で試してみました。(全て各ピクセルのRGBの平均としています)

Python

1import cv2 2import matplotlib.pyplot as plt 3 4img = cv2.imread("Flower1.jpg") 5 6shape = img.shape 7 8data = (img[:,:,0] + img[:,:,1] + img[:,:,2]) / 3 9 10fig, ax = plt.subplots() 11plt.imshow(data, cmap=plt.cm.Blues) 12plt.colorbar() 13 14plt.show()

イメージ説明
イメージ説明

投稿2020/01/16 11:35

meg_

総合スコア10579

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

meg_

2020/01/16 11:39

上記画像は求めているものとは違うかもしれませんが、numpyの計算はforループ使わない方法でやった方が良いです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問