被写体を切り抜いて、簡単に背景を簡単に変更・生成できるアプリケーションを作ってみた話

この記事は GMOインターネットグループ Advent Calendar 2024 17日目の記事です。

みなさん、おはようございます!こんにちは!こんばんは!
GMOペパボ株式会社の横山です。
今回のアドベントカレンダーでは、被写体を切り抜いて、簡単に背景を簡単に変更・生成できるアプリケーションを学習しつつ作ったときの記録を残したいと思います。

はじめに

こんにちは!GMOペパボ株式会社所属の横山です。社内ではあだ名で呼び合う文化があり、はるおつと呼ばれています。普段は誰でも簡単にマルチプレイ用ゲームサーバーが立てられる機能を提供する、ロリポップ for gamersでエンジニアをしています。

さて、みなさんAIしていますでしょうか。大AI時代のビッグウェーブに常に乗っていたいですね。
今回は私がそんな気持ちでふと、画像生成AIを使ってみたい、どうせなら実応用できそうなもので試してみるかという気持ちで学習しつつ作ったアプリケーションについて紹介します。

SNSやフリマアプリに画像をアップロードする際、次のような経験はありませんか?

  • 背景がごちゃごちゃしていて見栄えが悪い。
  • テーマに合った背景や撮影環境を整えるのが大変。

今回私が作ったアプリケーションはこんな悩みを解決できるかもしれません。

どんなものを作ったのか?

今回、yahoo-inc/photo-background-generationモデルを使用し被写体を切り抜いてその背景を自由に生成・変更できるようなアプリケーションを作成しました。

ユーザーが画像をアップロードした際に、その画像のメインとなる被写体を切り抜いて、

  • 背景画像が用意されている場合には、それと合成する
  • 背景画像が用意されていない場合には、プロンプトに基づいた背景を生成して合成する

仕様になっています。(カッコつけてProdDiffuserという名前を付けました。)

https://github.com/hrt-ykym/ProdDiffuser

動いているところを見てみましょう

例えば、被写体として以下のようなカメラの画像があるとします。

この被写体に対して、背景画像が用意されている(左図)場合、カメラ部分だけを切り抜いて、指定された背景と合成します(右図)。

次に、背景画像がなく、AIによって生成したい場合について、例えば「on the beach」というプロンプトを入れたとします。すると以下のような画像が生成されます。このようにAIが生成した背景画像を、被写体画像と合成することで、テーマに合った画像が簡単に作れる仕様になっています。

実装のポイント

背景生成に使用した yahoo-inc/photo-background-generation

背景生成には、Hugging Faceで提供されているyahoo-inc/photo-background-generationモデルを使用しました。このモデルは、背景生成に特化したDiffusion Modelであり、被写体と背景の境界部分が滑らかに融合するため特徴を持っています。

背景生成のみに焦点を当てたDiffusion Model

  • 通常のDiffusion Modelは、画像全体を生成対象とします。しかし、このモデルは背景部分に特化した生成を行い、被写体を破壊しないという独自の設計思想を持っています。
  • 背景領域のみに制約をかけることで既存の生成モデルでみられる、被写体と背景の区別が曖昧になり被写体と合成したときの不自然になってしまう問題を解決しています。

ControlNetによる領域制御

ControlNetは、条件画像(ここでは被写体の形状や配置を反映したマスク)を基に生成領域をピクセル単位で指定できるのが特徴です。

  • 他の生成モデルでは背景を制御する手段が限られていますが、本モデルはControlNet技術を採用し、被写体を除外した領域(背景)にのみ生成を限定します。これにより、背景部分をピクセル単位で精密に制御可能です。
  • 被写体の形状や配置に基づくマスクを入力することで、生成対象を意図的に限定できるため、より正確で自然な背景を作成できます。

テキストプロンプトとマスクのハイブリッド活用

  • 一般的な生成モデルは、テキストプロンプトだけ、または画像の構造だけを条件として利用します。しかし、本モデルテキストプロンプトとマスク画像を組み合わせることで、背景に対して物理的な制約を同時に満たす画像を生成することができます。つまり、物理的にありえない画像は生成しないということです。
  • 例えば、「on the beach」というプロンプトと被写体の形状マスクを組み合わせることで、背景全体に砂浜の風景を適用しつつ、被写体周囲の細かいエッジ処理を自然に行います。影などが生成されるのもこの影響と考えられます。

被写体の切り抜き

製品画像から被写体を切り抜くには、transparent-backgroundライブラリを使用しています。このライブラリは画像を解析して背景を透明化するシンプルなインターフェースを提供します。

from transparent_background import Remover
from PIL import Image

def process_foreground(product_image_path):
    remover = Remover()
    product_image = Image.open(product_image_path).convert("RGB")
    return remover.process(product_image).convert("RGBA")

これにより、被写体部分のみを切り抜いた画像が得られます。この結果はRGBA形式の画像として保存され、次の合成処理で透明部分を考慮した操作が可能になります。

工夫した点

今回、yahoo-inc/photo-background-generationモデルをそのまま使用するだけでなく、実用性を高めるためにいくつかの工夫を施しています。これにより、ユーザーがより簡単かつ直感的に利用できるようになっています。

背景生成と用意された背景画像の両対応

公式モデルでは背景生成が主なユースケースですが、本アプリケーションでは、すでに用意された背景画像を使用できるオプションを追加しました。

