Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Arbitration: scoring and trimming

✅ verified

What this section is for

Both sources’ candidates go into one pool, and one edge often has two or even three candidates, some shaky, some hallucinated. Which survive cannot be decided by source – “raster lineart wins” and “contours win” are each wrong half the time. The only judge is the source image: there really is an edge under the line, and the line rides that edge. This section is about scoring, about keeping one line where several overlap, and about how a person overrules it.

Concepts

support: sampling along the line, the fraction of samples where the source really has an edge and the line rides it. It answers “is this line real”.

flips: how many times the curvature changes sign per 100 px. It answers “is this line shaky”.

Trimming: where lines overlap, the later line is not dropped whole; only the part covered by an already-kept line is cut away, and what remains is refitted.

Three candidates: the one riding the edge has high support, while the one 8 px off the edge and the one on flat colour have support 0. Right: the luminance across the edge along the normal, and its gradient peak.

How it works

Scoring. Take a sample \(p_i\) every 2 px along the curve and a luminance profile of \(\pm 4\) px along the normal \(\mathbf{n}_i\); the largest difference between adjacent pixels on that profile is the gradient peak \(g_i\), and the peak’s distance from the line is \(o_i\).

\[ \text{support} = \frac{1}{N} \sum_{i=1}^{N} \big[\, g_i \ge T \ \wedge\ |o_i| \le 2 \,\big], \]

\[ \text{score} = \text{support} - 0.05 \cdot \text{flips}, \]

with \(T = 8\) (luminance per pixel). Neither condition can be dropped: with the gradient peak alone and no position, a line 8 px off the edge still counts as having one; with the position alone and no gradient, any line on flat colour counts. A raster-lineart line riding a real edge has support near 1; a contour, having been expanded a little, has 0.5 to 0.7; a shaky line has flips in the tens and support barely over the line. The cut between two same-coloured pieces of skin has low support and is not drawn, which is right.

Dropping. Anything with \(\text{support} < 0.5\) is dropped outright.

Trimming. First sort: good enough (support at least 0.6 and flips at most 4) comes first, then length in doubling bands (\(\lfloor \log_2 (L / 20) \rfloor\)), then score. Going down that order, each line measures what fraction of itself falls within \(r = 4\) px of the already-kept lines \(K\):

\[ f = \frac{|\{\, i : d(p_i, K) \le r \,\}|}{N}. \]

With \(f < 0.2\) the whole line is kept; otherwise only the uncovered runs are kept, each at least 12 px, refitted (tolerance 1.0) into sub-candidates. Why that order: a long, good-enough contour should not be “beaten” one by one by a string of 40 px fragments lying along it, and non-maximum suppression by score lets one small fragment drop a long line entirely, so the outline disappears.

One long line, two fragments and a line that runs off. After trimming: the long line claims its ground first; a fragment lying wholly within 4 px of it is dropped; and the one that runs off keeps only the part nothing covers.

Overriding. After scoring and trimming, the override table forces the outcome by box (full-image coordinates at original size, testing a candidate’s centre) or by id: keep the named source and drop the rest.

The method

  1. Compute support, flips and score for every candidate.
  2. Drop the ones whose support falls short.
  3. Sort, and trim one line at a time.
  4. Read the override table.
  5. Write the review image and every candidate’s scores.

How to read the review image

The review image is a faded source with the kept lines coloured by which source they came from: blue for raster lineart, brown for a contour, drawn at the radius they will be painted with; dropped candidates are red dashes. The scores file beside it holds each candidate’s support, flips, score and the reason it was dropped – and there are only two reasons: the source has no edge under it, or such-and-such a percentage of it was covered by an already-kept line.

The order to look in: first whether the outlines are whole, one line each; then whether any red dashes should have been kept (a real edge dropped, usually because the contrast is too faint); and last whether any blue line is something the source does not have (a highlight band in the hair, the inside of an eye).

Common problems

symptomcausewhat to change
a real edge was droppedthe contrast is faint and the gradient peak falls short of the thresholdname the source by box in the override table
a hallucinated line survivedit happens to ride a soft transitionoverride it to none
a long line disappears and only fragments remainthe trimming order is wrong (sorted by score)the current order is good-enough first, then length, so this cannot happen; if it does, it is a bug
trimming goes wrong after a change of resolution\(r = 4\) px is defined at original sizescore and trim at original size, never on the enlarged image

Further reading

  • Code: appendix B, under lineart – scoring, resolving overlaps, applying the overrules, the arbitration itself, and the review image.
  • Worked example: the lineart section of ch. 19, “The layered route”.