The slicer stops throwing away good sheets because two bellies touch
Oliver's idea, tested: give the generator a WHOLE POSE SHEET and ask it to redraw it as a different zombie. It works, and better than what I was doing — the master set's stride pair is 0.99/0.157/0.565 and the restyled brute came back 1.00/0.148/0.618. The pose relationship SURVIVED the character swap, in one call, four frames. It was reported as a failure only because the four brutes stand shoulder to shoulder and their bellies overlap, so segmentation by empty columns saw ONE blob and the sheet was discarded. That same rejection ate five rolls earlier today — "found 2 frames, expected 3" — and the sheet it lost last was the best roll of the day. A row is evenly spaced by construction, because that is what we ask for. So when the blob count comes up short of a count we ALREADY ASKED FOR, look near each even division and cut at the thinnest column in that window: between two figures the ink thins even where it does not vanish. Never used to guess how many figures there are, only to reach the number requested — a wrong guess would install garbage silently, and the frame count is the one thing this pipeline has always been able to trust. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4e7b26f874
commit
cccdaba8f0
1 file changed
+30
-2
+30
-2
@@ -73,7 +73,26 @@ def block_size(a):
|
||||
if tot and ok / tot > 0.82: best = b
|
||||
return best
|
||||
|
||||
def frames(a, cuts):
|
||||
# WHERE TO CUT WHEN THE FIGURES TOUCH. Segmentation by empty columns needs a clear gap, and the
|
||||
# generator routinely spaces a row so a belly, an elbow or a racket overlaps its neighbour — a whole
|
||||
# sheet of otherwise perfect art then reports "found 2 frames, expected 4" and is thrown away. That
|
||||
# happened five times in one afternoon, and the sheet it lost last was the best roll of the day.
|
||||
# A row is EVENLY SPACED by construction (we ask for one), so when the count is known the honest
|
||||
# recovery is: look near each even division and cut at the THINNEST column within that window —
|
||||
# between two figures the ink thins out even where it does not vanish. Never used to guess a count,
|
||||
# only to reach a count we already asked for.
|
||||
def valley_cuts(ink_cols, x0, x1, n):
|
||||
span = x1 - x0
|
||||
win = max(2, span // (3 * n))
|
||||
out = []
|
||||
for k in range(1, n):
|
||||
c = x0 + span * k // n
|
||||
lo, hi = max(x0 + 1, c - win), min(x1 - 1, c + win)
|
||||
if hi <= lo: return []
|
||||
out.append(lo + int(np.argmin(ink_cols[lo:hi])))
|
||||
return out
|
||||
|
||||
def frames(a, cuts, want=0):
|
||||
ink = (a[..., 3] > 0)
|
||||
cols = ink.any(0)
|
||||
out, start, gap = [], None, 0
|
||||
@@ -88,13 +107,22 @@ def frames(a, cuts):
|
||||
for c in sorted(cuts, reverse=True): # manual splits where two figures touch
|
||||
for i, (x0, x1) in enumerate(out):
|
||||
if x0 < c < x1: out[i:i+1] = [(x0, c), (c, x1)]; break
|
||||
# ...and the automatic version of the same rescue, when no manual cut was given
|
||||
if want and len(out) < want and not cuts:
|
||||
xs = np.where(cols)[0]
|
||||
if len(xs):
|
||||
auto = valley_cuts(ink.sum(0), int(xs.min()), int(xs.max()) + 1, want)
|
||||
if auto:
|
||||
print(f'note: only {len(out)} blob(s) — figures touch; cutting at {auto}')
|
||||
edges = [int(xs.min())] + auto + [int(xs.max()) + 1]
|
||||
out = list(zip(edges, edges[1:]))
|
||||
return out
|
||||
|
||||
def main(sheet, outs, target_h, cuts, anchor, defringe=2):
|
||||
a = key(Image.open(sheet), defringe)
|
||||
b = block_size(a)
|
||||
crops = []
|
||||
for x0, x1 in frames(a, cuts):
|
||||
for x0, x1 in frames(a, cuts, len(outs)):
|
||||
sub = a[:, x0:x1]
|
||||
ys = np.where((sub[..., 3] > 0).any(1))[0]; xs = np.where((sub[..., 3] > 0).any(0))[0]
|
||||
crops.append(sub[ys.min():ys.max()+1, xs.min():xs.max()+1])
|
||||
|
||||
Reference in new issue
Block a user