OpenCV python版で動画を加工する

静止画像については以前に2つ記事書いてます
https://qiita.com/studio_haneya/items/a50847be3891daa8e2f8
https://qiita.com/studio_haneya/items/111f0975eae6fdecb1aa


# 1. インストールとimport


pipで入ります


```terminal
pip install opencv-python
```


importするときはcv2で呼びます


```python:importする
import cv2
```


# 2. 動画を読み込む


今回は「C0001.MP4」という名前のファイルを加工したいと思います


```python
path = './C0001.MP4'
cap = cv2.VideoCapture(path)
cap
```


cv2.VideoCaptureはカメラからリアルタイムで取得するのにも使えるようです
https://note.nkmk.me/python-opencv-videocapture-file-camera/


# 3. 動画の情報を取得


cap.get()で動画の情報を取得できます


```python:動画の縦横幅とFPSを取得
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)
print("size: ({}, {}), fps: {:1.2f}".format(width, height, fps))
```


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/1f3864fb-b803-9bef-b911-47499fbcc36e.png" width=40%>


```python:フレーム数を取得してFPSから動画時間を算出
frame_num = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
play_time = frame_num / fps / 60
print('{} frames, {:1.1f} minutes'.format(frame_num, play_time))
```


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/d7efabab-9608-2c6b-7a82-ea877a6ca454.png" width=50%>


# 4. 1フレームずつ画像を取得する


cv2.VideoCapture.read()を呼ぶ度に1フレームずつ読みだしてくれます


```python:
for k in range(3):
    ret, img = cap.read()
    if ret:
        plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
        plt.show()
```


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/155f2bc8-023f-6a84-5b9f-9891ccc9eaac.png" width=40%>



# 5. 動画を加工して保存する


cv2.VideoWriterクラスをつくり、write()メソッドに1フレームずつ渡せば書き出してくれます


```python:
# 書き出し用のWriteクラスを作成
fmt = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
writer = cv2.VideoWriter('./hoge.mp4', fmt, fps, (width, height),0)

while(True):
    # 1フレームずつ読み込み
    ret, img = cap.read()

    # 動画の最後までいったらretがFalseになるので終了する
    if not ret:
        break

    # 画像をグレースケールに加工
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 1フレーム分を書き出し
    writer.write(img_gray)

    # 読み込んだ動画と書き出し先の動画を解放する
    cap.release()
    writer.release()
```


imgはnp.arrayなので、python版のOpenCVで静止画像と同様に扱えます


# 6. 動画に音声を付けて保存する


2021年12月リリースのOpenCV 4.5.5では音声も扱えるようになったらしいんですが、該当するドキュメントが見つけられませんでしたので、従来通り別ライブラリに音声部分を追加してもらうやり方をやろうと思います。ここではmoviepyを使いました。


## 6-1. moviepyのインストール手順など


moviepyのインストール手順はこちら
https://pypi.org/project/moviepy/

moviepyのドキュメントはこちら
https://zulko.github.io/moviepy/getting_started/quick_presentation.html

moviepyはffmpegなどを使って動く為か上手く入らないことがあるようです。僕の場合はpip install moviepy -Iしたら直りました。


## 6-2. 動画全体を編集する場合


以下ではmoviepy.editor.VideoFileClipで動画ファイルを読み込んで音声トラックを書き出しておいて、先ほど書き出したhoge.mp4に追加してhoge2.mp4として書き出しています。


```python:
import moviepy.editor as mp

# 音声トラックを書き出す
clip_input = mp.VideoFileClip(path)
clip_input.audio.write_audiofile('./audio.mp3')

# 音声トラックを動画に追加する
clip = mp.VideoFileClip("./hoge.mp4").subclip()
clip.write_videofile("./hoge2.mp4", audio='./audio.mp3')
```


## 6-3. 動画の一部を編集する場合


まず開始フレームと終了フレームを決めて、開始時間と終了時間を算出しておきます


```python:開始位置と終了位置を指定
start_frame = 10000
end_frame = 11000

start_time = start_frame / fps
end_time = end_frame / fps
print('{}フレーム({:1.3f}秒)から{}フレーム({:1.3f}秒)まで切り出します'.format(start_frame, start_time, end_frame, end_time))
```


cap.set()で開始フレームに移動して、終了フレームまでを書き出します


```python:画像トラックの書き出し
cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)

fmt = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
writer = cv2.VideoWriter('./hoge.mp4', fmt, fps, (width, height), 0)

for _ in tqdm(range(end_frame - start_frame + 1)):
    ret, img = cap.read()

    if not ret:
        break

    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    writer.write(img_gray)

cap.release()
writer.release()
```


moviepyはsubclip()で開始時間と終了時間を指定するとトリミングしてくれるので、書き出したい範囲の音声トラックを書き出すことが出来ます。


```python:音声トラックを追加
# 音声トラックを書き出す
clip = mp.VideoFileClip("./hoge.mp4").subclip()
clip_input = mp.VideoFileClip(path).subclip(start_time, end_time)
clip_input.audio.write_audiofile('./audio.mp3')

# 音声トラックを動画に追加する
clip.write_videofile("./hoge2.mp4", audio='./audio.mp3')
```


フレームと時間で微妙にずれそうな気もしますが、フレーム単位で指定する方法が見つかりませんでした


# 参考にしたページなど


音声を付けて動画書き出しする手順を紹介しているサイト
https://kp-ft.com/684