diff --git a/assets/_fine.py b/assets/_fine.py index 9234d7a..3908428 100644 --- a/assets/_fine.py +++ b/assets/_fine.py @@ -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])