togacat commited on
Commit
93dc2f1
·
verified ·
1 Parent(s): 4e51b58

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +92 -5
  2. static/index.html +36 -5
app.py CHANGED
@@ -99,6 +99,75 @@ def parse_no_logo(value) -> bool:
99
 
100
 
101
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  def add_logo_to_image(img: "Image.Image") -> "Image.Image":
103
  """
104
  生成画像の下部に "Generated by Mitsua Likes" のロゴテキストを描画して返す。
@@ -283,7 +352,8 @@ def generate_single_image(prompt, negative_prompt,
283
  width, height,
284
  steps, guidance_scale, guidance_rescale,
285
  seed,
286
- add_logo: bool = True):
 
287
  """
288
  通常の 1枚生成。latent はここでは保存しない(inpaint 用に選択されたときだけ再計算して保存する)。
289
  """
@@ -315,7 +385,10 @@ def generate_single_image(prompt, negative_prompt,
315
  pass
316
 
317
  img = ret.images[0]
318
- if add_logo:
 
 
 
319
  img = add_logo_to_image(img)
320
  filename = save_image_with_metadata(
321
  img, prompt, negative_prompt, steps, guidance_scale, guidance_rescale, seed
@@ -340,7 +413,8 @@ def generate_grid9(prompt, negative_prompt,
340
  width, height,
341
  steps, guidance_scale, guidance_rescale,
342
  seeds,
343
- add_logo: bool = True):
 
344
  """
345
  3x3 (9枚) をまとめて生成。ここでも latent は保存しない。
346
  """
@@ -369,7 +443,10 @@ def generate_grid9(prompt, negative_prompt,
369
  imgs = ret.images
370
 
371
  for idx, (seed, img) in enumerate(zip(seeds, imgs)):
372
- if add_logo:
 
 
 
373
  img_to_save = add_logo_to_image(img)
374
  else:
375
  img_to_save = img
@@ -523,6 +600,8 @@ def api_generate_one():
523
  seed = int(seed_param)
524
 
525
  no_logo = parse_no_logo(data.get("no_logo", False))
 
 
526
 
527
  try:
528
  info = generate_single_image(
@@ -535,6 +614,7 @@ def api_generate_one():
535
  guidance_rescale=guidance_rescale,
536
  seed=seed,
537
  add_logo=not no_logo,
 
538
  )
539
  return jsonify(info)
540
  except Exception as e:
@@ -565,6 +645,8 @@ def api_generate_grid9():
565
 
566
  no_logo = parse_no_logo(data.get("no_logo", False))
567
 
 
 
568
  try:
569
  images_info = generate_grid9(
570
  prompt=prompt,
@@ -576,6 +658,7 @@ def api_generate_grid9():
576
  guidance_rescale=guidance_rescale,
577
  seeds=seeds,
578
  add_logo=not no_logo,
 
579
  )
580
  return jsonify({"images": images_info})
581
  except Exception as e:
@@ -759,6 +842,7 @@ def api_inpaint_repaint():
759
  guidance_scale = float(data.get("guidance_scale", 6.0))
760
  guidance_rescale = float(data.get("guidance_rescale", 0.7))
761
  no_logo = parse_no_logo(data.get("no_logo", False))
 
762
  seed = int(data.get("seed", 0)) # seed はメタ情報用
763
  strength = float(data.get("strength", 0.5))
764
  mask_png = data.get("mask_png")
@@ -841,7 +925,10 @@ def api_inpaint_repaint():
841
 
842
  # 新しい画像&latent
843
  img = ret.images[0]
844
- if not no_logo:
 
 
 
845
  img = add_logo_to_image(img)
846
  new_latents = getattr(ret, "latents", None)
847
  if isinstance(new_latents, torch.Tensor) and new_latents.dim() == 4:
 
99
 
100
 
101
 
102
+
103
+
104
+
105
+ def parse_disable_character_detection(value) -> bool:
106
+ """JSON から渡ってくる disable_character_detection を安全に bool に変換。"""
107
+ # parse_no_logo と同じ解釈でOK(True/"true"/"1"/"yes"/"on" を True)
108
+ return parse_no_logo(value)
109
+
110
+
111
+ def _has_detected_public_fictional_characters(ret, idx: int = 0) -> bool:
112
+ """
113
+ Mitsua Likes pipeline の戻り値 ret から、公開キャラクター類似が検知されたか判定する。
114
+ ret.detected_public_fictional_characters は [ [str, ...], ... ] を想定。
115
+ """
116
+ try:
117
+ if not hasattr(ret, "detected_public_fictional_characters"):
118
+ return False
119
+ d = getattr(ret, "detected_public_fictional_characters", None)
120
+ if d is None:
121
+ return False
122
+ # list/tuple 想定
123
+ if not isinstance(d, (list, tuple)) or len(d) <= idx:
124
+ return False
125
+ one = d[idx]
126
+ if one is None:
127
+ return False
128
+ if isinstance(one, (list, tuple)) and len(one) > 0:
129
+ return True
130
+ # まれに文字列が入る場合も True 扱い
131
+ if isinstance(one, str) and one.strip():
132
+ return True
133
+ return False
134
+ except Exception:
135
+ return False
136
+
137
+
138
+ def make_character_detection_placeholder(size: Tuple[int, int],
139
+ text: str = "detected_public_fictional_characters") -> "Image.Image":
140
+ """真っ白な画像に指定テキストを描画した代替画像を返す。"""
141
+ if Image is None or ImageDraw is None:
142
+ raise RuntimeError("Pillow is not available; cannot create placeholder image.")
143
+ w, h = int(size[0]), int(size[1])
144
+ img = Image.new("RGB", (w, h), (255, 255, 255))
145
+ draw = ImageDraw.Draw(img)
146
+
147
+ font = None
148
+ if ImageFont is not None:
149
+ try:
150
+ # Pillow 標準の小さめフォント(環境依存で TTF が無いことがあるため)
151
+ font = ImageFont.load_default()
152
+ except Exception:
153
+ font = None
154
+
155
+ # 中央に配置
156
+ try:
157
+ bbox = draw.textbbox((0, 0), text, font=font)
158
+ tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
159
+ except Exception:
160
+ try:
161
+ tw, th = draw.textsize(text, font=font)
162
+ except Exception:
163
+ tw, th = (len(text) * 6, 12)
164
+
165
+ x = max(0, (w - tw) // 2)
166
+ y = max(0, (h - th) // 2)
167
+
168
+ # 白背景なので黒文字
169
+ draw.text((x, y), text, font=font, fill=(0, 0, 0))
170
+ return img
171
  def add_logo_to_image(img: "Image.Image") -> "Image.Image":
172
  """
