以前、画像にノイズを加える方法を書きましたが、動画への適用を試してみました。動画の読み込み、編集、出力と3段構えの内容ですが、結果としては以下のような動画ができます。
鹿せんべいを食べるかわいいかわいい小鹿ちゃんの動画です。左が元の動画、右がノイズを加えた動画です。ローカル環境で見るよりも若干かくかくしている感じがしますが、雰囲気は伝わるかなと思います。
では、順に処理をしていきます。使うのはいつものOpenCVです。
動画の読み込み
動画の読み込みはopencvのVideoCapture()で行います。
動画は1秒間にFPS (frame per second) 枚の画像が格納された画像群と見なすことができますので、動画の読み込みとはつまり、これらの画像群を順に読み込むことを意味します。
動画内の画像群を順に読み込み
動画の読み込みは.read()メソッドを使用します。戻り値は以下の2つです。
- ret: フレームが読み込めたか(bool値)=フレームを全部読み終わったらFalse
- frame: 読み込んだフレームの画像(array)
import cv2
cap = cv2.VideoCapture('Input.mp4')    # 動画の読み込み
while True:
    ret, frame = cap.read()  # フレームを順に読み込み
    if not ret:  
        # フレームが読み込めない=最終フレームまで行ったら終了
        break
読み込んだ動画の情報を表示
読み込んだ動画情報capのそれぞれの情報は下で取得することができます。動画全体の長さは、n_frame÷fpsで求められますね。
w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # 画像サイズ(幅) h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 画像サイズ(高さ) fps = cap.get(cv2.CAP_PROP_FPS) # FPS n_frame = cap.get(cv2.CAP_PROP_FRAME_COUNT) # 動画のフレーム数
取り出した画像の編集
画像として取り出していますので、単純な画像処理で対応できます。
以前に作成したノイズ付与の関数 (defの部分) をそのまま使います。
import numpy as np
import cv2
np.random.seed(0)
def add_noise(img, mu=0, sigma=100):
    # 画素数xRGBのノイズを生成
    noise = np.random.normal(mu, sigma, img.shape)
    # ノイズを付加して8bitの範囲にクリップ
    noisy_img = img.astype(np.float64) + noise
    noisy_img = np.clip(noisy_img, 0, 255).astype(np.uint8)
    return noisy_img
img = np.ones((400, 400, 3), np.uint8) * 127
noisy_img = add_noise(img)
cv2.imwrite('noisy_img.png', cv2.hconcat((img, noisy_img)))
サンプルの出力はこんな感じになります。グレーの画像にノイズがのった状態です。

動画の出力
最後、動画の出力です。読み込みとは逆に画像群をまとめるという動作でしょうか。下の例ではとりあえず、現在位置にあるjpgを動画に変換する処理をしています。
h, wとfpsは読み込んだ画像サイズと作りたい動画の速さに応じて設定します。
# フォルダ内の画像を全部リストアップしてソート
images = glob.glob('*.jpg')
images.sorted()
# 出力フォーマットを指定: mp4の場合
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
# 出力用のインスタンスを作成
# 必要なのは出力ファイル名、フォーマット、FPS、画像サイズ
out = cv2.VideoWriter('output.mp4',fourcc, fps, (w, h))
# 画像を順に読み込んで動画に追加
for image_name in images:
    image = cv2.imread(image_name)
    out.write(image)
# 全部終わったら処理を終了させる
out.release()
まとめ:動画を編集して出力
ということで、これらを全部まとめたのが以下のコードです。
Input.mp4を読み込んでoutput.mp4を出力します(処理に少し時間がかかります)
import numpy as np
import cv2
np.random.seed(0)
def add_noise(img, mu=0, sigma=100):
    # 画素数xRGBのノイズを生成
    noise = np.random.normal(mu, sigma, img.shape)
    # ノイズを付加して8bitの範囲にクリップ
    noisy_img = img.astype(np.float64) + noise
    noisy_img = np.clip(noisy_img, 0, 255).astype(np.uint8)
    
    return noisy_img
cap = cv2.VideoCapture('Input.mp4')
w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)
# 出力の準備
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
out = cv2.VideoWriter('output.mp4',fourcc, fps, (w*2, h))    # ノイズ付加前後の画像をconcatするので幅は2倍に設定
while True:
    # フレームを順に読み込んでノイズ付加
    ret, frame = cap.read()
    if not ret:
        break
    
    # ノイズを加えて、オリジナル画像にconcat
    noisy_frame = add_noise(frame)
    out_frame = cv2.hconcat((frame, noisy_frame))
    out.write(out_frame)
        
# 全部終わったら処理を終了させる
cap.release()
out.release()
	