みなさん、こんにちは!
先日、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
ちゃんと動画として保存できてますね!
苦労した点
-
FPSの値がよく分からなかった
カメラのFPS値を渡す必要があったのですが、この値が分からなかったです。
「cv2.CAP_PROP_FPS」で取得できることが分かり、どうにか解決しました。 -
グレースケールのままでは動画を保存できなかった
最初、何度試しても、mp4のヘッダしか保存されず、ここが一番時間がかかりました。
試しにカメラから取得したframeをそのまま保存してみたらできたので、グレースケールが原因と判明。
「cv2.COLOR_GRAY2BGR」で解決しました。
まとめ
今回は線画を応用して、アニメーションぽくしてみました。
簡単に保存できると思ってトライしてみましたが、思ったより苦戦しました。
しかし、初めて得た知識もあったので、チャレンジしてみて良かったです
これからも日々チャレンジして、スキルアップを目指します!