ストーリー
田
田中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 |
チェックリスト
推定所要時間: 30分