173
  生成画像の下部に "Generated by Mitsua Likes" のロゴテキストを描画して返す。
 
352
  width, height,
353
  steps, guidance_scale, guidance_rescale,
354
  seed,
355
+ add_logo: bool = True,
356
+ enable_character_detection: bool = True):
357
  """
358
  通常の 1枚生成。latent はここでは保存しない(inpaint 用に選択されたときだけ再計算して保存する)。
359
  """
 
385
  pass
386
 
387
  img = ret.images[0]
388
+ detected = bool(enable_character_detection and _has_detected_public_fictional_characters(ret, 0))
389
+ if detected:
390
+ img = make_character_detection_placeholder(img.size, "detected_public_fictional_characters")
391
+ elif add_logo:
392
  img = add_logo_to_image(img)
393
  filename = save_image_with_metadata(
394
  img, prompt, negative_prompt, steps, guidance_scale, guidance_rescale, seed
 
413
  width, height,
414
  steps, guidance_scale, guidance_rescale,
415
  seeds,
416
+ add_logo: bool = True,
417
+ enable_character_detection: bool = True):
418
  """
419
  3x3 (9枚) をまとめて生成。ここでも latent は保存しない。
420
  """
 
443
  imgs = ret.images
444
 
445
  for idx, (seed, img) in enumerate(zip(seeds, imgs)):
446
+ detected = bool(enable_character_detection and _has_detected_public_fictional_characters(ret, idx))
447
+ if detected:
448
+ img_to_save = make_character_detection_placeholder(img.size, "detected_public_fictional_characters")
449
+ elif add_logo:
450
  img_to_save = add_logo_to_image(img)
451
  else:
452
  img_to_save = img
 
600
  seed = int(seed_param)
601
 
602
  no_logo = parse_no_logo(data.get("no_logo", False))
603
+ disable_character_detection = parse_disable_character_detection(data.get("disable_character_detection", False))
604
+
605
 
606
  try:
607
  info = generate_single_image(
 
614
  guidance_rescale=guidance_rescale,
615
  seed=seed,
616
  add_logo=not no_logo,
617
+ enable_character_detection=not disable_character_detection,
618
  )
619
  return jsonify(info)
620
  except Exception as e:
 
645
 
646
  no_logo = parse_no_logo(data.get("no_logo", False))
647
 
648
+ disable_character_detection = parse_disable_character_detection(data.get("disable_character_detection", False))
649
+
650
  try:
651
  images_info = generate_grid9(
652
  prompt=prompt,
 
658
  guidance_rescale=guidance_rescale,
659
  seeds=seeds,
660
  add_logo=not no_logo,
661
+ enable_character_detection=not disable_character_detection,
662
  )
663
  return jsonify({"images": images_info})
664
  except Exception as e:
 
