Open electronicsdiy opened 3 years ago
I've changed Mr.@hitomatagi's optical flow Python code. The purpose is displaying motion direction vector as arrows (OpenCV2's prepared arrow-object) and not as lines.
import time import math import cv2 import numpy as np # ビデオデータ VIDEO_DATA = "trump.mp4" # Esc キー ESC_KEY = 0x1b # モーションの残存期間(sec) DURATION = 1.0 # 全体の方向を表示するラインの長さ LINE_LENGTH_ALL = 60 # 座標毎の方向を表示するラインの長さ LINE_LENGTH_GRID = 20 # 座標毎の方向を計算する間隔 GRID_WIDTH = 40 # 方向を表示するラインの丸の半径 CIRCLE_RADIUS = 2 # 表示ウィンドウの初期化 cv2.namedWindow("motion") # ビデオデータの読み込み video = cv2.VideoCapture(VIDEO_DATA) # 最初のフレームの読み込み end_flag, frame_next = video.read() height, width, channels = frame_next.shape motion_history = np.zeros((height, width), np.float32) frame_pre = frame_next.copy() while(end_flag): # フレーム間の差分計算 color_diff = cv2.absdiff(frame_next, frame_pre) # グレースケール変換 gray_diff = cv2.cvtColor(color_diff, cv2.COLOR_BGR2GRAY) # 2値化 retval, black_diff = cv2.threshold(gray_diff, 30, 1, cv2.THRESH_BINARY) # プロセッサ処理時間(sec)を取得 # Python 3.9系ではtime.clock()はない。 # proc_time = time.clock() # https://stackoverflow.com/questions/58569361/attributeerror-module-time-has-no-attribute-clock-in-python-3-8 proc_time = time.process_time() # モーション履歴画像の更新 #https://qiita.com/hitomatagi/items/d5d475a446ec9c73261e #https://qiita.com/fiftystorm36/items/1a285b5fbf99f8ac82eb #pip install opencv-contrib-python cv2.motempl.updateMotionHistory(black_diff, motion_history, proc_time, DURATION) # 古いモーションの表示を経過時間に応じて薄くする hist_color = np.array(np.clip((motion_history - (proc_time - DURATION)) / DURATION, 0, 1) * 255, np.uint8) # グレースケール変換 hist_gray = cv2.cvtColor(hist_color, cv2.COLOR_GRAY2BGR) # モーション履歴画像の変化方向の計算 # ※ orientationには各座標に対して変化方向の値(deg)が格納されます mask, orientation = cv2.motempl.calcMotionGradient(motion_history, 0.25, 0.05, apertureSize = 5) # 各座標の動きを緑色の線で描画 width_i = GRID_WIDTH while width_i < width: height_i = GRID_WIDTH while height_i < height: # 座標点の描画を削除 #cv2.circle(hist_gray, \ # (width_i, height_i), \ # CIRCLE_RADIUS, \ # (0, 255, 0), \ # 2, \ # 16, \ # 0) angle_deg = orientation[height_i - 1][width_i - 1] if angle_deg > 0: angle_rad = math.radians(angle_deg) #https://shikaku-mafia.com/opencv-arrowedline/ #tipLength は「全体の矢印線の長さに対する比率」で指定します。 #サンプルコードでは tipLength=0.1 としているので #「全体の長さ」の10%が「矢の先の長さ」になっています。 #cv2.line(hist_gray, \ # (width_i, height_i), \ # (int(width_i + math.cos(angle_rad) * LINE_LENGTH_GRID), int(height_i + math.sin(angle_rad) * LINE_LENGTH_GRID)), \ # (0, 255, 0), \ # 2, \ # 16, \ # 0) # frame_preに変更 cv2.arrowedLine(frame_pre, \ pt1=(width_i, height_i), \ pt2=(int(width_i + math.cos(angle_rad) * LINE_LENGTH_GRID), int(height_i + math.sin(angle_rad) * LINE_LENGTH_GRID)), \ color=(255, 0, 0), thickness=2, line_type=cv2.LINE_4, shift=0, tipLength=0.5) height_i += GRID_WIDTH width_i += GRID_WIDTH # 全体的なモーション方向を計算 angle_deg = cv2.motempl.calcGlobalOrientation(orientation, mask, motion_history, proc_time, DURATION) # 全体の動きを黄色い線で描画 # 座標点を削除する #cv2.circle(hist_gray, \ # (int(width / 2), int(height / 2)), \ # CIRCLE_RADIUS, \ # (0, 215, 255), \ # 2, \ # 16, \ # 0) angle_rad = math.radians(angle_deg) #https://shikaku-mafia.com/opencv-arrowedline/ #cv2.line(hist_gray, \ # (int(width / 2), int(height / 2)), \ # (int(width / 2 + math.cos(angle_rad) * LINE_LENGTH_ALL), int(height / 2 + math.sin(angle_rad) * LINE_LENGTH_ALL)), \ # (0, 215, 255), \ # 2, \ # 16, \ # 0) # frame_preに変更 cv2.arrowedLine(frame_pre, \ pt1=(int(width / 2), int(height / 2)), \ pt2=(int(width / 2 + math.cos(angle_rad) * LINE_LENGTH_ALL), int(height / 2 + math.sin(angle_rad) * LINE_LENGTH_ALL)), \ color=(0, 0, 255), thickness=3, line_type=cv2.LINE_4, shift=0, tipLength=0.5) # モーション画像を表示 # frame_preに変更 cv2.imshow("motion", frame_pre) # Escキー押下で終了 if cv2.waitKey(20) == ESC_KEY: break # 次のフレームの読み込み frame_pre = frame_next.copy() end_flag, frame_next = video.read() # 終了処理 cv2.destroyAllWindows() video.release()
import time import math import cv2 import numpy as np # ビデオデータ VIDEO_DATA = "trump.mp4" # Esc キー ESC_KEY = 0x1b # モーションの残存期間(sec) DURATION = 1.0 # 全体の方向を表示するラインの長さ LINE_LENGTH_ALL = 60 # 座標毎の方向を表示するラインの長さ LINE_LENGTH_GRID = 20 # 座標毎の方向を計算する間隔 GRID_WIDTH = 40 # 方向を表示するラインの丸の半径 CIRCLE_RADIUS = 2 # 表示ウィンドウの初期化 cv2.namedWindow("motion") # ビデオデータの読み込み video = cv2.VideoCapture(VIDEO_DATA) # 最初のフレームの読み込み end_flag, frame_next = video.read() height, width, channels = frame_next.shape motion_history = np.zeros((height, width), np.float32) frame_pre = frame_next.copy() while(end_flag): # フレーム間の差分計算 color_diff = cv2.absdiff(frame_next, frame_pre) # グレースケール変換 gray_diff = cv2.cvtColor(color_diff, cv2.COLOR_BGR2GRAY) # 2値化 retval, black_diff = cv2.threshold(gray_diff, 30, 1, cv2.THRESH_BINARY) # プロセッサ処理時間(sec)を取得 # Python 3.9系ではtime.clock()はない。 # proc_time = time.clock() # https://stackoverflow.com/questions/58569361/attributeerror-module-time-has-no-attribute-clock-in-python-3-8 proc_time = time.process_time() # モーション履歴画像の更新 #https://qiita.com/hitomatagi/items/d5d475a446ec9c73261e #https://qiita.com/fiftystorm36/items/1a285b5fbf99f8ac82eb #pip install opencv-contrib-python cv2.motempl.updateMotionHistory(black_diff, motion_history, proc_time, DURATION) # 古いモーションの表示を経過時間に応じて薄くする hist_color = np.array(np.clip((motion_history - (proc_time - DURATION)) / DURATION, 0, 1) * 255, np.uint8) # グレースケール変換 hist_gray = cv2.cvtColor(hist_color, cv2.COLOR_GRAY2BGR) # モーション履歴画像の変化方向の計算 # ※ orientationには各座標に対して変化方向の値(deg)が格納されます mask, orientation = cv2.motempl.calcMotionGradient(motion_history, 0.25, 0.05, apertureSize = 5) # 各座標の動きを緑色の線で描画 width_i = GRID_WIDTH while width_i < width: height_i = GRID_WIDTH while height_i < height: # 座標点の描画を削除 #cv2.circle(hist_gray, \ # (width_i, height_i), \ # CIRCLE_RADIUS, \ # (0, 255, 0), \ # 2, \ # 16, \ # 0) angle_deg = orientation[height_i - 1][width_i - 1] if angle_deg > 0: angle_rad = math.radians(angle_deg) #https://shikaku-mafia.com/opencv-arrowedline/ #tipLength は「全体の矢印線の長さに対する比率」で指定します。 #サンプルコードでは tipLength=0.1 としているので #「全体の長さ」の10%が「矢の先の長さ」になっています。 #cv2.line(hist_gray, \ # (width_i, height_i), \ # (int(width_i + math.cos(angle_rad) * LINE_LENGTH_GRID), int(height_i + math.sin(angle_rad) * LINE_LENGTH_GRID)), \ # (0, 255, 0), \ # 2, \ # 16, \ # 0) cv2.arrowedLine(hist_gray, \ pt1=(width_i, height_i), \ pt2=(int(width_i + math.cos(angle_rad) * LINE_LENGTH_GRID), int(height_i + math.sin(angle_rad) * LINE_LENGTH_GRID)), \ color=(255, 0, 0), thickness=2, line_type=cv2.LINE_4, shift=0, tipLength=0.5) height_i += GRID_WIDTH width_i += GRID_WIDTH # 全体的なモーション方向を計算 angle_deg = cv2.motempl.calcGlobalOrientation(orientation, mask, motion_history, proc_time, DURATION) # 全体の動きを黄色い線で描画 # 座標点を削除する #cv2.circle(hist_gray, \ # (int(width / 2), int(height / 2)), \ # CIRCLE_RADIUS, \ # (0, 215, 255), \ # 2, \ # 16, \ # 0) angle_rad = math.radians(angle_deg) #https://shikaku-mafia.com/opencv-arrowedline/ #cv2.line(hist_gray, \ # (int(width / 2), int(height / 2)), \ # (int(width / 2 + math.cos(angle_rad) * LINE_LENGTH_ALL), int(height / 2 + math.sin(angle_rad) * LINE_LENGTH_ALL)), \ # (0, 215, 255), \ # 2, \ # 16, \ # 0) cv2.arrowedLine(hist_gray, \ pt1=(int(width / 2), int(height / 2)), \ pt2=(int(width / 2 + math.cos(angle_rad) * LINE_LENGTH_ALL), int(height / 2 + math.sin(angle_rad) * LINE_LENGTH_ALL)), \ color=(0, 0, 255), thickness=3, line_type=cv2.LINE_4, shift=0, tipLength=0.5) # モーション画像を表示 cv2.imshow("motion", hist_gray) # Escキー押下で終了 if cv2.waitKey(20) == ESC_KEY: break # 次のフレームの読み込み frame_pre = frame_next.copy() end_flag, frame_next = video.read() # 終了処理 cv2.destroyAllWindows() video.release()
I've changed Mr.@hitomatagi's optical flow Python code. The purpose is displaying motion direction vector as arrows (OpenCV2's prepared arrow-object) and not as lines.
Output from my modified code which was given a MP4 movie file.
black and white frames output version
Modified scirpt file
motion_arrow_color_image.py
black and white frame version
motion_arrow.py