2025年2月23日日曜日

Pythonで簡単にできる自動モザイク処理と動画編集方法(第2回) by裏方徹也

 第2回: Pythonで動画にコメントを追加する方法

こんにちは!裏方です!第2回の記事へようこそ!前回は、動画編集の基礎として、Pythonを使って単純に入力の動画をそのまま出力する基本的な操作を紹介しましたが、今回はさらに進んで、動画の各フレーム単位にデバッグ用のコメントを追加する方法を紹介します。


前回の記事はこちら

リンク: 前回の記事


★目標

今回の目標は、動画の各フレームに「フレーム番号」や「座標の目盛り」を表示することです。


フレーム番号: 動画内のフレームごとに番号を表示することで、特定のフレームに関するデバッグや処理の確認をしやすくします。

座標の目盛り: 動画内の100ピクセルごとに目盛りを表示することで、座標に基づく処理(物体追跡やエフェクト適用)の精度を視覚的に確認できます。


★出力例

実行前


実行後



★コードの準備その1

それでは実際にコードを書いてみましょう!

まずは、前回のコードをベースにして、

process_video(input_video_path) 関数の frame_count += 1 の直後に以下の2行を追加します。




        # 左上にフレーム番号を載せる
        frame = writeinfo(frame, frame_count, original_width, original_height) 
        # 目盛りを描画
        frame = add_scale_to_frame(frame, original_width, original_height)
    


① フレーム番号の追加

frame = writeinfo(frame, frame_count, original_width, original_height) 

ここでは、入力動画の映像(frame)とフレーム番号(frame_count)、動画の横の長さ(original_width)、動画の縦の長さ( original_height)を使ってwriteinfo処理を実行しています。writeinfoの詳細はコードの準備その2にて説明します。


② 座標の目盛りを追加

# 目盛りを描画

frame = add_scale_to_frame(frame, original_width, original_height)

次に②では、①でフレーム番号を付けた後の画面に、入力動画の映像(frame)とフレーム番号(frame_count)、動画の横の長さ(original_width)、動画の縦の長さ( original_height)を使ってadd_scale_to_frame処理を実行しています。add_scale_to_frameの詳細はコードの準備その2にて説明します。




★コードの準備その2

次に、下記のコードを記載します。

