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

Per-layer lineart: each layer’s own lines

🧪 in progress (it is in the library and running on the worked example) · it reuses ch. 2’s candidate pool and arbitration · needs ComfyUI · step 4 of the layered route · code: appendix B

What this step does

Once the layers exist – after ch. 3, each one is a whole object – each layer gets its own lines. Each is put alone on a ground and a lineart is extracted from it, with the same candidate pool and the same arbitration as ch. 2 and exactly one difference: the image being scored is that layer’s own image, which is the source’s pixels where visible and the completed image where hidden. A line belongs to the layer it was extracted from, with nothing to guess; the hidden part has lines too, drawn inside the group and visible only when the layers above are hidden; and because those were extracted from invented pixels, every one of them is accounted for.

Why ch. 2 is not enough: extracting the lines before the layers exist leaves nothing to assign them by but “how many sample points it covers”, so at an occlusion boundary two layers’ lines are mixed together; and the hidden part has no edge on the source to score against, leaving only an unscored ring of contour. This step puts the order right.

Inputs and outputs

inputfrom
each layer’s complete pixels, and the whole shapes with their invented areascomplete
the sub-parts and the silhouette, plus one mask per word, for the groups cut out of a masksegment
the lines on the sourcelineart (optional, used only for comparison)
which sub-parts make up each group, which group is cut out of a mask, and which lineart layer each layer’s lines belong tothe parts table
one lineart per layerComfyUI
output (stepNN-lines/)contents
the linesevery surviving line, one stroke per row, with two things ch. 2’s lines do not carry: which layer it belongs to, and what fraction of it falls in the invented area
one review image per layerthe same three colours, plus that layer’s scores
the combined review imagethe kept lines drawn over the source and coloured by layer, the pale ones being in the invented area
the tallythe line layers, the candidate / kept / invented counts per group, and the agreement with the lines on the source
what ComfyUI was given and producedthis layer alone on a flat ground, and the raster lineart it came back with (only the one extractor in use)

The file names are all in appendix F; the parts-table fields are in appendix G.

The method

For each group, in the order the parts table gives them, with a mask-cut group inserted after the group it was cut out of:

  1. This layer’s image: a completed group uses its completed pixels; a group with nothing above it and no completion (the topmost hair, the eyes cut out of a mask) uses the source multiplied by its visible mask.
  2. This layer and nothing else: on a flat ground, with nothing else at all. The ground’s brightness is chosen against the layer’s own colour – a bright layer gets dark grey and a dark layer light grey – so that the outline is always a real edge on this image and the extractor is certain to draw it, which is exactly the outline wanted. No glow and no other parts: lineart extraction needs no context, and given some it takes it for lines.
  3. Extract: ComfyUI produces this image’s lineart (manga by default, changed with --source=; ch. 2, “source two”).
  4. The candidate pool: skeletonizing (ch. 2, “Skeletonizing”) plus the ring of this layer’s whole shape (ch. 2, “source one”), treating this layer as the only sub-part there is.
  5. Arbitration: scoring, trimming and overrides.json exactly as in ch. 2, except that the support for a line is measured against this layer’s image, not the source.
  6. The account: every stroke carries group = this layer, part = LINE_LAYER_OF_GROUP[group], and made_up = the fraction of its samples falling in region_<G>, the invented part.

Against ch. 2. The lines in the visible part (made_up < 0.5) and the lines arbitrated on the source are compared both ways: the fraction of samples falling within 4 px of the other. Both being high means the visible part did not change, and the differences should be at the occlusion boundaries only – where ch. 2 guessed which layer a line belonged to and this step knows.

Knobs

parameterdefaultmeaning
sourcemangawhich extractor to use (ch. 2, “source two”)
all of ch. 2’s knobsthe samepassed straight through to the candidate pool (thr, fit_err, line_min_len, …)

What a person decides here

the decisionhow to make it
which lineart layer a layer’s lines go toone entry per group, the mask-cut ones included; a group nobody listed gets no lines

The field name, its format and the step to rerun are in appendix G.

overrides.json is shared with ch. 2 (its boxes are 1x full-image coordinates and apply to both steps).

Reviewing and overruling

One review image per layer: that layer alone on a ground, with blue for a kept AI line, brown for a kept contour and red dashes for dropped. Look for: whether the outline is a single line; whether the lines in the hidden part (the pale ones in the combined image) look like this object’s lines – they were extracted from invented pixels, so if the model invented the wrong shape the lines are wrong with it; and whether the eye layer has any lines at all. An agreement below ninety per cent in the tally calls for looking at which layer it is, and it is usually that layer’s visible mask disagreeing with ch. 2’s guess.

Where it breaks

  • The image given to the extractor must contain nothing but this layer: the glow reference is black in its depths, and once a person-shaped black silhouette got into the image the extractor drew the whole person’s outline into that layer’s lineart. Use a flat ground.
  • A line in the invented area is invented: it carries its own invented fraction, and ch. 3’s account says which pixels those were; in the finished painting they sit under the layers above and surface only when those are hidden.
  • The extractor always draws a ring around a layer’s outline: which is right, since that is the outline; but when a layer’s mask has a hole in it (the eyes cut out of the face) it draws a ring around the hole too – and a hole’s edge belongs to the layer above.
  • A layer too small (a few hundred pixels) is skipped and gets no lines.
  • Disagreeing with ch. 2: a low agreement does not necessarily mean this step is wrong; more often ch. 2 assigned a line to the wrong layer by “how much it covers”.

On origin

The candidate / kept / invented counts per layer and the agreement with ch. 2: ch. 19, “The layered route”.