Spaces:
Running on Zero
Running on Zero
Detect touching duplicate sprite subjects
Browse files
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 =
|
| 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 |
-
|
| 2177 |
-
if not
|
| 2178 |
warnings.append("sprite has no significant foreground subject")
|
| 2179 |
-
elif
|
| 2180 |
-
warnings.append(f"sprite contains {
|
| 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 |
|