842
  guidance_scale = float(data.get("guidance_scale", 6.0))
843
  guidance_rescale = float(data.get("guidance_rescale", 0.7))
844
  no_logo = parse_no_logo(data.get("no_logo", False))
845
+ disable_character_detection = parse_disable_character_detection(data.get("disable_character_detection", False))
846
  seed = int(data.get("seed", 0)) # seed はメタ情報用
847
  strength = float(data.get("strength", 0.5))
848
  mask_png = data.get("mask_png")
 
925
 
926
  # 新しい画像&latent
927
  img = ret.images[0]
928
+ detected = bool((not disable_character_detection) and _has_detected_public_fictional_characters(ret, 0))
929
+ if detected:
930
+ img = make_character_detection_placeholder(img.size, "detected_public_fictional_characters")
931
+ elif not no_logo:
932
  img = add_logo_to_image(img)
933
  new_latents = getattr(ret, "latents", None)
934
  if isinstance(new_latents, torch.Tensor) and new_latents.dim() == 4:
static/index.html CHANGED
@@ -217,6 +217,11 @@
217
  <input id="noLogo" type="checkbox" style="margin-right:4px;">
218
  <span>No logo(生成画像を共有する場合、作品にMitsua Likesのクレジット表示は必須)</span>
219
  </label>
 
 
 
 
 
220
  </div>
221
  </header>
222
 
@@ -252,6 +257,11 @@
252
  <input id="i2iStrength" type="number" value="100" min="0" max="100" step="5">
253
  </label>
254
  <button id="i2iBtn">i2i (全体)</button>
 
 
 
 
 
255
  </div>
256
  <div id="inpaintCanvasWrapper">
257
  <img id="inpaintImage" src="" alt="inpaint base">
@@ -278,6 +288,9 @@
278
  const guidanceRescaleEl = document.getElementById("guidanceRescale");
279
 
280
  const noLogoEl = document.getElementById("noLogo");
 
 
 
281
 
282
  const generateOneBtn = document.getElementById("generateOneBtn");
283
  const generateGridBtn = document.getElementById("generateGridBtn");
@@ -329,6 +342,19 @@
329
  let drawMode = "pen"; // "pen" or "eraser"
330
  let maskColor = "rgba(255,0,0,0.4)";
331
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  function setStatus(msg) {
333
  statusEl.textContent = msg;
334
  console.log(msg);
@@ -412,7 +438,8 @@
412
  steps: overrides.steps !== undefined ? overrides.steps : baseSteps,
413
  guidance_scale: overrides.guidance_scale !== undefined ? overrides.guidance_scale : baseGuidanceScale,
414
  guidance_rescale: overrides.guidance_rescale !== undefined ? overrides.guidance_rescale : baseGuidanceRescale,
415
- no_logo: noLogoEl && noLogoEl.checked
 
416
  };
417
 
418
  if (overrides.seed !== undefined && overrides.seed !== null && !Number.isNaN(overrides.seed)) {
@@ -529,7 +556,8 @@
529
  guidance_scale: baseGuidanceScale,
530
  guidance_rescale: baseGuidanceRescale,
531
  first_seed: useFirstSeed ? firstSeedVal : null,
532
- no_logo: noLogoEl && noLogoEl.checked
 
533
  };
534
 
535
  try {
@@ -649,7 +677,8 @@
649
  guidance_scale: baseGuidanceScale,
650
  guidance_rescale: baseGuidanceRescale,
651
  first_seed: (!usedFirstSeedTopLeft && useFirstSeed) ? firstSeedVal : null,
652
- no_logo: noLogoEl && noLogoEl.checked
 
653
  };
654
 
655
  setStatus("Auto (3x3 一括): 生成中...");
@@ -1056,7 +1085,8 @@
1056
  seed: parseInt(seedEl.value, 10) || baseInfo.seed,
1057
  strength: 0.5,
1058
  mask_png: maskDataUrl,
1059
- no_logo: noLogoEl && noLogoEl.checked
 
1060
  };
1061
 
1062
  setStatus("Repaint: inpaint 実行中...");
@@ -1124,7 +1154,8 @@
1124
  mask_png: maskDataUrl,
1125
  i2i_mode: true,
1126
  i2i_mask_pct: pct,
1127
- no_logo: noLogoEl && noLogoEl.checked
 
1128
  };
1129
 
1130
  setStatus(`i2i: 画像全体を ${pct}% で再描画中...`);
 
217
  <input id="noLogo" type="checkbox" style="margin-right:4px;">
218
  <span>No logo(生成画像を共有する場合、作品にMitsua Likesのクレジット表示は必須)</span>
219
  </label>
