LESSON 30分

ストーリー

田中VPoE
画像認識と画像生成を学んだが、実務ではゼロから画像を作るよりも、既存の画像を「編集」することの方が多い
あなた
確かに。商品画像の背景を変えたり、不要なオブジェクトを消したりする作業は日常的にありますよね
田中VPoE
そうだ。インペインティング、背景除去、スタイル変換など、AI画像編集の技術を覚えておけば、デザイナーに依頼していた作業の多くを自動化できる
あなた
Photoshopの作業をAIで自動化するイメージですね
田中VPoE
その通り。APIベースで大量の画像を一括処理できるのがポイントだ

インペインティング(部分修復)

概要

画像の一部をマスクで指定し、その領域を自然に修復・置換する技術です。

元画像 + マスク画像 → [インペインティングモデル] → 修復された画像

用途

ユースケース説明
不要オブジェクト除去商品画像から影やゴミを除去
商品置換棚の商品を別の商品に差し替え
テキスト除去画像内のウォーターマークや文字を除去
欠損補完スキャン文書の破れや汚れを補完

実装例

from openai import OpenAI
from PIL import Image
import io

client = OpenAI()

def inpaint_image(
    image_path: str,
    mask_path: str,
    prompt: str
) -> str:
    """DALL-E 2でインペインティング"""
    response = client.images.edit(
        image=open(image_path, "rb"),
        mask=open(mask_path, "rb"),
        prompt=prompt,
        n=1,
        size="1024x1024"
    )
    return response.data[0].url


# Stable Diffusion APIでのインペインティング
import requests
import base64

def inpaint_with_sd(
    image_path: str,
    mask_path: str,
    prompt: str,
    denoising_strength: float = 0.75
) -> bytes:
    """Stable Diffusionでインペインティング"""
    with open(image_path, "rb") as f:
        image_b64 = base64.b64encode(f.read()).decode()
    with open(mask_path, "rb") as f:
        mask_b64 = base64.b64encode(f.read()).decode()

    payload = {
        "init_images": [image_b64],
        "mask": mask_b64,
        "prompt": prompt,
        "negative_prompt": "low quality, blurry",
        "denoising_strength": denoising_strength,
        "steps": 30,
        "cfg_scale": 7.0
    }

    response = requests.post(
        "http://localhost:7860/sdapi/v1/img2img",
        json=payload
    )
    return base64.b64decode(response.json()["images"][0])

背景除去

主要なAPIとツール

ツール特徴コスト
remove.bg API高精度、髪の毛も対応有料
rembg(OSS)ローカル実行、無料無料
Segment Anything(SAM)Meta、汎用セグメンテーション無料
PhotoRoom API商品画像特化有料

rembgを使った背景除去

from rembg import remove
from PIL import Image
import io

def remove_background(image_path: str, output_path: str) -> str:
    """画像の背景を除去"""
    with open(image_path, "rb") as f:
        input_data = f.read()

    output_data = remove(input_data)

    img = Image.open(io.BytesIO(output_data))
    img.save(output_path, format="PNG")
    return output_path


def replace_background(
    image_path: str,
    background_color: tuple = (255, 255, 255),
    output_path: str = "output.jpg"
) -> str:
    """背景を除去し、指定色の背景に置換"""
    with open(image_path, "rb") as f:
        input_data = f.read()

    # 背景除去(透過PNGを取得)
    output_data = remove(input_data)
    foreground = Image.open(io.BytesIO(output_data)).convert("RGBA")

    # 新しい背景を作成
    background = Image.new("RGBA", foreground.size, background_color + (255,))
    composite = Image.alpha_composite(background, foreground)
    composite = composite.convert("RGB")
    composite.save(output_path, format="JPEG", quality=95)
    return output_path

バッチ処理パイプライン

import os
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor

def batch_process_product_images(
    input_dir: str,
    output_dir: str,
    background_color: tuple = (255, 255, 255),
    max_workers: int = 4
) -> dict:
    """商品画像の一括背景除去"""
    input_path = Path(input_dir)
    output_path = Path(output_dir)
    output_path.mkdir(parents=True, exist_ok=True)

    image_files = list(input_path.glob("*.jpg")) + list(input_path.glob("*.png"))
    results = {"success": 0, "failed": 0, "errors": []}

    def process_single(img_file):
        try:
            out_file = output_path / f"{img_file.stem}_nobg.png"
            replace_background(str(img_file), background_color, str(out_file))
            return True
        except Exception as e:
            return str(e)

    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = {executor.submit(process_single, f): f for f in image_files}
        for future in futures:
            result = future.result()
            if result is True:
                results["success"] += 1
            else:
                results["failed"] += 1
                results["errors"].append(result)

    return results

スタイル変換

用途

変換タイプ説明業務例
色調変換カラーパレットの変更ブランドカラーへの統一
アート変換写真をイラスト風にSNS用素材
高解像度化低解像度画像のアップスケール古い商品画像の改善
フォーマット統一サイズ・アスペクト比の統一EC出品画像の一括調整

img2imgによるスタイル変換

def style_transfer(
    image_path: str,
    style_prompt: str,
    denoising_strength: float = 0.5
) -> bytes:
    """画像のスタイルを変換"""
    with open(image_path, "rb") as f:
        image_b64 = base64.b64encode(f.read()).decode()

    payload = {
        "init_images": [image_b64],
        "prompt": style_prompt,
        "denoising_strength": denoising_strength,  # 0.0〜1.0(高いほど変化大)
        "steps": 30,
        "cfg_scale": 7.0
    }

    response = requests.post(
        "http://localhost:7860/sdapi/v1/img2img",
        json=payload
    )
    return base64.b64decode(response.json()["images"][0])

超解像(アップスケール)

# Real-ESRGANによる超解像
from realesrgan import RealESRGANer
import cv2

def upscale_image(image_path: str, scale: int = 4) -> str:
    """画像を高解像度化"""
    upscaler = RealESRGANer(
        scale=scale,
        model_path="RealESRGAN_x4plus.pth"
    )

    img = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
    output, _ = upscaler.enhance(img, outscale=scale)

    output_path = image_path.rsplit(".", 1)[0] + f"_x{scale}.png"
    cv2.imwrite(output_path, output)
    return output_path

画像編集の業務ワークフロー

商品画像の一括処理パイプライン

原画像取り込み


[品質チェック] → 不合格 → 再撮影依頼
    │ 合格

[背景除去] → 白背景に統一


[リサイズ・クロップ] → ECサイト規格に統一


[色調補正] → ブランドカラーに調整


[バリエーション生成] → 複数背景パターン


[最終品質チェック] → 人間レビュー


ECサイトにアップロード

まとめ

技術概要主なツール
インペインティング画像の一部を修復・置換DALL-E 2, Stable Diffusion
背景除去被写体を切り抜きrembg, remove.bg, SAM
スタイル変換色調やスタイルの変更img2img, ControlNet
超解像低解像度画像の高品質化Real-ESRGAN

チェックリスト

  • インペインティングの仕組みとAPIを使った実装を理解した
  • 背景除去のツール選定とバッチ処理パイプラインを把握した
  • スタイル変換と超解像の業務活用法を説明できる
  • 商品画像の一括処理ワークフローを設計できる

推定所要時間: 30分