def combine_foreground_with_background_centered(
    foreground,
    background_image_path,
    target_size=(512, 512),
    scale=1.0,
    position=None
):
    background_image = Image.open(background_image_path).convert("RGBA")
    # 背景画像をリサイズしてアスペクト比を維持
    background_image = resize_with_aspect_ratio_and_padding(background_image, target_size)
    
    # 被写体画像をスケール調整
    new_width = int(foreground.width * scale)
    new_height = int(foreground.height * scale)
    foreground = foreground.resize((new_width, new_height), Image.Resampling.LANCZOS)

    # 被写体画像を中央に配置
    if position is None:
        x = (background_image.width - foreground.width) // 2
        y = (background_image.height - foreground.height) // 2
    else:
        x, y = position

    # 背景と被写体を合成
    combined_image = background_image.copy()
    combined_image.paste(foreground, (x, y), foreground)
    return combined_image

パラメータ

背景生成において、ユーザー自身でのカスタマイズ性を保つために、関数の引数をもたせました。しかし、すべてユーザーに設定させるのは非効率であるため、デフォルト値も一般的なユースケースに合わせて設定しました。

result_image = generate_background_with_prompt_and_mask_or_combine(
    product_image_path="assets/product_image.jpg",
    prompt="on the beach",  # 背景生成用プロンプト
    background_image_path=None,  # 背景画像がある場合はパスを指定
    seed=13, # シード値
    target_size=(512, 512),
    scale=0.3,  # 被写体スケール
    position=None,  # 被写体の配置
    num_inference_steps=20,  # 推論ステップ
    controlnet_conditioning_scale=1.0  # ControlNetの条件付け
)

被写体マスクの自動生成とエッジ融合の工夫

被写体の輪郭を正確に保持するため、被写体のアルファチャンネルを反転してマスクを生成することで、背景生成が不要な領域を除外し、自然な合成が可能です。

from PIL import ImageOps

def generate_mask(image):
    # アルファチャンネルを反転し、背景領域を明示
    return ImageOps.invert(image.split()[-1])  # 最後のチャンネルを反転

被写体切り抜きを調整したい場合

デフォルトの設定でも十分な被写体切り抜きが可能ですが、もしどこからどこまでが被写体なのかを判別するかをチューニングしたい場合には、以下のように背景と被写体の境界を判定するしきい値を動的に変更することで、より柔軟な抽出が可能であることを確認しています。

def tune_mask(mask_image, threshold=0.5):
    mask_array = np.array(mask_image)
    # しきい値処理
    adjusted_mask = (mask_array > threshold * 255).astype(np.uint8) * 255
    return Image.fromarray(adjusted_mask)

この処理をtransparent_backgroundのRemoverクラスの後処理としてこの関数を組み込むことで、ユーザーが簡単にしきい値を設定できるようになります。例えば、被写体が背景に溶け込みやすい場合は、しきい値を下げるなどして対応が可能となります。

GPUとCPU環境での柔軟な動作

だれもがGPU (CUDA)を積んだPCを持っているとは限らないため、モデルの高性能を活かすため、NVIDIA GPUがある場合は自動で切り替え、ない場合はCPUで動作するように設計しました。

device = "cuda" if torch.cuda.is_available() else "cpu"
pipeline = DiffusionPipeline.from_pretrained(model_id, custom_pipeline=model_id)
pipeline = pipeline.to(device)  # 使用可能なデバイスに移行

なお、もしCUDAを用いたGPUの設定に困っている場合は別途記事を書いたのでこちらを参考にしてください。

類似ソフトウェアとの違い

被写体切り抜きと背景生成を組み合わせたアプリケーションは、近年さまざまなプラットフォームやツールで提供されていますが、今回のアプリケーションは以下のような特徴を持っています。

  • ControlNet技術を活用して、背景生成領域をピクセル単位で制御するため、被写体と背景の統合精度が非常に高い。
  • 被写体と背景の合成を行った際に境界が不自然になりがちだが、自然なエッジ融合を可能にしている。
  • 背景をすでに用意している場合としていない場合の柔軟な対応が可能にしている。
  • 背景をすでに用意している場合としていない場合の柔軟な対応が可能にしている。
    GPU/CPUのいずれの環境でも動作可能で、オンプレミスやクラウド上での運用がすぐに可能にしている。

まとめ

今回の記事では、画像生成AIを組み合わせた被写体の切り抜きと背景生成・変更を簡単に行えるアプリケーションを作った時の記録を紹介しました。SNSやフリマアプリ、さらにはECサイトの商品画像など、幅広いシーンで活用されれば嬉しいなと思います。

今後やりたいこと

ここまでお読みいただきありがとうございます!最後に今後やってみたいことを書いておきます。ぜひ他にも「こんな機能があったらいいな」「こうしたらもっと使いやすくなる」などのアイデアがあれば、ぜひ教えてください!GitHubのリポジトリやSNSを通じてコメントをいただけると嬉しいです。

日本語プロンプト

今回、yahoo-inc/photo-background-generationモデルを使用した都合上、背景生成の際に入力するプロンプトは英語のみとなっています。これを日本語にも対応させることがもし実際の場面で使用する場合には必要になるかと思います。手法としては、実現方法としては以下を検討中です。

  • ユーザーが入力した日本語のプロンプトを自動で英語に翻訳し、それを生成モデルに渡す仕組み。Google TranslateやDeepL APIを活用する対応
  • 日本語プロンプトを直接解釈できる生成モデルを用いたり、本モデルのアルゴリズムを用いて日本語で再学習したりする日本語対応モデルによる対応

複数画像のバッチ処理

現在は一枚ずつ画像をアップロードして加工する形をとっていますが、複数の画像を一括で処理できる機能を実装すれば、商品画像やSNS投稿用画像の一括加工といった場面での活用が期待されます。

ブログの著者欄

横山 遥乙

GMOペパボ株式会社

GMOペパボ株式会社/ホスティング事業部ロリポップfor gamersチーム/2024年新卒入社

採用情報

関連記事

KEYWORD

採用情報

SNS FOLLOW

GMOインターネットグループのSNSをフォローして最新情報をチェック