220
+ <label style="display:inline-flex;align-items:center;margin-top:4px;font-size:0.85rem;">
221
+ <input id="disableCharDetectNormal" type="checkbox" style="margin-right:4px;">
222
+ <span>キャラ検知モード無効化(無効の場合はパブリックキャラの再生成に注意)</span>
223
+ </label>
224
+
225
  </div>
226
  </header>
227
 
 
257
  <input id="i2iStrength" type="number" value="100" min="0" max="100" step="5">
258
  </label>
259
  <button id="i2iBtn">i2i (全体)</button>
260
+ <label style="display:inline-flex;align-items:center;font-size:0.85rem;">
261
+ <input id="disableCharDetectInpaint" type="checkbox" style="margin-right:4px;">
262
+ <span>キャラ検知モード無効化(無効の場合はパブリックキャラの再生成に注意)</span>
263
+ </label>
264
+
265
  </div>
266
  <div id="inpaintCanvasWrapper">
267
  <img id="inpaintImage" src="" alt="inpaint base">
 
288
  const guidanceRescaleEl = document.getElementById("guidanceRescale");
289
 
290
  const noLogoEl = document.getElementById("noLogo");
291
+ const disableCharDetectNormalEl = document.getElementById("disableCharDetectNormal");
292
+ const disableCharDetectInpaintEl = document.getElementById("disableCharDetectInpaint");
293
+
294
 
295
  const generateOneBtn = document.getElementById("generateOneBtn");
296
  const generateGridBtn = document.getElementById("generateGridBtn");
 
342
  let drawMode = "pen"; // "pen" or "eraser"
343
  let maskColor = "rgba(255,0,0,0.4)";
344
 
345
+ // キャラ検知モード無効化(通常/インペイントで状態を同期)
346
+ if (disableCharDetectNormalEl && disableCharDetectInpaintEl) {
347
+ // 初期同期
348
+ disableCharDetectInpaintEl.checked = disableCharDetectNormalEl.checked;
349
+
350
+ disableCharDetectNormalEl.addEventListener("change", () => {
351
+ disableCharDetectInpaintEl.checked = disableCharDetectNormalEl.checked;
352
+ });
353
+ disableCharDetectInpaintEl.addEventListener("change", () => {
354
+ disableCharDetectNormalEl.checked = disableCharDetectInpaintEl.checked;
355
+ });
356
+ }
357
+
358
  function setStatus(msg) {
359
  statusEl.textContent = msg;
360
  console.log(msg);
 
438
  steps: overrides.steps !== undefined ? overrides.steps : baseSteps,
439
  guidance_scale: overrides.guidance_scale !== undefined ? overrides.guidance_scale : baseGuidanceScale,
440
  guidance_rescale: overrides.guidance_rescale !== undefined ? overrides.guidance_rescale : baseGuidanceRescale,
441
+ no_logo: noLogoEl && noLogoEl.checked,
442
+ disable_character_detection: disableCharDetectNormalEl && disableCharDetectNormalEl.checked
443
  };
444
 
445
  if (overrides.seed !== undefined && overrides.seed !== null && !Number.isNaN(overrides.seed)) {
 
556
  guidance_scale: baseGuidanceScale,
557
  guidance_rescale: baseGuidanceRescale,
558
  first_seed: useFirstSeed ? firstSeedVal : null,
559
+ no_logo: noLogoEl && noLogoEl.checked,
560
+ disable_character_detection: disableCharDetectNormalEl && disableCharDetectNormalEl.checked
561
  };
562
 
563
  try {
 
677
  guidance_scale: baseGuidanceScale,
678
  guidance_rescale: baseGuidanceRescale,
679
  first_seed: (!usedFirstSeedTopLeft && useFirstSeed) ? firstSeedVal : null,
680
+ no_logo: noLogoEl && noLogoEl.checked,
681
+ disable_character_detection: disableCharDetectNormalEl && disableCharDetectNormalEl.checked
682
  };
683
 
684
  setStatus("Auto (3x3 一括): 生成中...");
 
1085
  seed: parseInt(seedEl.value, 10) || baseInfo.seed,
1086
  strength: 0.5,
1087
  mask_png: maskDataUrl,
1088
+ no_logo: noLogoEl && noLogoEl.checked,
1089
+ disable_character_detection: disableCharDetectInpaintEl && disableCharDetectInpaintEl.checked
1090
  };
1091
 
1092
  setStatus("Repaint: inpaint 実行中...");
 
1154
  mask_png: maskDataUrl,
1155
  i2i_mode: true,
1156
  i2i_mask_pct: pct,
1157
+ no_logo: noLogoEl && noLogoEl.checked,
1158
+ disable_character_detection: disableCharDetectInpaintEl && disableCharDetectInpaintEl.checked
1159
  };
1160
 
1161
  setStatus(`i2i: 画像全体を ${pct}% で再描画中...`);