OpenCV2で物体追跡 〜 特徴点の移動軌跡をlineではなくarrowedLIneに変えてウインドウに動画出力


__[前の記事](https://qiita.com/electronics_diy721/items/ad9bb5a095ba90e96e37)__で取り組んだオプティカル・フローの物体移動軌跡（__*trajectory*__）を線（__*cv2.line*__）から矢印（__*cv2.arrwedLine*__）に変えてみた。

__（ 前の記事 ）__

- [OpenCV2で物体追跡 〜 特徴点の移動軌跡をフレーム画像から切離してウインドウに動画出力](https://qiita.com/electronics_diy721/items/ad9bb5a095ba90e96e37)

Have changed *cv2.line* tp *cv2.arrowedLine* in optical flow drawing script.

###（ 変更後の出力結果 ）

- 左下の画面を見ると、ところどころ小さな__矢印の先（「>」）__が見える

![](https://user-images.githubusercontent.com/87643752/129138904-7a64b40e-dfbe-424e-9e42-16ab2c0a06af.png)

####( 変更前 )

>```Python3:
> mask = cv2.line(mask, (int(next_x), int(next_y)), (int(prev_x), int(prev_y)), color[i].tolist(), 2)
>``` 

####( 変更後 )

>```Python3:
> mask = cv2.arrowedLine(mask, \
>     pt1=(int(next_x), int(next_y)), \
>     pt2=(int(prev_x), int(prev_y)), \
>            color=color[i].tolist(),
>            thickness=2,
>            line_type=cv2.LINE_4,
>            shift=0,
>            tipLength=0.5)
>``` 


### 変更後のスクリプト・ファイル

```Python3:
import numpy as np
import cv2
import argparse    

# 動画ファイル名をコマンドライン引数から受け取る
parser = argparse.ArgumentParser(description='')    #
parser.add_argument('file_name')  
args = parser.parse_args()  

movie_file = args.file_name
print('次の動画ファイルを解析します。 ：' + movie_file)
cap = cv2.VideoCapture(movie_file)

# Shi-Tomasiのコーナー検出パラメータ
feature_params = dict( maxCorners = 100,
                    qualityLevel = 0.3,
                    minDistance = 7,
                    blockSize = 7 )

# Lucas-Kanade法のパラメータ
lk_params = dict( winSize  = (15,15),
                  maxLevel = 2,
                  criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))

# ランダムに色を１００個生成（値0～255の範囲で100行3列のランダムなndarrayを生成）
color = np.random.randint(0, 255, (100, 3))

# 最初のフレームの処理
end_flag, frame = cap.read()
gray_prev = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
feature_prev = cv2.goodFeaturesToTrack(gray_prev, mask = None, **feature_params)
mask = np.zeros_like(frame)

while(end_flag):
    if feature_prev is None:
        print("フレームに追跡すべき特徴点が見つかりませんでした。")
        break

    # グレースケールに変換
    gray_next = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # オプティカルフロー検出
    feature_next, status, err = cv2.calcOpticalFlowPyrLK(gray_prev, gray_next, feature_prev, None, **lk_params)

    # フレーム前後でトラックが成功した特徴点のみを
    if feature_next is None:
        print("フレームに追跡すべき特徴点が見つからなくなりました。")
        break

    # オプティカルフローを検出した特徴点を選別（0：検出せず、1：検出した）
    good_prev = feature_prev[status == 1]
    good_next = feature_next[status == 1]

    # オプティカルフローを描画
    for i, (next_point, prev_point) in enumerate(zip(good_next, good_prev)):
        prev_x, prev_y = prev_point.ravel()
        next_x, next_y = next_point.ravel()
        # cv2.lineをcv2.arrowedLineに変更
        #mask = cv2.line(mask, (int(next_x), int(next_y)), (int(prev_x), int(prev_y)), color[i].tolist(), 2)
        frame = cv2.circle(frame, (int(next_x), int(next_y)), 5, color[i].tolist(), -1)
        
        mask = cv2.arrowedLine(mask, \
                pt1=(int(next_x), int(next_y)), \
                pt2=(int(prev_x), int(prev_y)), \
                color=color[i].tolist(),
                thickness=2,
                line_type=cv2.LINE_4,
                shift=0,
                tipLength=0.5)

    img = cv2.add(frame, mask)

    # 背景黒の画面。zerosの引数がheightが先になるので注意
    height_ = frame.shape[0]
    width_ = frame.shape[1]
    black_bachground_img = np.zeros((height_, width_, 3),np.uint8)

    # ウィンドウに表示
    #cv2.imshow('window', img)
    #cv2.imshow('window2', frame)
    #cv2.imshow('window3', mask)
    
    # 4つの画像を１つのウィンドウに組込む
    merged_image_group_1 = cv2.hconcat((frame, img))
    merged_image_group_2 = cv2.hconcat((mask, black_bachground_img))
    height = merged_image_group_1.shape[0]
    width = merged_image_group_1.shape[1]
    
    resized_output_img_1 = cv2.resize(merged_image_group_1, (int(1.7*width), int(1.7*height)))
    resized_output_img_2 = cv2.resize(merged_image_group_2, (int(1.7*width), int(1.7*height)))
    final_output_image = cv2.vconcat((resized_output_img_1, resized_output_img_2))
    
    cv2.namedWindow("Video", cv2.WINDOW_NORMAL)
    cv2.imshow("Video", final_output_image)

    # ESCキー押下で終了
    if cv2.waitKey(30) & 0xff == 27:
        break

    # 次のフレーム、ポイントの準備
    gray_prev = gray_next.copy()
    feature_prev = good_next.reshape(-1, 1, 2)
    end_flag, frame = cap.read()

# 終了処理
cv2.destroyAllWindows()
cap.release()
```