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

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

### 記事で猫memeを使いたい

皆さん、記事で猫memeを使いたいですよね！！！！（ヤケクソ）

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

<br>

### 記事作成までの流れ

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


<br>

### mp4動画をダウンロード

https://sasalabo.net/2024/02/27/%E7%8C%AB%E3%83%9F%E3%83%BC%E3%83%A0%E3%81%A8%E3%81%AF/#i-7

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

### mp4動画をgifに変換

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

pythonで作ろう！！

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

```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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935049/f20cd18e-78d8-0ef6-508d-a5abe37da5e9.gif)