LeafCat79 commited on
Commit
daff9c6
·
verified ·
1 Parent(s): 902a9ad

Detect touching duplicate sprite subjects

Browse files
Files changed (1) hide show
  1. app.py +55 -5
app.py CHANGED
@@ -1774,7 +1774,7 @@ def primary_diffusion_png(spec: AssetSpec, index: int, run_id: int) -> tuple[byt
1774
  if is_background:
1775
  return content, None
1776
 
1777
- last_component_count = len(significant_foreground_component_areas(content))
1778
  if last_component_count == 1:
1779
  return content, None
1780
 
@@ -1928,6 +1928,56 @@ def significant_foreground_component_areas(content: bytes) -> list[int]:
1928
  return sorted((area for area in areas if area >= minimum), reverse=True)
1929
 
1930
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1931
  def free_diffusion_png(spec: AssetSpec, index: int, run_id: int) -> tuple[bytes | None, str | None]:
1932
  global FREE_DIFFUSION_PIPE, FREE_DIFFUSION_ERROR
1933
  if FREE_DIFFUSION_ERROR:
@@ -2173,11 +2223,11 @@ def validate_asset_png(content: bytes, spec: AssetSpec) -> list[str]:
2173
  )
2174
  if any(value > 16 for value in corners):
2175
  warnings.append("sprite has opaque corner pixels")
2176
- component_areas = significant_foreground_component_areas(content)
2177
- if not component_areas:
2178
  warnings.append("sprite has no significant foreground subject")
2179
- elif len(component_areas) > 1:
2180
- warnings.append(f"sprite contains {len(component_areas)} significant foreground subjects")
2181
  return warnings
2182
 
2183
 
 
1774
  if is_background:
1775
  return content, None
1776
 
1777
+ last_component_count = estimated_foreground_subject_count(content)
1778
  if last_component_count == 1:
1779
  return content, None
1780
 
 
1928
  return sorted((area for area in areas if area >= minimum), reverse=True)
1929
 
1930
 
1931
+ def has_deep_vertical_foreground_valley(content: bytes) -> bool:
1932
+ """Detect two large side-by-side silhouettes joined by a thin prop, tail, or shadow."""
1933
+ image = Image.open(io.BytesIO(content)).convert("RGBA")
1934
+ width, height = image.size
1935
+ alpha = image.getchannel("A")
1936
+ foreground = [[alpha.getpixel((x, y)) >= 64 for x in range(width)] for y in range(height)]
1937
+ active_x = [x for x in range(width) if any(foreground[y][x] for y in range(height))]
1938
+ active_y = [y for y in range(height) if any(foreground[y][x] for x in range(width))]
1939
+ if not active_x or not active_y:
1940
+ return False
1941
+
1942
+ x0, x1 = min(active_x), max(active_x)
1943
+ y0, y1 = min(active_y), max(active_y)
1944
+ bbox_width = x1 - x0 + 1
1945
+ bbox_height = y1 - y0 + 1
1946
+ if bbox_width < 12 or bbox_height < 12:
1947
+ return False
1948
+
1949
+ columns = [sum(foreground[y][x] for y in range(y0, y1 + 1)) for x in range(x0, x1 + 1)]
1950
+ total = sum(columns)
1951
+ minimum_side_area = total * 0.28
1952
+ minimum_side_width = max(4, int(bbox_width * 0.22))
1953
+ minimum_side_peak = bbox_height * 0.55
1954
+ maximum_valley = bbox_height * 0.25
1955
+ prefix = 0
1956
+
1957
+ for index in range(1, len(columns) - 1):
1958
+ prefix += columns[index - 1]
1959
+ suffix = total - prefix - columns[index]
1960
+ if prefix < minimum_side_area or suffix < minimum_side_area:
1961
+ continue
1962
+ if index < minimum_side_width or len(columns) - index - 1 < minimum_side_width:
1963
+ continue
1964
+ valley = sum(columns[index - 1 : index + 2]) / 3
1965
+ if valley > maximum_valley:
1966
+ continue
1967
+ if max(columns[:index]) < minimum_side_peak or max(columns[index + 1 :]) < minimum_side_peak:
1968
+ continue
1969
+ return True
1970
+ return False
1971
+
1972
+
1973
+ def estimated_foreground_subject_count(content: bytes) -> int:
1974
+ """Estimate material subjects, including silhouettes that touch through a narrow bridge."""
1975
+ component_count = len(significant_foreground_component_areas(content))
1976
+ if component_count != 1:
1977
+ return component_count
1978
+ return 2 if has_deep_vertical_foreground_valley(content) else 1
1979
+
1980
+
1981
  def free_diffusion_png(spec: AssetSpec, index: int, run_id: int) -> tuple[bytes | None, str | None]:
1982
  global FREE_DIFFUSION_PIPE, FREE_DIFFUSION_ERROR
1983
  if FREE_DIFFUSION_ERROR:
 
2223
  )
2224
  if any(value > 16 for value in corners):
2225
  warnings.append("sprite has opaque corner pixels")
2226
+ subject_count = estimated_foreground_subject_count(content)
2227
+ if not subject_count:
2228
  warnings.append("sprite has no significant foreground subject")
2229
+ elif subject_count > 1:
2230
+ warnings.append(f"sprite contains {subject_count} significant foreground subjects")
2231
  return warnings
2232
 
2233