python wxpython + openCVでシンプルな動画解析ツールを作成

## アジェンダ

1. はじめに
2. 出来上がったツールのデモ
3. openCVで動画解析
4. wxpythonでGUI作成
5. pyinstallerでexe化


## はじめに

動画中の「どの時間に」「どんな人物がでてくるか」を知りたいって思ったことがあって、
勉強していたwxpythonとopenCVでシンプルな動画解析ツールを作成してみました。
自分用のメモも兼ねて、ここで紹介させていただきます。

ダウンロードはこちらから
[GitLab ChuntaMovieAnalyzer](https://gitlab.com/RyutaKaii/chuntamovieanalyzer)


## 出来上がったツールのデモ

まずはツールのデモ画面から。
動画を指定してhtmlのレポートを出力します。
動画の秒数ごとに人間の顔を抽出して表示しています。
GUIで秒数の間隔や抽出する最大人数を変更できます。

![capture_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527168/a1d520a6-fd23-2675-5f78-356e706fb55f.jpeg)
![capture_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527168/7c8c7e1e-404a-eb42-0171-95fa6bc2abdc.jpeg)


ダウンロードはこちらから
[GitLab ChuntaMovieAnalyzer](https://gitlab.com/RyutaKaii/chuntamovieanalyzer)


## openCVで動画解析

動画を解析する際に利用したライブラリはopenCVです。
今回は動画の1フレームを画像データにし、
画像データから顔を検出し、トリミングしています。
トリミングしたデータはhtml用の画像として出力しています。
ポイントと思われる部分のコードについて紹介します。


### 動画を読み込んで画像を出力

動画を読み込んで、動画の総フレーム数とfps(1秒あたりのフレーム数)を利用し、指定された秒数の間隔ごとに処理を行っています。

```
def get_sampling_data_list(self, movie_path, report_path, interval_time, max_human, is_advance):
    sampling_data_list = []
    
    cap = cv2.VideoCapture(movie_path)
    
    if not cap.isOpened():
        print('cannot open movie file.')
        return []
    
    # 総フレーム数を取得
    all_frame_cnt = cap.get(cv2.CAP_PROP_FRAME_COUNT)
    
    # フレームレートからサンプリングするフレーム数を算出
    fps = cap.get(cv2.CAP_PROP_FPS)
    sampling_frame_num = math.floor( fps * interval_time )
    
    frame_cnt = 1
    
    while True:
        cap.set(cv2.CAP_PROP_POS_FRAMES, frame_cnt)
        ret, frame = cap.read()
        
        if ret:
            sampling_data = self.get_sampling_data(frame, frame_cnt, fps, report_path, max_human, is_advance)
            sampling_data_list.append(sampling_data)
            
            frame_cnt = frame_cnt + sampling_frame_num
        else:
            break
    
    return sampling_data_list
```


### 1フレームごとの処理

1フレームを画像データ化 ⇒ 画像データから顔を検出 ⇒ トリミング ⇒ html用の画像として出力
の順で処理を行っています。

```
def get_sampling_data(self, frame, frame_cnt, fps, report_path, max_human, is_advance):
    data = sampling_data(frame_cnt, fps)
    
    face_cascade = cv2.CascadeClassifier(resourcePath('cascade/haarcascade_frontalface_default.xml'))
    src_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faceInfoList = face_cascade.detectMultiScale(src_gray)
    
    human_cnt = 0
    
    for x, y, w, h in faceInfoList:
        # 最大人数を超えている場合はループを抜ける
        if human_cnt >= max_human:
            break
        
        # トリミング
        img_trim = frame[y:(y + h), x:(x + w)]
        
        data2 = None
        
        data2 = human_data()
        
        # 画像出力用フォルダを作成
        if False == os.path.exists(report_path + '/img'):
            os.mkdir(report_path + '/img')
        
        # 画像出力
        file_name = str(frame_cnt) + '_' + str(human_cnt) + '.jpg'
        cv2.imwrite(report_path + '/img/' + file_name, img_trim)
        data2.set_img_src(report_path, file_name)
        
        data.append_human_data(data2)
        
        human_cnt = human_cnt + 1
    
    return data
```


## wxpythonでGUI作成

GUI部分のソースです。
ポイントと思われる部分のコードについて紹介します。

### 動画解析処理呼び出し処理

動画解析処理は時間がかかるため、非同期で呼び出さないとビジー状態になってしまいます。
これを回避するために、呼び出し部分は非同期にしています。

```
import threading
```

```
# 処理に時間がかかり画面がフリーズするのを防ぐためにスレッドキックする
analysis_thread = threading.Thread(target=exec_analyze, args=(self.analysis, self.btn, movie_path, interval_time, max_human, is_advance, token, report_path))
analysis_thread.start()
```

```
def exec_analyze(analysis, btn, movie_path, interval_time, max_human, is_advance, token, report_path):
    analysis.analyze(movie_path, int(interval_time), int(max_human), is_advance, token, report_path)
    
    wx.MessageBox('指定されたフォルダにhtmlファイルを出力しました。', '処理完了')
```


## exe化

exe化する際はpyinstallerを利用しました。
外部リソースをexeに同封する際はひと工夫必要でした。
下記のサイトを参考に実施しました。

参考にしたURL
[Pyinstaller でリソースを含めたexeを作成する](https://qiita.com/ELIXIR/items/48f276874367b2bc19bd)

コマンドプロンプトで下記を実行

```
pyinstaller --onefile --icon=icon.ico -n chuntaMovieAnalyzer view_gui.py
```

作成されたspecファイルを編集し、再度コマンドプロンプトで下記を実行

```
pyinstaller chuntaMovieAnalyzer.spec
```

ダウンロードはこちらから
[GitLab ChuntaMovieAnalyzer](https://gitlab.com/RyutaKaii/chuntamovieanalyzer)