既存の写植要素DR(セリフ生成・顔回避配置・フキダシ・SFX・後写植WF)を1本に束ね、「何を順にどう処理すれば100点か」の全体図に徹した統合設計図。各段の入出力・ツール・擬似コード・閾値まで実装直結で記述する。
現行エンジン(_remote_compose_v2+合体マスク+ベタフラ+SFX-FX)は「組版の見た目」は作れるが、①セリフが固定句で文脈とズレる ②顔/見せ場にかぶる ③可読性の自動検証がないの3点で素人の壁に当たっている。本パイプラインはこの3点を VLM解析+LLM生成+自動配置+品質ゲートの4ブロックで機械的に埋める。59→100は「美麗化」ではなく「ズレ・かぶり・読めなさの自動排除」で達成する。
同人/AIエロ漫画制作の最大ボトルネックは「文字を画像に焼き込んだ瞬間に崩壊する」こと。SDXL/Pony/Illustrious系は日本語テキスト描画がほぼ破綻し、焼込SFXは可読不能・修正不能になる[6]。そこで「絵は文字なしで生成し、文字は後から写植で乗せる」後写植ピボットが必然になる。
※本DRは「市場で売る製品」ではなくCC3の内製ライン設計。価値は「写植工数の削減と品質上限の引き上げ」に換算する(§06)。
| # | 手法/ツール | 強み | 弱み | 本パイプラインの差別化 |
|---|---|---|---|---|
| 1 | Blambot AutoLetter | 英語レタリングの定番・フォント最適化 | CJK縦書き/感情連動/顔回避なし[3] | 合体マスク縦書き+顔被覆率検証を追加 |
| 2 | Clip Studio Paint 自動フキダシ | 商用完成度・手動の効率化 | セリフ生成も顔被覆検証も無し | VLM→LLMでセリフ自体を生成し全自動化 |
| 3 | ComicStudio系 Smart Balloon | テキスト長→フキダシ径の推定 | 配置はヒューリスティック止まり[3] | distanceTransform/最大空矩形で最適配置 |
| 4 | arXiv 2101.11111 emotion-driven balloon | 感情→フキダシ形状・読み順最適化[4] | 動画字幕入力前提・CJK未対応・実装非公開 | 静止画+VLM grounding で即時実装版に |
| 5 | Deep CNN balloon segmentation | 既存フキダシの検出/分割に強い[5] | 「検出」専用で「生成・配置」はしない | 逆問題(空白から配置)を解く |
| 6 | Florence-2 単体 | 無料MIT・caption/OD/grounding/OCR[1] | セリフ生成・配置まで到達しない | 解析専任に位置付け後段と結合 |
| 7 | Qwen2.5-VL 単体 | 空間localization/レイアウト読解が強い[8] | 創作セリフ生成は不得手 | 解析クロスチェック役に限定 |
| 8 | manga-ocr 単体 | 多行フキダシOCRの精度が高い[2] | 生成・配置はできない | 可読性ゲート専用に転用 |
| 9 | 商用AIレタリングSaaS | UIが整備・即利用可 | R18/CJK縦書き対応が弱く外部送信 | 完全ローカル・R18外部送信ゼロ |
| 10 | 完全手動(Clip Studio手作業) | 品質上限が最も高い | 1ページ数時間・量産不能 | 手動の上限品質に自動で漸近させる |
差別化の核:個々の要素(解析・生成・配置・検証)は既存物の組合せだが、「文字なし絵→完成ページ」を1本のpass/fail付きパイプラインで貫通させ、CJK縦書き×感情×顔回避×可読自動検証を全て満たす内製ラインはここにしかない。
| 段 | 入力 | 処理 | ツール | 出力 |
|---|---|---|---|---|
| ① 入力 | 文字なしPNG/JPG (推奨 2048×3072) | パネル矩形検出・正規化 | OpenCV(枠線検出) | image, panel_rects[] |
| ② シーン解析 | image, panels | caption/物体検出/grounding/空間QA→構造化 | Florence-2[1] + Qwen2.5-VL[8] + MediaPipe顔[9] | scene.json |
| ③ セリフ生成 | scene.json+人格 | 品位ガード判定→文脈一致セリフ/SFX語をLLM生成 | grok_router(grok-4.3)[15] | script.json |
| ④ フキダシ作画 | script行・形状指定 | 丸+しっぽを合体マスク(引き算で輪郭化) | PIL/numpy(合体マスク[14]) | balloon mask/shape |
| ⑤ 配置・流し込み | script,balloon,masks | 禁止域→空き矩形探索→読み順→級数調整→縦書き組版 | OpenCV saliency[11]+HarfBuzz/Pango[12] | layout.json+描画 |
| ⑥ 描き文字SFX | scene感情/動作 | SFX語彙→フォント描画→閾値/筆圧/縁取りで手描き化 | PIL後処理レイヤー[13] | SFXレイヤー |
| ⑦ 効果線・仕上げ | 感情ピーク/移動量 | 集中線/スピード線/ベタフラを条件分岐で付与 | numpy放射ブラー | 合成済ページ |
| ⑧ 品質ゲート | 完成画像+script+masks | OCR可読/被覆IoU/4AIペアワイズ→pass/fail→リカバリ⟳ | manga-ocr[2]+VLM judge[10] | 合否+failログ |
scene.json # ②の出力 — 段③以降の唯一の真実
{ "panels":[ {"id":0,"bbox":[x,y,w,h],
"characters":[{"id":1,"gender":"female","action":"standing",
"emotion":"smile","mouth_open":true,"face_bbox":[..],"mouth":[mx,my]}],
"stage":1, # s1着衣 〜 s5絶頂
"speaker_count":1,
"is_sex_scene":false, # 品位ガードの基準
"confidence":0.87 } ] }
script.json # ③の出力
{ "lines":[ {"panel_id":0,"speaker":1,"text":"おかえりなさい",
"emotion":"smile","type":"speech", # speech / monologue / moan / sfx
"shape":"round_tail","priority":"normal"} ] }
layout.json # ⑤の出力(再現・再編集の保存形式)
{ "balloons":[ {"line_ref":0,"bbox":[x,y,w,h],"shape":"round_tail",
"font_size":16,"line_dir":"ttb","tail_angle":214} ],
"sfx":[...], "fx":[{"type":"betaflash","panel_id":3}] }
設計の肝:全段が scene.json / script.json / layout.json を介して疎結合。VLMやLLMを差し替えても下流は無改修。layout.jsonを保存すれば「再編集・多言語差し替え・別オチ」が絵を触らず可能になる。
素人写植が破綻する最大要因は「絵を見ずに固定句を貼る」こと。立ち絵に喘ぎが出る、出迎えなのに絶頂セリフ、という事故[14]はここで生む。解決は VLMで絵を構造化 → LLMが構造に従って生成の2段。
<MORE_DETAILED_CAPTION> で全体状況、<OD> で人物・物体のbbox、<REGION_TO_DESCRIPTION> で各人物の動作を取得[1]。def guard(line, scene_panel):
# 喘ぎ/絶頂は「実際に絡んでいるコマ」だけ [14]
if line.type == "moan" and not scene_panel["is_sex_scene"]:
return demote_to_safe(line) # 出迎え/奉仕/照れ/誘いの上品なセリフへ
# 男セリフは男が同コマに居る絡みコマだけ [14]
sp = get_char(scene_panel, line.speaker)
if sp["gender"] == "male" and not has_male_in_intercourse(scene_panel):
return None # 画面外の声もNG。男セリフを発生させない
return line
system = "あなたは {char.name}({char.persona}・口調:{char.tone})として話す。"
user = f"""このコマの状況: {scene_panel を自然文化}
ステージ: s{stage}(s1着衣〜s5絶頂)/ 感情: {emotion} / 同コマ人数: {count}
制約:
- 状況・感情・ステージに一致する 1〜2 文だけ。固定句・テンプレ禁止。
- 立ち絵/日常コマなら品のあるセリフ+軽い感情。喘ぎ語は出すな。
- 出力は script.json の lines 形式。"""
text, _ = gr.ask(user, kind="quick_check", system=system) # 1行=約¥0.1
人格プロフィール(3サイズ/性格/好きな言葉/口調)はCC1の偏愛プロフィール資産をそのまま注入[15]。これで「キャラが本当に喋っている」セリフになる。
配置は本パイプラインの心臓。顔/見せ場をavoid → 空きスペース探索 → 読み順 → はみ出し回避を1本のgreedyループに統合する。
def build_forbidden_mask(image, faces, panels):
H, W = image.shape[:2]
forbidden = np.zeros((H, W), np.uint8)
# 顔+口(口は最も重い禁止域)
for f in faces:
cv2.rectangle(forbidden, (f.x1,f.y1), (f.x2,f.y2), 255, -1)
cv2.circle(forbidden, tuple(f.mouth), 28, 255, -1)
# 見せ場 = OpenCV static saliency [11]
sal = cv2.saliency.StaticSaliencySpectralResidual_create()
ok, sal_map = sal.computeSaliency(image) # 0..1
forbidden = cv2.bitwise_or(forbidden, ((sal_map>0.6)*255).astype(np.uint8))
# パネル境界マージン
for p in panels:
cv2.rectangle(forbidden, p, 255, thickness=8)
return forbidden, sal_map
dt = cv2.distanceTransform(255-forbidden, cv2.DIST_L2, 5) の最大値座標を中心に。禁止域から最も離れた安全地帯に置ける。def place_all_balloons(image, script_lines, panels, scene):
faces = mediapipe_blazeface(image) # bbox + mouth [9]
forbidden, sal_map = build_forbidden_mask(image, faces, panels)
placed = []
# 読み順: 話者顔位置でコマ内を 右上→左下(Z/逆N)にソート
order = sorted(script_lines,
key=lambda L: reading_key(speaker_face(L, faces, scene)))
for i, line in enumerate(order):
line.speaker_index = i
fs, w, h = estimate_balloon_size(line.text, base=16) # 級数→外接矩形
# --- 空き探索 ---
if line.priority == "high":
cx, cy = argmax_xy(cv2.distanceTransform(255-forbidden, cv2.DIST_L2,5))
rect = rect_around(cx, cy, w, h)
else:
rect = find_maximal_empty_rect(forbidden, w, h)
# --- しっぽ角度 = フキダシ中心→話者の口 ---
sp = speaker_face(line, faces, scene)
tail_angle = atan2(sp.mouth.y - rect.cy, sp.mouth.x - rect.cx)
# --- はみ出し/重なり回避(級数3段縮小→フォールバック) ---
while (not inside_panel(rect, panels)) or overlaps(rect, placed):
fs -= 2 # 16→14→12→10
w, h = recompute_box(line.text, fs)
rect = find_maximal_empty_rect(forbidden, w, h)
if fs < 10:
rect = fallback_leader(rect, panels) # 枠外引出し+0.5ptリーダー線
break
draw_balloon(image, rect, line.text, fs, tail_angle, dir="ttb")
placed.append(rect)
forbidden = cv2.bitwise_or(forbidden, mask_of(rect)) # 逐次=次の禁止域に追加
return image, [r.to_layout() for r in placed]
| 段階 | 手段 | 閾値/規則 |
|---|---|---|
| 顔avoid | BlazeFace bbox+口circle r=28px | 口は最重量・絶対不可侵 |
| 見せ場avoid | static saliency > 0.6 を禁止域へ | grounding bboxは0.9倍縮小して使う |
| 読み順 | 右上→左下 Z/逆N、speaker_index付与 | コマ内優先→コマ間 |
| 級数縮小 | 16→14→12→10級 | 10級未満は枠外リーダー線へ |
| 重なり回避 | placed済bboxもforbiddenに追加(greedy) | balloon間IoU=0で配置 |
本パイプラインは「売る製品」ではなく内製ライン。価値は写植工数の削減と月産ページ数の増加・単価維持に出る。
| 項目 | 従来(手動) | 本パイプライン |
|---|---|---|
| 1ページ写植時間 | 約4.5時間 | 約12分(VLM+生成+配置+確認) |
| 月産ページ(写植律速) | 約8ページ | 約65ページ |
| 1ページ内製コスト | 人件費のみ | grok-4.3解析/生成 ¥0.28+ローカルVLM ¥0 |
| 単価(CG集/ページ換算) | 維持 | 維持(品質ゲートで下げない) |
前提:VLM/manga-ocr/MediaPipe/PIL/OpenCVは全てローカル(¥0)。課金はgrok-4.3のシーン要約+セリフ生成のみ(1ページ数回呼び=quick_check $0.001前後)。100点必達ページのみ品質ゲートでgrok-4.3フル採点を回す(1ページ ¥1未満)。「文脈一致セリフ+自動配置+自動検証」を機械が担い、人は最終確認だけに絞れるのが粗利源泉。
| リスク | 症状 | 緩和策 |
|---|---|---|
| VLM話者誤判定 | 別人のセリフが生成される | 口開き+視線+面積最大の3条件+2モデル一致でconfidence、低信頼は人間確認フラグ |
| セリフ文脈ズレ・固定句病 | 絵と無関係/テンプレ台詞 | scene.json必須注入+「固定句禁止」明示+品位ガード前処理(§05) |
| 配置はみ出し/重なり | 枠から飛び出す・被る | 級数3段縮小→枠外リーダー線フォールバック+placed逐次forbidden化 |
| OCR可読落ち | 写植文字が機械にも読めない | 事前にmanga-ocrシミュレーション→0.90未満なら級数/字間を上げて再描画 |
| 4AIスコアのブレ | 同じ絵で点数が揺れる | 絶対点を廃止しペアワイズのみ。順序入替で position bias を平均化[10] |
| VLM position bias | 左/先の選択肢に偏る | 5回×順序入替、勝率で判定(§品質ゲート) |
| 縦書きレンダ崩れ | Pillow ttbで字がずれる | HarfBuzz/Pangoで縦組シェイピング(§10落とし穴)[12] |
| 品位事故 | 立ち絵に喘ぎ/男声混入 | is_sex_scene / 男在席をロジック判定して生成前に遮断[14] |
| 週 | 実装タスク(関数名) | DoD(受け入れ基準) | MVPで切る/後回し |
|---|---|---|---|
| Week1 配置MVP | D1-3 build_forbidden_mask()(顔+saliency+枠margin)D4-5 place_all_balloons() 距離変換版+縦書き描画 | 単一コマで10行以内が顔/見せ場を避け重ならず配置される | 切る:SFX・効果線・多コマ |
| Week2 品質ゲート | D6-8 gate_ocr()(manga-ocr+Levenshtein 0.90)D9-10 pairwise_vlm_judge()(順序入替) | 50枚でpass率85%以上、failが正しくリカバリへ戻る | 後回し:4AI複数モデル化(まず1VLM) |
| Week3 SFX/効果線 | D11-14 sfx_layer()(フォント→閾値→筆圧→縁取り)D15 fx_concentration/betaflash() | 手描き化SFXが5パターン+絡みコマのみベタフラ発火 | 後回し:動くSFX(APNG) |
| Week4 解析統合 | D16-20 build_scene_json()(Florence-2+Qwen2.5-VL)D21-25 複数フキダシgreedy+リカバリループ D26-30 全体結合テスト+ゲート集約 | 文字なし1ページ投入→完成ページがpassで出力 | 後回し:縦読みwebtoon対応・WebUI |
最初に作るのは scene.json 生成器と配置器。セリフ生成と配置が繋がれば「中身の薄い完成ページ」が出る=検証ループが回り始める。SFX/効果線は見栄えなので後段でよい。
| 指標 | 撤退/見直しライン | 意味 |
|---|---|---|
| 写植スコア | 2週間連続で85未満 | 自動配置だけでは上限不足→手動ハイブリッドに切替検討 |
| OCR可読率 | 95%未達が常態化 | 縦書きレンダ/級数設計の根本見直し |
| 工数削減率 | 70%未満 | 「人間確認フラグ」が多すぎ=VLM精度不足。解析を上位モデルへ |
| 品質ゲート勝率 | 参照プロ作品にペアワイズ勝率35%未満が固定 | 配置/フキダシの質的限界。SFX/手描き化を強化 or 部分手動 |
| VLM話者誤判定率 | 20%超 | scene.json生成が信用できない=全工程の前提崩壊。話者推定を再設計 |
撤退=全廃ではなく「どこまで自動・どこから手動」の線引きを引き直すサイン。例えば配置だけ自動・セリフは人間、など段階的縮退を許容する。
| 地雷 | 現象 | 回避策 |
|---|---|---|
| Pillow ttbバグ | 日本語縦書きで字が左下にずれ重なる[12] | PIL直描きをやめHarfBuzz/Pango+Cairoで縦組シェイピング。縦書きはシェイピング方向をttbに変換してからシェイプ |
| 合体マスク継ぎ目衝突 | 丸としっぽの境界に線が出る/欠ける | 線を描かずシルエット→膨張→引き算で外周リングだけ輪郭化。引き算時1pxバッファ[14] |
| 焼込SFX混在 | 元絵に既に文字が焼込まれている | manga-ocrで焼込検出→inpaintでクリーン除去してから写植[7]。原則は文字なし生成を徹底 |
| VLM position bias | 採点が先/左の選択肢に偏る | 絶対点を捨てペアワイズ+順序入替×5回平均[10] |
| 固定句病 | 絵を見ずテンプレ台詞 | scene.json必須注入+品位ガード(§05) |
| 被覆率の罠 | 顔を避けたつもりが口にかぶる | 口landmarkを独立した重い禁止域に。IoUは「顔3%/見せ場8%」で別閾値 |
| greedy配置の枯渇 | 後半のフキダシで空きが尽きる | 大きい/重要セリフから先に配置。枯渇時は枠外リーダー線フォールバック |
| grok_router課金罠 | 旧slug指定でgrok-4.3価格にリダイレクト課金 | kindで指定(quick_check/dr_world_top等)。コストは自動ログ記録[15] |
VLMは「順位は付けられるが点数は付けられない」[10]。よって品質ゲートは絶対点を信用せず、客観測定2本(OCR・被覆)+ペアワイズ1本のANDで切る。
def quality_gate(final_image, script, masks, placed_balloons, anchors):
R = {}
# --- ゲート1: OCR可読率(写植した文字を再OCRして一致を見る) ---
tot = 0.0
for bb, line in zip(placed_balloons, script.lines):
crop = crop_region(final_image, bb)
ocr = manga_ocr(crop) # 多行OCR [2]
tot += levenshtein_ratio(normalize(ocr), normalize(line.text))
R["ocr"] = (tot/len(script.lines)) >= 0.90 # FAIL: 級数/字間↑で再描画
# --- ゲート2: 被覆率(顔/見せ場マスクとフキダシのIoU上限) ---
bmask = union_masks(placed_balloons)
face_ov = overlap_ratio(masks["face"], bmask) # 重なり面積/フキダシ面積
sal_ov = overlap_ratio(masks["saliency_high"], bmask)
R["face"] = face_ov < 0.03 # 顔にかぶったら即FAIL
R["saliency"] = sal_ov < 0.08 # 見せ場は8%でWARN→確認
# --- ゲート3: 4AIペアワイズ(自作 vs 参照プロ作品・rank/ルブリック) ---
wins = 0; N = 5
for k in range(N):
a, b = (final_image, anchors[k]) if k%2==0 else (anchors[k], final_image) # 順序入替
winner = vlm_pairwise(a, b, rubric=[
"可読性","フキダシ自然さ","顔/見せ場の被覆","SFX/効果線","全体プロ度"])
wins += (winner == "final")
R["vlm"] = (wins/N) >= 0.50 # 絶対点は使わない [10]
return all(R.values()), R
| ゲート | 測定 | 閾値 | FAIL時の自動リカバリ |
|---|---|---|---|
| ① OCR可読率 | 再OCR×Levenshtein正規化一致 | ≥ 0.90 | 級数↑/字間↑で再描画→再OCR |
| ② 顔被覆 | 顔マスク∩フキダシ 面積比 | < 3% | greedy再配置(別空き矩形へ) |
| ② 見せ場被覆 | saliency_high∩フキダシ | < 8%(超=WARN) | WARN→人間確認フラグ |
| ③ 4AI勝率 | vs参照プロ ペアワイズ×5順序入替 | ≥ 50% | 配置/フキダシ/SFXを再生成 |
集約:全ゲートANDでのみpass。FAIL項目ごとに対応リカバリへ戻し最大3周ループ、それでも落ちれば人間確認フラグを立てて停止(無限ループ・自己申告passを防ぐ)。参照プロ作品アンカー集(売れ筋teardown済み)を10〜20枚用意しておくのが前提[7]。
if sal_peak > 0.75 and stage >= 4: add_concentration_lines(panel) # 集中線(放射ブラー) if motion_vec(speaker) > 30: add_speed_lines(panel) # スピード線 if stage == 5 and is_sex_scene: add_betaflash(panel) # ベタフラ(放射状黒)[14]
<MORE_DETAILED_CAPTION>=全体状況、<OD>=人物bbox、Qwen2.5-VL空間QA=ステージ/性別/人数、MediaPipe=顔/口。3者を1つのscene.jsonにマージ。speaker = argmax( 0.5*口開き + 0.3*視線が前 + 0.2*コマ内面積 )。独白(monologue)は口閉じ+無しっぽ。本パイプラインは新規実装が主ではなく「散在する要素DRと現行エンジンの統合」。各要素を scene.json/script.json/layout.json という共通I/Fに接続するのが本DRの役目。
| 段 | 流用する既存資産 | 接続点 |
|---|---|---|
| ④⑤ フキダシ/配置 | CC3 _remote_compose_v2+合体マスク+ベタフラ+SFX-FX(既存DR:フキダシ透過PNG/顔回避配置/合体マスク方式[14]) | layout.jsonを入力に取る形へ薄くラップ |
| ③ セリフ生成 | CC1キャラ人格プロフィール(偏愛/口調/3サイズ)+grok_router[15] | system promptへ人格注入 |
| ② 解析 | 既存DR:文字なし後写植WF / 焼込除去実務[6][7] | scene.json生成器に集約 |
| ⑥⑦ SFX/効果線 | 既存DR:描き文字SFX手描き化/集中線素材/ベタフラFX[13] | PIL後処理レイヤーとして発火条件で呼ぶ |
| ⑧ 品質ゲート | 既存3AI/4AI評価ハーネス(grok/gemini/qwen)[15]、売れ筋teardownアンカー[7] | ペアワイズ判定に転用 |
統合の唯一のルール:各要素は scene/script/layout.json 以外で会話しない。これで「どのDRの成果も差し替え可能なモジュール」になり、パイプライン全体が壊れにくくなる。
※これらは「部品」DR。本DRはそれらを順序立てて1本のpass/fail付きラインに編成する「アーキDR」として差別化される(既存と重複せず統合視点)。
起草: grok-4.3(dr_world_top+dr_long)| 推定コスト: 約 ¥208($1.385=§下書き$0.705+深掘り$0.680)| 1次情報15ソース| 重複: 近接要素DR多数 → 統合アーキDRとして新規発行