今回は便宜上、def process_video(input_video_path):の真下に追加しました。


        def writeinfo(frame, frame_count, width, height):
            # フレームにframe_countを表示
            cv2.rectangle(frame, (0, 0), (100, 40), (255, 255, 255), -1)
            cv2.putText(frame, f"{frame_count}", (0, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2, cv2.LINE_AA)
            return frame

        def add_scale_to_frame(frame, width, height, scale_interval=100, large_scale_interval=500):
            # 目盛りを描画
            font = cv2.FONT_HERSHEY_SIMPLEX
            font_scale = 0.5
            font_thickness = 1
            text_color = (0, 0, 0)
            red_color = (0, 0, 255)  # 赤色
            green_color = (0, 255, 0)  # 緑色

            # 縦横の目盛りを描画
            for y in range(scale_interval, height, scale_interval):
                cv2.line(frame, (0, y), (10, y), (0, 0, 0), 2)  # 目盛り線
                cv2.putText(frame, str(y), (15, y + 5), font, font_scale, text_color, font_thickness, cv2.LINE_AA)

            for x in range(scale_interval, width, scale_interval):
                cv2.line(frame, (x, 0), (x, 10), (0, 0, 0), 2)  # 目盛り線
                cv2.putText(frame, str(x), (x + 5, 20), font, font_scale, text_color, font_thickness, cv2.LINE_AA)

            # 100ピクセルごとの交点に赤い点を描画
            for x in range(scale_interval, width, scale_interval):
                for y in range(scale_interval, height, scale_interval):
                    cv2.circle(frame, (x, y), 3, red_color, -1)  # 赤い点
                    cv2.putText(frame, f"({x},{y})", (x + 5, y + 10), font, font_scale, red_color, font_thickness, cv2.LINE_AA)

            # 500ピクセルごとの交点に緑の点を描画
            for x in range(large_scale_interval, width, large_scale_interval):
                for y in range(large_scale_interval, height, large_scale_interval):
                    cv2.circle(frame, (x, y), 3, green_color, -1)  # 緑の点
                    cv2.putText(frame, f"({x},{y})", (x + 5, y + 10), font, font_scale, green_color, font_thickness, cv2.LINE_AA)

            return frame
    



③def writeinfo(frame, frame_count, width, height):

入力から1フレーム単位で、映像(frame)を受け取り、画面左上にフレームの番号(frame_count)に応じて番号を記載する処理をしています。


④add_scale_to_frame(frame, width, height)

入力から1フレーム単位で、映像(frame)を受け取り、100ピクセル毎に座標目盛りと赤色の点を、500ピクセル毎に緑色の点を付けています。動画の横の長さ(width)、縦の長さ(height)の上限まで目盛りと点を付しています。


★改良後の全体のコード


        import tkinter as tk
        from tkinter import filedialog
        import cv2
        import moviepy.editor as mp
        import os

        # ファイルダイアログで動画を選択
        def select_video_file():
            root = tk.Tk()
            root.withdraw()
            video_path = filedialog.askopenfilename(title="動画ファイルを選択", filetypes=[("動画ファイル", "*.mp4 *.avi *.mov *.mkv")])
            return video_path

        # 動画の音声と映像を出力
        def process_video(input_video_path):
            output_video_path_temp = input_video_path.rsplit('.', 1)[0] + "_outputtemp.mp4"
            output_video_path = input_video_path.rsplit('.', 1)[0] + "_output.mp4"

            cap = cv2.VideoCapture(input_video_path)
            original_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
            original_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
            fps = cap.get(cv2.CAP_PROP_FPS)
            total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

            fourcc = cv2.VideoWriter_fourcc(*'mp4v')
            out = cv2.VideoWriter(output_video_path_temp, fourcc, fps, (original_width, original_height))

            frame_count = 0

            while True:
                ret, frame = cap.read()
                if not ret:
                    break

                frame = writeinfo(frame, frame_count, original_width, original_height)
                frame = add_scale_to_frame(frame, original_width, original_height)

                frame_count += 1
                out.write(frame)

            cap.release()
            out.release()

            add_audio_to_video(input_video_path, output_video_path_temp, output_video_path, total_frames, fps)
            print(f"出力ファイル: {output_video_path}")

        # 音声を動画に統合
        def add_audio_to_video(input_video_path, output_video_path_temp, output_video_path, total_frames, fps):
            video_clip = mp.VideoFileClip(input_video_path)
            audio_clip = video_clip.audio
            duration = total_frames / fps
            audio_clip = audio_clip.subclip(0, duration)

            final_video = mp.VideoFileClip(output_video_path_temp)
            final_video = final_video.set_audio(audio_clip)
            final_video.write_videofile(output_video_path, codec="libx264", audio_codec="aac")

            if os.path.exists(output_video_path_temp):
                os.remove(output_video_path_temp)
                print(f"中間ファイル {output_video_path_temp} を削除しました。")


                def writeinfo(frame, frame_count, width, height):
            # フレームにframe_countを表示
            cv2.rectangle(frame, (0, 0), (100, 40), (255, 255, 255), -1)
            cv2.putText(frame, f"{frame_count}", (0, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2, cv2.LINE_AA)
            return frame

        def add_scale_to_frame(frame, width, height, scale_interval=100, large_scale_interval=500):
            # 目盛りを描画
            font = cv2.FONT_HERSHEY_SIMPLEX
            font_scale = 0.5
            font_thickness = 1
            text_color = (0, 0, 0)
            red_color = (0, 0, 255)  # 赤色
            green_color = (0, 255, 0)  # 緑色

            # 縦横の目盛りを描画
            for y in range(scale_interval, height, scale_interval):
                cv2.line(frame, (0, y), (10, y), (0, 0, 0), 2)  # 目盛り線
                cv2.putText(frame, str(y), (15, y + 5), font, font_scale, text_color, font_thickness, cv2.LINE_AA)

            for x in range(scale_interval, width, scale_interval):
                cv2.line(frame, (x, 0), (x, 10), (0, 0, 0), 2)  # 目盛り線
                cv2.putText(frame, str(x), (x + 5, 20), font, font_scale, text_color, font_thickness, cv2.LINE_AA)

            # 100ピクセルごとの交点に赤い点を描画
            for x in range(scale_interval, width, scale_interval):
                for y in range(scale_interval, height, scale_interval):
                    cv2.circle(frame, (x, y), 3, red_color, -1)  # 赤い点
                    cv2.putText(frame, f"({x},{y})", (x + 5, y + 10), font, font_scale, red_color, font_thickness, cv2.LINE_AA)

            # 500ピクセルごとの交点に緑の点を描画
            for x in range(large_scale_interval, width, large_scale_interval):
                for y in range(large_scale_interval, height, large_scale_interval):
                    cv2.circle(frame, (x, y), 3, green_color, -1)  # 緑の点
                    cv2.putText(frame, f"({x},{y})", (x + 5, y + 10), font, font_scale, green_color, font_thickness, cv2.LINE_AA)

            return frame


        # メイン処理
        def main():
            video_file = select_video_file()
            if video_file:
                process_video(video_file)
            else:
                print("動画ファイルが選択されませんでした。")

        if __name__ == "__main__":
            main()
    

★実行例

上記のコードを実行してみた結果、以下の通りとなりました。


〇実行前


〇実行後





実行後の動画には画面左上にフレーム番号が記載されており、100ピクセル毎に印がプロットされていますね。


★まとめ

今回は、動画にデバッグ用のコメントを追加する方法を紹介しました。動画に動的にフレーム番号や座標を表示することで、デバッグやトラブルシューティングを効率的に行うことができます。


次回は、いよいよ顔検出技術を使って動画にモザイクをかける方法を紹介しますので、ぜひお楽しみに!


2025年2月19日水曜日

Pythonで簡単にできる自動モザイク処理と動画編集方法(第1回) by裏方徹也

長年ほったらかしてるこのブログで裏方氏がプログラミングをレクチャーしたいそうですので興味ある方はご覧ください。以下は裏方氏の執筆です↓

Pythonで初心者向け!自動モザイク処理と簡単な動画編集方法【初心者向け】

裏方徹也です!ブログ初心者ですが、よろしくお願いします!撮影の舞台裏を発信していきます!

1弾として、最近公開した動画でちらっと紹介した自動モザイク処理のプログラムについて、全4回程度にわたってその制作過程をお伝えしていきます!このシリーズを通して、プログラミング初心者でも動画編集が簡単にできるようになる方法を紹介していきますので、ぜひ最後までチェックしてみてください。

 

題材の1万円チャレンジはこちらから

 

この記事のポイント

Pythonを使って簡単に動画編集ができるようになる

・自動モザイク処理を学び、手間を省く方法を紹介

・初心者でも使いやすい、Eclipseを使った開発環境構築方法

これからの連載では、さらに進んだ内容として、顔検出技術を使った動画編集方法も紹介予定です。次回以降もお楽しみに!

 

連載予定

1: Pythonで簡単に動画編集!動画を単純出力する方法今回の内容

2: 動画にデバッグ用のコメントを追加する方法

3: 顔検出を使って動画にモザイクをかける方法

4: 一定間隔で顔を検出し、自動でモザイクをかける方法

 

動画編集にプログラミングを使うメリット

動画編集には、何度も同じ作業を繰り返さなければならないシーンが多くあります。例えば、モザイク処理を一つ一つ手作業で行うのは非常に手間がかかりますよね。そこで、プログラミングを使うことで、このような作業を 自動化することができます。

Pythonを使えば、モザイク処理や動画の出力、さらには音声の追加もスムーズに行えるので、面倒な作業を効率的にこなすことができるんです。これを実現するために、最初に簡単な動画出力方法を学び、次回以降でさらに進んだ内容を紹介していきます!

 

★Eclipseを使って簡単に開発環境を整える方法

プログラミング初心者の方には、開発環境のセットアップが少し不安かもしれませんが、Eclipseという無料の統合開発環境(IDE)を使えば、Pythonの開発が非常に楽になります。Eclipseは無料で使え導入が簡単なので、初心者でもスムーズにプログラミングを始められます。

Eclipseを導入すれば、エラーメッセージが見やすく、デバッグも簡単になります。Pythonでのプログラミングをする際には、Eclipseを使うと効率的です。

 

★Eclipseの導入方法

・公式サイト(https://willbrains.jp/)からEclipsePythonが含まれているもの)を使用予定のPCOSおよびbit数に応じてダウンロード

zipファイルを解凍する

・解凍後はeclipse.exeをクリック

※Eclipseは容量が1GB程度あるのでご注意ください。

 

新規プロジェクトの作り方

次に新規プロジェクト及びモジュールを作成します。

解凍したフォルダにあるeclipse.exeをクリックし、コードを作成したいフォルダをつくります。

以下のように画面左上の新規>その他をクリックします。

 


次に、Pydevプロジェクトを選び、次へボタンを押します。

 

 


 


プロジェクト名に適当な名前入力し、文法バージョンを選びます。今回は便宜上、最新版の3.12を使用しています。



 

すると、画面左にTESTプロジェクトがでました。



 

次に、モジュールを作ります。先ほど作ったTESTプロジェクトを右クリックし、pydevモジュールを選びます。



次に名前を適当につけます。今回はTEST1としました。



このあと選択画面が出ますが、デフォルトのままOKを押します。

すると、TESTプロジェクト配下にTESTモジュールができました。

このモジュールに実行したいコードを書きます。



 

コードが書けたら、実行(R)>実行(R)を選択し、以下のようにPython実行を押すとコードが動きます。



試しにHELLOW WORLDをコンソールに出力してみましょう。

以下のコードをTEST1にコピペして実行してください。

print("HELLO WORLD")

すると、コンソールにHELLO WORLDが表示されます。



 

これで、動画編集に必要な準備が整いました!

続いて、動画を単純に出力するコードを見てみましょう!

 

コードの概要

まず最初に、動画をそのまま出力するコードを紹介します。このコードでは、ユーザーが選んだ動画をそのまま映像として出力し、音声も追加する処理を行います。

 

コード(全体)

import tkinter as tk

from tkinter import filedialog

import cv2

import moviepy.editor as mp

import os

 

# ファイルダイアログで動画を選択

def select_video_file():

    root = tk.Tk()

    root.withdraw()  # Tkinterウィンドウを非表示にする

    video_path = filedialog.askopenfilename(title="動画ファイルを選択", filetypes=[("動画ファイル", "*.mp4 *.avi *.mov *.mkv")])

    return video_path

 

# 動画の音声と映像を出力

def process_video(input_video_path):   

    # 中間ファイル作成(映像だけ出力)

    output_video_path_temp = input_video_path.rsplit('.', 1)[0] + "_outputtemp.mp4"

   

    # 出力ファイルのパスを生成(元の動画と同じディレクトリにoutput.mp4として保存)

    output_video_path = input_video_path.rsplit('.', 1)[0] + "_output.mp4"

 

   

    # 動画キャプチャの準備

    cap = cv2.VideoCapture(input_video_path)

    original_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))

    original_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

    fps = cap.get(cv2.CAP_PROP_FPS)

   

    # 総フレーム数を取得

    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

 

    # 出力動画ファイルの作成(音声なしで映像のみ)

    fourcc = cv2.VideoWriter_fourcc(*'mp4v')

    out = cv2.VideoWriter(output_video_path_temp, fourcc, fps, (original_width, original_height))

 

    frame_count = 0

 

    while True:               

        # 動画の1フレームを読み込む

        ret, frame = cap.read()

        if not ret:

            break  # 動画の終わりに達したら終了

       

        frame_count += 1  # フレーム番号をインクリメント

        out.write(frame)

 

        cv2.imshow("frame", frame)

        cv2.waitKey(5)

 

    # 動画キャプチャと書き込みを解放

    cap.release()

    out.release()

    cv2.destroyAllWindows()

 

    # 動画と音声をそのまま出力

    add_audio_to_video(input_video_path, output_video_path_temp,output_video_path, total_frames,fps)

   

    print(f"出力ファイル: {output_video_path}")

 

 

# 音声を動画に統合する

def add_audio_to_video(input_video_path, output_video_path_temp,output_video_path, total_frames,fps):

    # moviepyで動画と音声を読み込む

    video_clip = mp.VideoFileClip(input_video_path)

    audio_clip = video_clip.audio

 

    # 映像部分の長さと音声の長さが一致するように、音声を調整

    duration = total_frames / fps  # 映像の長さ(秒)

    audio_clip = audio_clip.subclip(0, duration)  # 音声を映像の長さに合わせる

   

   

    # 映像と音声を統合

    final_video = mp.VideoFileClip(output_video_path_temp)

    final_video = final_video.set_audio(audio_clip)

 

    # 音声付きの動画を保存

    final_video.write_videofile(output_video_path, codec="libx264", audio_codec="aac")

   

    # 中間ファイル(output_video_path)を削除

    if os.path.exists(output_video_path_temp):

        os.remove(output_video_path_temp)

        print(f"中間ファイル {output_video_path_temp} を削除しました。")

   

    print(f"音声付きの動画が保存されました。最終ファイル名: {output_video_path_temp}")

 

   

# メイン処理

def main():

    video_file = select_video_file()

    if video_file:

        process_video(video_file)

    else:

        print("動画ファイルが選択されませんでした。")

 

if __name__ == "__main__":

    main()

 

 

 

処理全体の流れ

処理の内容は以下となります。

①def main(): 処理対象の動画を選択し、process_videoに処理をcallする。

②process_video(): 映像をフレーム単位で出力する

③add_audio_to_video():②で出力した動画に音声を追加して、音声付きの動画として出力する

今回初心者向けということで詳細な処理内容は割愛させて頂きます。

 

ワンポイントアドバイス

もし、出力した動画に何かしら加工をしたい場合、

process_video処理のwhile True:out.write(frame)より前で処理を追加しましょう。

例:画面全体にぼかしを入れたいとき

frame = cv2.GaussianBlur(frame, (11, 11), 11)

 

    while True:               

        # 動画の1フレームを読み込む

        ret, frame = cap.read()

        if not ret:

            break  # 動画の終わりに達したら終了

       

        frame_count += 1  # フレーム番号をインクリメント

 

         frame = cv2.GaussianBlur(frame, (11, 11), 11)←追加

 

        out.write(frame)

 

        cv2.imshow("frame", frame)

        cv2.waitKey(5)

実行すると、画面全体にぼかしがかかった状態で出力されます。

 

まとめ

今回は、Pythonを使って簡単に動画を編集する方法を紹介しました。Eclipseを使って開発環境を整えるところから始め、最終的に音声付きの動画を出力する処理までを行いました。

次回は、このコードを使って「デバッグ用のコメントを動画に追加する方法」を紹介します。プログラミングを活用すれば、面倒な作業がどんどん効率化され、動画編集がぐっと楽になりますので、ぜひ次回もお楽しみに!

 

次回予告

2: 動画にデバッグ用のコメントを追加する方法