aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/specs/2026-10-30-upscale-qt-design.md
blob: 88f938f9ba65a5a7e76ee9911afd822b48c025ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# upscale-qt design

> **Revised 2026-10-30 during implementation:** the imajev model suggestion
> below was built, measured (right about half the time, thinking cannot be
> disabled in `llama-mtmd-cli`) and removed. The dropdown instead shows
> descriptions from an optional `models.txt` in the upscalers folder. Jobs
> carry no state enum yet. See AGENTS.md for the measurements.

## Goal

Pick an image, preview it, upscale it with an ESRGAN model through `sd-cli`,
check the result against the original, then save it as a new file or
overwrite the original.

## Stack

Single file `upscale-qt`, Python 3 + PyQt6, no other dependencies. External
binaries: `sd-cli` (required), `llama-mtmd-cli` (optional, for suggestions).

## Window

- Toolbar: Open, model dropdown, Upscale, Save As, Overwrite, Settings.
- Center: compare view (one custom widget).
- Status bar: current activity, suggestion result, last stderr line.

Save As and Overwrite are disabled until an upscaled result exists. Upscale is
disabled while a process runs or no model is selected.

## Compare view

- No result yet: shows the original only.
- With result: the original is scaled to the result's size and drawn left of
  a vertical divider, the result right of it.
- Drag near the divider moves it. Drag elsewhere pans. Wheel zooms around
  the cursor. Double-click resets to fit-to-window.
- Before and after share one transform, so the same pixels line up.

## Jobs

Internally a list of jobs, each: source path, chosen model, result path
(temp file) or None, state (idle, suggesting, upscaling, done, failed). The
UI shows only the current job. Opening an image replaces the list with one
job. Batch mode later adds UI over the same list.

## Flow

1. Open via file dialog or `argv[1]`. Image shown at once.
2. If imajev is configured, run
   `llama-mtmd-cli -m <model> --mmproj <mmproj> --image <src> -p <prompt>`,
   the prompt listing the upscaler filenames and asking for exactly one back.
   The prompt also tells it to weigh source quality (noise, compression,
   blur) and not only the subject: in testing it picked a detail model for
   a noisy phone photo where a denoising one did far better. If that keeps
   happening, add an optional `models.txt` (filename plus one-line note)
   fed into the prompt.
   The answer is matched to a filename (exact filename, then stem, then
   case-insensitive substring); a match preselects the dropdown and the
   status bar says "imajev suggests X". No match or failure: dropdown
   unchanged, status says so. The user can always change the dropdown.
3. Upscale runs
   `sd-cli -M upscale --upscale-model <dir/model> -i <src> -o <tmp>.png`.
   The temp file lives in a `QTemporaryDir` for the app's lifetime.
4. On exit code 0 with a readable output, the result loads into the compare
   view. Otherwise a message box shows the last stderr lines.
5. Save As: file dialog, format from the chosen extension.
   Overwrite: confirmation dialog naming the file and new size, then save in
   the original's format to `<file>.part` and rename over the original, so a
   failed write never leaves a truncated original.

## Settings

`QSettings`: upscalers dir, imajev model path, imajev mmproj path, last open
dir. Defaults point at the maintainer's model folders and are editable in a
small Settings dialog. Missing upscalers dir or empty model list: Upscale
disabled with a tooltip. Missing imajev files or binary: suggestion silently
skipped.

## Errors

- Binary not found or crash: message box with the error.
- Non-zero exit: message box with the stderr tail.
- Unreadable input image: message box, job not created.

## Testing

`test_upscale_qt.py` with plain asserts for the non-GUI functions, at least
the answer-to-filename matcher. GUI checked by hand, or headless with
`QT_QPA_PLATFORM=offscreen`.

## Out of scope for now

Batch UI, choice of output scale, backup copies of overwritten files.