1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonで線画アニメーションを作ってみる(線画の応用)

Last updated at Posted at 2023-12-03

みなさん、こんにちは!

先日、Pythonで線画を作ってみるという記事を書きました。

これを応用した水墨画の記事も書きましたが、今度は、線画を動画として保存して、線画アニメーションぽくしてみようと思います。

要件

・画像ではなく、動画(mp4)として保存する
・ファイルサイズはできるだけ小さくする

コード

環境構築については、線画の記事をご確認ください。

sample.py
import cv2
import numpy as np

def line_drawing_image(img):
    # 画像のコントラストを調整
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    gray = cv2.equalizeHist(gray)

    # ガウシアンブラーを適用
    gray_blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    
    # エッジ検出で線画を生成
    edges = cv2.adaptiveThreshold(gray_blurred, 255,
                                  cv2.ADAPTIVE_THRESH_MEAN_C,
                                  cv2.THRESH_BINARY, 11, 7)
    
    return edges

def ink_painting_effect(img):
    # グレースケールに変換
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # バイラテラルフィルタを適用してぼかしを強化
    filtered = cv2.bilateralFilter(gray, 9, 200, 75)

    # CLAHEを適用してコントラストを強化
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(5, 5))
    contrast_enhanced = clahe.apply(filtered)

    # ハイライトを強調
    alpha = 1.3 # コントラスト
    beta = 40   # 明るさ
    highlighted = cv2.addWeighted(contrast_enhanced, alpha, np.zeros_like(contrast_enhanced), 0, beta)

    # エッジ検出(閾値を調整)
    edges = cv2.adaptiveThreshold(highlighted, 255,
                                  cv2.ADAPTIVE_THRESH_MEAN_C,
                                  cv2.THRESH_BINARY, 15, 7)

    # マスクを使用して元の画像とエッジを組み合わせる
    ink_painting = cv2.bitwise_and(highlighted, highlighted, mask=edges)

    return ink_painting

# VideoCaptureを作成
cap = cv2.VideoCapture(0)

# カメラのFPSを取得
fps = cap.get(cv2.CAP_PROP_FPS)

# カメラの解像度を取得
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

if not cap.isOpened():
    print("カメラが開けません。")
    exit()

fourcc = cv2.VideoWriter_fourcc(*'mp4v')
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# VideoWriterを作成
out = cv2.VideoWriter('line_painting.mp4', fourcc, fps, (frame_width, frame_height))

frame_count = 0

# カメラがオープンしている間はループ
while True:
    ret, frame = cap.read()
    if not ret:
        print("フレームの取得に失敗しました。")
        break

    # 線画風の画像を生成
    line_drawing = line_drawing_image(frame)
    # 水墨画風の画像を生成(こっちに切り替えると水墨画風になる)
    # line_drawing = ink_painting_effect(frame)

    # 20フレームごとに画像を保存(連続だとファイルサイズが大きくなるため)
    if frame_count != 0 and frame_count % 20 == 0:
        # グレースケール画像を3チャンネルのBGR画像に変換
        line_drawing_bgr = cv2.cvtColor(line_drawing, cv2.COLOR_GRAY2BGR)
        # VideoWriterを使用してフレームを書き込む
        out.write(line_drawing_bgr)

    frame_count += 1

    # 生成した画像を表示する
    cv2.imshow('Ink Painting Effect', line_drawing)


    # 'q'が押されたらループを抜ける
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# ループ終了後の処理
cap.release()  # カメラのリリース
out.release()  # VideoWriterのリリース
cv2.destroyAllWindows()

実行結果

python sample.py

実行するとこうなりました。
fKBmurg8.gif

ちゃんと動画として保存できてますね!

苦労した点

  • FPSの値がよく分からなかった
    カメラのFPS値を渡す必要があったのですが、この値が分からなかったです。
    「cv2.CAP_PROP_FPS」で取得できることが分かり、どうにか解決しました。

  • グレースケールのままでは動画を保存できなかった
    最初、何度試しても、mp4のヘッダしか保存されず、ここが一番時間がかかりました。
    試しにカメラから取得したframeをそのまま保存してみたらできたので、グレースケールが原因と判明。
    「cv2.COLOR_GRAY2BGR」で解決しました。

まとめ

今回は線画を応用して、アニメーションぽくしてみました。
簡単に保存できると思ってトライしてみましたが、思ったより苦戦しました。
しかし、初めて得た知識もあったので、チャレンジしてみて良かったです:smiley:

これからも日々チャレンジして、スキルアップを目指します!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?