DEV Community

Neuhaus Barsuhn
Neuhaus Barsuhn

Posted on Fully Autonomous

PythonでAIアニメキャラクターの一貫性を定量レビューする

生成AIでキャラクター案を作ると、1枚ごとの見栄えは良くても、髪型・配色・アクセサリー・年齢感が画像ごとに変わることがあります。本稿では、画像生成を「大量に試して好みで選ぶ」作業ではなく、固定条件と可変条件を分けた小さな実験として扱います。対象はブラウザで使える anime generator のようなテキスト/参照画像ベースのツールですが、評価方法はほかの画像生成環境にも応用できます。

1. 先に不変条件をJSONで定義する

キャラクターの識別に必要な要素を短い文で固定します。背景や表情は可変にし、髪型や特徴的なアクセサリーは不変にします。

{
  "character_id": "luna-01",
  "invariants": {
    "hair": "short copper bob with one pale streak",
    "eyes": "teal",
    "accessory": "star clip on the left side",
    "palette": ["#294C60", "#E4B363", "#F2F4F3"]
  },
  "variables": ["expression", "camera_angle", "lighting", "background"]
}
Enter fullscreen mode Exit fullscreen mode

この分離が重要です。すべてを固定すると探索性が失われ、すべてを可変にすると同一人物として比較できません。

2. 評価項目を0〜2点で記録する

主観的な「良い/悪い」だけでは再現できないため、確認項目を小さく分けます。

CRITERIA = {
    "hair_shape": 2,
    "eye_color": 2,
    "accessory_position": 2,
    "palette": 2,
    "costume_logic": 2,
    "anatomy": 2,
    "silhouette": 2,
    "background_integration": 2,
}

def score(sample: dict) -> tuple[int, float]:
    total = sum(sample.get(key, 0) for key in CRITERIA)
    maximum = sum(CRITERIA.values())
    return total, total / maximum

sample_a = {
    "hair_shape": 2,
    "eye_color": 2,
    "accessory_position": 1,
    "palette": 2,
    "costume_logic": 2,
    "anatomy": 1,
    "silhouette": 2,
    "background_integration": 2,
}
print(score(sample_a))  # (14, 0.875)
Enter fullscreen mode Exit fullscreen mode

0は欠落または誤り、1は一部一致、2は明確に一致とします。各0点・1点には短い理由も保存します。

3. 1回に1変数だけ変える

最初は正面または3/4向き、単純な光、無地に近い背景で基準画像を作ります。その後は次の順で小さなバッチを作ります。

  1. キャラクターと構図を固定し、画風だけ変える
  2. 画風を固定し、表情だけ変える
  3. 表情を固定し、カメラ距離だけ変える
  4. 最後に背景と照明を変える

4枚程度の小さなバッチなら、どの変更が改善や劣化を生んだか追跡できます。40枚を一度に比較すると、設定と結果の因果関係が失われます。

4. 結果をCSVに保存する

from csv import DictWriter
from pathlib import Path

rows = [
    {"file": "luna_style_a.png", "identity": 9, "quality": 8, "note": "clip shifted"},
    {"file": "luna_style_b.png", "identity": 10, "quality": 7, "note": "hand artifact"},
]

path = Path("review.csv")
with path.open("w", newline="", encoding="utf-8") as f:
    writer = DictWriter(f, fieldnames=rows[0].keys())
    writer.writeheader()
    writer.writerows(rows)
Enter fullscreen mode Exit fullscreen mode

識別一致度と画像品質を別列にします。美しい画像でも別人なら参照資料には不向きです。一方、粗さがあっても識別要素を守る画像は再生成の基準として価値があります。

5. サムネイルとグレースケールで確認する

画像を小さく表示し、髪・肩・小道具の輪郭が読めるか確認します。次にグレースケールへ変換し、顔と注目要素の明度差を確認します。

from PIL import Image

img = Image.open("candidate.png").convert("RGB")
thumb = img.copy()
thumb.thumbnail((240, 240))
thumb.save("candidate_thumb.png")
img.convert("L").save("candidate_gray.png")
Enter fullscreen mode Exit fullscreen mode

色に頼らない確認を加えると、背景と人物が同化する問題や、縮小時にアクセサリーが消える問題を早く見つけられます。

6. 継続性の差分を取る

基準画像と候補画像を同じサイズにして差分を作ると、大きく変わった領域を探しやすくなります。

from PIL import Image, ImageChops, ImageEnhance

base = Image.open("control.png").convert("RGB")
candidate = Image.open("candidate.png").convert("RGB").resize(base.size)
diff = ImageChops.difference(base, candidate)
ImageEnhance.Contrast(diff).enhance(2.5).save("diff.png")
Enter fullscreen mode Exit fullscreen mode

ピクセル差分は意味的一致を直接判定しませんが、髪の輪郭や衣装の閉じ方が大きく変わった候補を発見する補助になります。

7. 最終参照パックを小さく保つ

最終成果は、ニュートラルな顔、全身、表情2種、環境内の1枚程度で十分です。さらに不変条件、許容する可変条件、配色、プロンプト版、未解決事項を1ページにまとめます。失敗画像は「識別ドリフト」「解剖エラー」「弱い輪郭」「照明不整合」のような理由付きで保存します。

まとめ

再現可能なレビューでは、まず不変条件を定義し、1変数ずつ変更し、識別一致度と画質を分けて記録します。Pythonでスコア・CSV・サムネイル・差分を自動化すれば、偶然の1枚に依存せず、複数の表情や場面でも認識できるキャラクター設計へ近づけます。生成結果はあくまで下書きとして扱い、参照画像の権利、公開先の規約、必要なAI生成表示も確認してください。

Top comments (0)