人類サンタ化計画🎅🤶（アプリ公開中）

# 1. はじめに


ある日Twitterを眺めていると、思わず目を引くツイートが流れてきました。

https://x.com/sunlight3shine/status/1863958013461287218

なんと、iOSアプリ開発者のharuさんがサンタになっていました！
羨ましい、、、私もサンタになりたい！！

https://x.com/sunlight3shine/status/1864243453762392400


この瞬間『人類サンタ化計画🎅🤶』が始動しました。


# 2. 実装

## 2.1. 仕様

このアプリは、**とても簡単に**サンタクロースの帽子を追加したプロフィール画像を作成することで、**人類のサンタ化を促進**するものです。

主な仕様は以下の通りです：
- **画像アップロード機能**: ユーザーが任意の画像をアップロード可能。
- **帽子のカスタマイズ**: 帽子のサイズ、位置、回転をスライダーで自由に調整可能。
- **画像保存・ダウンロード**: 生成した画像をローカルに保存してダウンロード可能。

簡単な操作で誰でもサンタに変身できます！

## 2.2. ディレクトリ構成
以下はプロジェクトのディレクトリ構成です：
```
santa-claus-hats/
├── app.py                 # Streamlitアプリのメインスクリプト
├── requirements.txt       # 必要なパッケージ一覧
├── assets/                # 静的ファイルを格納
│   └── santa_hat.png      # サンタクロースの帽子画像
├── outputs/               # ユーザーが生成した画像の保存先
└── utils/                 # ユーティリティ関数やヘルパースクリプトを格納
```

ソースコードは下記のGitHubレポジトリからご確認ください：

https://github.com/Isaka-code/santa-claus-hats

## 2.3. ソースコード

### 主なポイント

以下はアプリ実装の主要なポイントです：

1. **StreamlitによるシンプルなUI設計**: プロフィール画像のアップロードから帽子の調整、生成画像のプレビューまでを一貫して提供。
2. **Pillowを使った画像処理**: 帽子の位置、サイズ、回転をリアルタイムで調整可能。
3. **ダウンロード機能**: 生成画像を即座にダウンロードできる手軽さ。

### app.py

```python
import os

import streamlit as st
from PIL import Image

# サンタクロースの帽子画像のパス
SANTA_HAT_PATH = "assets/santa_hat.png"
OUTPUT_DIR = "outputs"


# ディレクトリの準備
def prepare_directories():
    os.makedirs(OUTPUT_DIR, exist_ok=True)


# サンタクロースの帽子をプロフィール画像に合成する関数
def add_santa_hat(
    profile_image: Image.Image,
    santa_hat: Image.Image,
    hat_scale: float,
    hat_offset_x: int,
    hat_offset_y: int,
    hat_rotation: int,
) -> Image.Image:
    """
    プロフィール画像にサンタクロースの帽子を重ねる。

    Parameters:
        profile_image (Image.Image): ユーザーがアップロードしたプロフィール画像。
        santa_hat (Image.Image): サンタクロースの帽子画像。
        hat_scale (float): 帽子のサイズを調整するスケール。
        hat_offset_x (int): 帽子の横方向のオフセット。
        hat_offset_y (int): 帽子の縦方向のオフセット。
        hat_rotation (int): 帽子の回転角度。

    Returns:
        Image.Image: 合成された画像。
    """
    profile_image = profile_image.convert("RGBA")
    santa_hat = santa_hat.convert("RGBA")

    # 帽子のサイズをプロフィール画像に合わせて調整
    hat_width = int(profile_image.width * hat_scale)
    hat_height = int(santa_hat.height * (hat_width / santa_hat.width))
    santa_hat = santa_hat.resize((hat_width, hat_height), Image.Resampling.LANCZOS)

    # 帽子を回転
    santa_hat = santa_hat.rotate(hat_rotation, expand=True)

    # 帽子の位置（中央に配置し、オフセットを適用）
    hat_position = (
        (profile_image.width - santa_hat.width) // 2
        + hat_offset_x,  # 中央揃え + 横方向オフセット
        hat_offset_y,  # 縦方向のオフセット
    )

    # 画像を合成
    combined_image = Image.alpha_composite(
        profile_image, Image.new("RGBA", profile_image.size, (0, 0, 0, 0))
    )
    combined_image.paste(santa_hat, hat_position, santa_hat)

    return combined_image


# Streamlitアプリ
def main():
    st.title("サンタプロフィール画像メーカー")

    # サンタ帽子のロード
    try:
        santa_hat = Image.open(SANTA_HAT_PATH)
    except FileNotFoundError:
        st.error(
            "サンタクロースの帽子画像が見つかりません！assetsディレクトリにsanta_hat.pngを配置してください。"
        )
        return

    # プロフィール画像のアップロード
    uploaded_file = st.file_uploader(
        "プロフィール画像をアップロードしてください", type=["jpg", "jpeg", "png"]
    )

    if uploaded_file is not None:
        profile_image = Image.open(uploaded_file)

        # 帽子のサイズ、位置、回転を調整するスライダー
        hat_scale = st.slider(
            "帽子のサイズ (画像幅に対する割合)",
            min_value=0.1,
            max_value=1.0,
            value=0.6,
            step=0.05,
        )
        hat_offset_x = st.slider(
            "帽子の横方向位置調整",
            min_value=-profile_image.width,
            max_value=profile_image.width,
            value=0,
            step=5,
        )
        hat_offset_y = st.slider(
            "帽子の縦方向位置調整",
            min_value=-profile_image.height,
            max_value=profile_image.height,
            value=-10,
            step=5,
        )
        hat_rotation = st.slider(
            "帽子の回転角度 (度)", min_value=-180, max_value=180, value=0, step=5
        )

        # サンタクロースの帽子を合成
        combined_image = add_santa_hat(
            profile_image,
            santa_hat,
            hat_scale,
            hat_offset_x,
            hat_offset_y,
            hat_rotation,
        )

        # 合成画像を表示
        st.image(
            combined_image,
            caption="サンタクロースプロフィール画像",
            use_container_width=True,
        )

        # 画像をダウンロード可能にする
        output_path = os.path.join(OUTPUT_DIR, "santa_profile.png")
        combined_image.save(output_path, format="PNG")

        with open(output_path, "rb") as file:
            st.download_button(
                label="画像をダウンロード",
                data=file,
                file_name="santa_profile.png",
                mime="image/png",
            )


if __name__ == "__main__":
    prepare_directories()
    main()
```

# 3. セキュリティについて補足

本アプリでは、以下のような点に配慮しています：

1. **データの削除**: アップロードされたデータはアプリのリロード時に削除されます。

2. **データの非公開性**: アップロードされたデータが他のユーザーに閲覧されることはありません。

3. **秘匿性の高い画像のアップロードは非推奨**: 傍受の可能性を完全に排除することはできないため、秘匿性の高い画像のアップロードはお控えください。

これらの点を踏まえ、適切にアプリをご利用ください。

# 4. アプリの使い方

アプリの使い方はとても簡単です。

1. **画像をアップロードする**: 任意のプロフィール画像をアップロードします。

2. **帽子をカスタマイズする**: スライダーを操作して、帽子のサイズ、位置、回転を調整します。

3. **画像をダウンロードする**: アプリ下部の“画像をダウンロード”ボタンを押して、生成画像を保存します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627015/00b289fa-3503-9d25-1761-90ea6fb40110.png)




# 5. アプリのリンク

以下のリンクからアプリをお試しください。皆さんも一緒にサンタになりましょう！🎅🤶

https://santa-claus-hats.streamlit.app/