Installing and self-checking
✅ verified · the setup, self-check and ComfyUI scripts are listed in appendix A · the code behind them: appendix B
What you need
| thing | version and notes |
|---|---|
| Fedora | 44. No other distribution has been tried; the package names here are Fedora’s |
| Krita | 6.0 or newer, installed from RPM. Node.paintLine and paintPath are new in 6.0 and are the foundation of all of this; 5.x leaves only injected tablet events (coordinate calibration unknowable, strokes landing only on the first layer, saveAs segfaulting, throughput a few hundred strokes a minute) and is not supported. Flatpak does not work either: the sandboxed Python has no numpy, and the paths are moved to ~/.var/app/org.kde.krita/ |
| Xvfb | Headless Krita still needs an X display (QT_QPA_PLATFORM=offscreen segfaults) |
| uv | 0.12 or newer. The library is a uv workspace whose member is dtp/ |
| the system Python | Krita embeds the system Python (3.14). The numpy / Pillow / scipy / scikit-learn a job imports have to be system packages; it cannot see the ones in a venv |
| ComfyUI | Three stages need it (segment, lineart, per-layer lineart), through two custom_nodes: comfyui_controlnet_aux (lineart) and comfyui_segment_anything (GroundingDINO + SAM). 16 GB of VRAM is enough |
| See-through | Completion needs it (ch. 3). It lives in see-through/ (beside dtp/; seethrough_dir in dtp.toml) with its own venv (torch 2.8 + cu128, transformers 5.0, diffusers 0.37 – leave ComfyUI’s alone), and its weights download from Hugging Face on the first run. 12 GB of VRAM or more, and stop ComfyUI before running it |
sudo dnf install krita python3-numpy python3-pillow python3-scipy python3-scikit-learn python3-matplotlib xorg-x11-server-Xvfb
cd Krita/ && uv sync # the library's dependencies (numpy scipy pillow scikit-learn matplotlib) plus pytest
uv run pytest # the library's unit tests, which never touch Krita
ComfyUI lives in ComfyUI/ (beside dtp/ and diffusion-to-painting/; the location can be changed with comfy_dir in dtp.toml or DTP_COMFY_DIR) with its own .venv (Python 3.12, torch + cu130, transformers 5). ComfyUI itself is not modified; only custom_nodes and models are added. comfyui_segment_anything needs two patches on transformers 5 (in local_groundingdino/models/GroundingDINO/bertwarper.py: BertModel.get_head_mask is gone, so pass None; and the third positional argument of get_extended_attention_mask became a dtype, so drop it), with the originals kept as .orig.
The harness: a separate HOME
Running jobs in headless Krita rests on a separate HOME, krita-bot/ (the location can be changed with harness_home in dtp.toml). The harness script (appendix A) creates it; it is idempotent and is done once:
krita-bot/
run_job.sh the launcher
.config/kritarc the minimal config: autosave off, undoStackLimit=3
.local/share/krita/pykrita/autopaint/ a Krita extension: 3s after startup it execs
$KRITA_JOB, then os._exit
Two reasons for a separate HOME: a batch job’s settings never pollute your own Krita preferences, and rm *autosave*.kra cannot reach your real recovery files.
What run_job.sh does: sets HOME, XDG_RUNTIME_DIR, LANG=C.UTF-8 and PYTHONIOENCODING=utf-8, puts every package’s src on PYTHONPATH (colon-separated: the library is one distribution per rank, all sharing the one dtp. namespace), deletes any leftover *autosave*.kra, and runs xvfb-run -a krita --nosplash (with --gui it runs on a real desktop, which is how tools/gui.py <step dir> replays a step). The job process imports the library’s source directly – no code is copied, and changing the library needs no re-setup. Before starting Krita, harness.run_job() strips the uv venv’s PYTHONHOME, PYTHONPATH, VIRTUAL_ENV and .venv/bin: Krita’s embedded system Python reads them, its plugin system fails to start (“Please make sure that the needed packages are installed”), and the job never begins while the process hangs.
31 self-checks
The self-check script (appendix A) starts Krita once and really runs each check, reading the pixels back: version at least 6 / filling the ground / preset / stroke / path / ellipse / a selection confining and releasing / select_mask feathering / erase / a filter’s box / blending / stage and final saving / the output layout / building the tree / nesting order / the GroupLayer type / addressing / by_role / by_label / rejecting a duplicate name / clipping / a partial box read and write / the manifest / the .kra round trip (layer count, clip, blend / opacity / label). After changing the session wrapper, all of them have to pass.
ComfyUI
uv run python dtp/tools/comfy.py # starts on 127.0.0.1:8188 in the background, logging to work/comfy.log; exits 1 if it is not up within 60 seconds
fuser -k 8188/tcp # to stop it: kill by port
Which stages need it: segment (one mask per SAM word), lineart (extracting lineart) and lines (one lineart per layer). complete (See-through, its own process), trace, draft, paint and gauge do not. ComfyUI’s output is cached in the step’s ai/, and it is not called again when that is present. The driver is HTTP (POST /prompt plus polling /history); LoadImage only accepts a filename under ComfyUI/input/, so the driver copies the file there first.
How far each model can be trusted: lineart extraction is discriminative and steady; text-prompted segmentation depends on the word (ch. 1). The completion model (See-through) is generative, so only the shape and pixels of what is hidden are taken from it, and every one of them is reviewed (ch. 3).
It will not start, or it dies halfway
| symptom | cause | fix |
|---|---|---|
| Krita segfaults on startup | QT_QPA_PLATFORM=offscreen | It needs Xvfb or a real display (run_job.sh already uses xvfb-run) |
| the job never starts and the process hangs | Krita was started from inside a uv venv, carrying VIRTUAL_ENV / PYTHONHOME in with it | harness.run_job() already strips them; do the same if you start it yourself |
print("中文") raises UnicodeEncodeError partway through | only LANG was set | PYTHONIOENCODING=utf-8 has to be set too, and job files must be read with encoding='utf-8' |
| every later run fails with no error and no traceback | an autosave left by a crash popped up the recovery dialog | rm -f krita-bot/*autosave*.kra (run_job.sh deletes them every time too), and EnableAutosave=false |
| a segfault in the C++ layer, with no Python traceback | paint* was called with no view | Session.__init__ already calls addView; if you create a document yourself you must too |
| the criteria report on the previous round’s output | a failed job was swallowed | the harness raises on JOB FAIL; do not work around it |
| “the harness is not installed” | krita-bot/ was deleted | run the harness script, then the self-check (appendix A) |
Ending a session kills the background processes and can leave a krita zombie: pkill -x krita and rerun. Do not pkill -f "<a pattern that matches your own command line>", and do not for pid in $(pgrep -f ...) grepping your own keyword either – both kill the shell that issued the command (exit 144). Before starting a long job, pgrep -a -f main.py shows whether one is still running from an earlier session.