electronicsdiy / electronics_diy721

electronics_diy721's repository
0 stars 0 forks source link

Modification done to Mr.@hitomatagi's optical flow Python code (changed from line to arrow) #4

Open electronicsdiy opened 3 years ago

electronicsdiy commented 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.

Output from my modified code which was given a MP4 movie file.

optical_flow_mod_1 optical_flow_mod2 opitical_flow_mod3 optical_flow_mod4 optical_flow_mod5

black and white frames output version

スクリーンショット 2021-08-11 13 19 59 スクリーンショット 2021-08-11 13 20 06 スクリーンショット 2021-08-11 13 20 18

Modified scirpt file

motion_arrow_color_image.py

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()

black and white frame version

motion_arrow.py

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()