OpenCV + Matplotlib (Seaborn) で 2 画面の動画 + グラフ表示
前に「OpenCV映像とMatplotlibグラフを、Matplotlibの1つの画面で表示させる」というのをやりました。
OpenCV画像 + matplotlibグラフを合わせて表示する - 雑食性雑感雑記
今回は、綺麗に表示ができる「Seaborn」を使ってみたくなったのに合わせて、
OpenCV 側で2画面を出す方法が分かったので、それをやってみます。
完成形としては、こんな感じで動画のウィンドウとグラフのウィンドウが出る形です。
※ 動画はフリーサイト Pexels より。https://www.pexels.com/video/river-with-strong-current-2019791/
コード
そこまで長くないので、全体のコードを貼っておきます。
グレースケール化した色値のヒストグラムを取ってます。
要 cv2, matplotlib, numpy, seaborn。
# for CLI import matplotlib matplotlib.use('TkAgg') import cv2 import matplotlib.pyplot as plt import numpy as np import seaborn as sns INPUT_MOVIE_PATH = "sample.mp4" def hist(frame, fig) : """ Seaborn histgram """ # Grayscale frame_g = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # Graph plt.clf() sns.distplot(frame_g.flatten()) fig.canvas.draw() # Graph to np img frame_graph = np.fromstring(fig.canvas.tostring_rgb(), dtype = np.uint8, sep = '') frame_graph = frame_graph.reshape(fig.canvas.get_width_height()[::-1] + (3,)) frame_graph = cv2.cvtColor(frame_graph, cv2.COLOR_RGB2BGR) return frame_graph if __name__ == '__main__' : # Seaborn initialize sns.set() fig = plt.figure() # Start video capture cap = cv2.VideoCapture(INPUT_MOVIE_PATH) while True : ret, frame = cap.read() if not ret : break # Resize ... 大きすぎる場合に # size = (int(frame.shape[1] / 2), int(frame.shape[0] / 2)) # frame = cv2.resize(frame, size) # Histogram frame_graph = hist(frame, fig) cv2.imshow("Window", frame) cv2.imshow("Graph", frame_graph) key = cv2.waitKey(30) & 0xFF if key == ord('q') : break
説明
OpenCV で動画表示
―― はどこでも書かれていると思うので略。
グラフの numpy 画像化
この↓
# Graph plt.clf() sns.distplot(frame_g.flatten()) fig.canvas.draw() # Graph to np img frame_graph = np.fromstring(fig.canvas.tostring_rgb(), dtype = np.uint8, sep = '') frame_graph = frame_graph.reshape(fig.canvas.get_width_height()[::-1] + (3,)) frame_graph = cv2.cvtColor(frame_graph, cv2.COLOR_RGB2BGR) return frame_graph
あたり。
plt.clf() で canvas に書かれたグラフのクリア。
ここ入れないと同じ figure 上に描かれ続けてカラフルなグラフになる。(特定の場合には不要かもだけど…?)
sns.distplot() で描画し、それを fig.canvas.draw() で Matplotlib 内描画エリアに描画しているらしい。
その canvas エリアを numpy array として持ってきて reshape して cv2 用に色空間変換しているのが後半部分。
その他
今回、WSL 上の Python3 から MobaXTerm を使って実行しました。
CLI 実行の場合は python-tk が入ってないとダメとか、
import matplotlib matplotlib.use('TkAgg')
の設定が無いとダメとかあるとのこと。
メモ
- とりあえずやりたいことはできました。
- ただし、遅い。。描画方法等要検討。
- グラフ描画のための計算 (ヒストグラム) 自体は遅くないハズ。
- fig.canvas.draw() 自体が遅いという話もある。特定の部分だけ描画更新すると速くなるらしい?
- グラフも nparray の画像化できてるので、元画像上に重ねて貼るような表現にしてもよいかも。
参照
- Matplotlib のグラフを numpy 画像化する方法は以下 Stackoverflow より。
- CLI 実行の場合は python-tk 等必要とのこと。