0
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?

爆速で猫memeで記事を作ろう

Last updated at Posted at 2024-11-20

グリーンバック動画からgifファイルを作る

記事で猫memeを使いたい

皆さん、記事で猫memeを使いたいですよね!!!!(ヤケクソ)

ということで謎のユーティリティツールを紹介します!!!!


記事作成までの流れ

  • mp4動画をダウンロード
  • mp4動画をgifに変換
  • 好きなとこにgifを貼り付けよう

mp4動画をダウンロード

ここが素晴らしいです!!

mp4動画をgifに変換

ちょっとネット探すと動画を透過してgifにするサイトが見つかるが
https通信じゃないとかウォータマークがつくつかダルい!!!

pythonで作ろう!!

自分用の殴り書きなので、自分好みに調整してください

mp4_to_gif_with_GB_chroma_key.py
import cv2
import numpy as np
from PIL import Image
import os

def create_palette_from_frames(frames):
    """
    フレームから最適なパレットを作成する
    """
    # 一時的な画像を作成してパレットを抽出
    total_pixels = np.zeros((0, 3), dtype=np.uint8)
    
    for frame in frames:
        frame_array = np.array(frame.convert('RGB'))
        pixels = frame_array.reshape(-1, 3)
        total_pixels = np.vstack((total_pixels, pixels))
    
    # K-means クラスタリングで代表的な色を選択
    total_pixels = total_pixels[::10]  # サンプリングして処理を軽くする
    if len(total_pixels) > 0:
        total_pixels = total_pixels.astype(np.float32)
        criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
        _, labels, palette = cv2.kmeans(total_pixels, 255, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
        palette = palette.astype(np.uint8)
        return Image.new('P', (1, 1))._new(palette.tolist())
    return None

def process_video(input_file, output_file, green_sensitivity=40, default_fps=30):
    """
    MP4動画をクロマキー処理してGIFに変換する関数(パレット最適化版)
    
    Parameters:
        input_file (str): 入力MP4ファイルのパス
        output_file (str): 出力GIFファイルのパス
        green_sensitivity (int): 緑色の検出感度(値が大きいほど広い範囲を透過)
        default_fps (int): FPSが取得できない場合のデフォルト値
    """
    # 動画を読み込む
    cap = cv2.VideoCapture(input_file)
    
    if not cap.isOpened():
        print("動画ファイルを開けませんでした。")
        return
    
    frames = []
    
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        
        # BGRからRGBに変換
        frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        
        # HSV色空間に変換
        frame_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        
        # 緑色の範囲を定義
        lower_green = np.array([60 - green_sensitivity, 50, 50])
        upper_green = np.array([60 + green_sensitivity, 255, 255])
        
        # マスクを作成
        mask = cv2.inRange(frame_hsv, lower_green, upper_green)
        
        # マスクの微調整
        kernel = np.ones((3,3), np.uint8)
        mask = cv2.erode(mask, kernel, iterations=1)
        mask = cv2.dilate(mask, kernel, iterations=1)
        
        # マスクを反転
        mask = cv2.bitwise_not(mask)
        
        # PILイメージに変換
        pil_image = Image.fromarray(frame_rgb)
        
        # RGBA形式に変換
        pil_image = pil_image.convert('RGBA')
        
        # アルファチャンネルを設定
        data = np.array(pil_image)
        data[:, :, 3] = mask
        
        # 完全に透明なピクセルのRGB値をクリア
        data[data[:, :, 3] == 0] = [0, 0, 0, 0]
        
        pil_image = Image.fromarray(data)
        frames.append(pil_image)
    
    cap.release()
    
    if not frames:
        print("フレームを取得できませんでした。")
        return
    
    # FPSを30に指定
    original_fps = 30    
    frame_duration = int(1000 / original_fps)
    print(f"FPS: {original_fps}")
    print(f"フレーム間隔: {frame_duration}ms")

    # リサイズしたい幅を指定
    target_width = 400  # 出力GIFの幅を指定(例: 640px)
    print(f"リサイズ幅: {target_width}px")
    
    # 透過情報をmaskとしてindex255として貼り付け、リサイズも一緒にやる
    converted_frames = []
    for frame in frames:
        # 元のサイズ
        original_width, original_height = frame.size
        # 縦横比を保持して新しい高さを計算
        aspect_ratio = original_height / original_width
        target_height = int(target_width * aspect_ratio)
        # フレームをリサイズ
        frame = frame.resize((target_width, target_height), Image.Resampling.LANCZOS)
        alpha = frame.split()[3]
        frame = frame.convert('RGB').convert('P', palette=Image.ADAPTIVE, colors=255)
        mask = Image.eval(alpha, lambda a: 255 if a <= 128 else 0)
        frame.paste(255, mask=mask)
        converted_frames.append(frame)

    converted_frames[0].save(
        output_file,
        save_all=True,
        append_images=converted_frames[1:],
        duration=frame_duration,
        loop=0,
        disposal=2,
        transparency=255,  # 設定したインデックスを渡す
        optimize=False
    )
    print(f"変換完了: {output_file}")
        
# 使用例
if __name__ == "__main__":
    input_video = r'mp4動画のfull path'
    output_gif = r'出力gifのfull path'
    
    if os.path.exists(input_video):
        process_video(input_video, output_gif, green_sensitivity=40)
    else:
        print(f"入力ファイル {input_video} が見つかりません。")

好きなとこにgifを貼り付けよう

chinese cat.gif

0
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
0
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?