[Tool] ChromIQ – a macOS and Windows GUI for ArgyllCMS printer profiling (v3.0.0 beta 10)

itsab1989

Getting Fingers Dirty
Joined
Feb 11, 2026
Messages
91
Reaction score
37
Points
40
Printer Model
Epson ET 8550, Canon Pro 300
@Alan G
Just some of my quick thoughts:


Read patchset again with averaging: Maybe after completing the reading process another option in the pop up window? Add „_1“ to the end of original ti3 name and „_2“ to the new one (or maybe something better) average measurements and proceed with new ti3.

Would this roughly make sense?
 

Alan G

Fan of Printing
Joined
Jul 15, 2017
Messages
34
Reaction score
31
Points
55
Location
Bethesda, Maryland
Printer Model
Canon Pro-1000
I did not even know that something like this exists. Thanks for pointing this out. For now I would add this to my list and maybe come back to you later for further information because I would like to fix some other issues first. The information about the patches with high deltaE values SHOULD be a simple addition I think.

But for what you suggested I would have to see for myself how it works and how I would fit it in without breaking anything.
No problem, I am happy to continue helping you out on this.
 

Alan G

Fan of Printing
Joined
Jul 15, 2017
Messages
34
Reaction score
31
Points
55
Location
Bethesda, Maryland
Printer Model
Canon Pro-1000
@Alan G
Just some of my quick thoughts:


Read patchset again with averaging: Maybe after completing the reading process another option in the pop up window? Add „_1“ to the end of original ti3 name and „_2“ to the new one (or maybe something better) average measurements and proceed with new ti3.

Would this roughly make sense?
Yes, that is exactly what I do when I manually do it using the terminal.
 

itsab1989

Getting Fingers Dirty
Joined
Feb 11, 2026
Messages
91
Reaction score
37
Points
40
Printer Model
Epson ET 8550, Canon Pro 300
It‘s on my list. I just have to confirm that it integrates in the current workflow without breaking anything.
The more stuff like this you add the more you have to think about such things :)
 

pharmacist

Printer VIP
Platinum Printer Member
Joined
May 29, 2007
Messages
3,100
Reaction score
1,802
Points
333
Location
Ghent, Belgium
Printer Model
SC-900 ET-8550 WF-7840 TS705
Not really, Github is just a distribution site and necessary to share files. No rivalling site, as it has to do with programming.
 

pharmacist

Printer VIP
Platinum Printer Member
Joined
May 29, 2007
Messages
3,100
Reaction score
1,802
Points
333
Location
Ghent, Belgium
Printer Model
SC-900 ET-8550 WF-7840 TS705
Here is the proof that the built-in TC9.18 (or my slightly modified TC9.24) is legit:

1779729681418.png
 

itsab1989

Getting Fingers Dirty
Joined
Feb 11, 2026
Messages
91
Reaction score
37
Points
40
Printer Model
Epson ET 8550, Canon Pro 300
I would never doubted that it works. One of my plans is already active again but I have some issues with Claude doing test that don’t give a result. Hope to get this working again
 

itsab1989

Getting Fingers Dirty
Joined
Feb 11, 2026
Messages
91
Reaction score
37
Points
40
Printer Model
Epson ET 8550, Canon Pro 300
@pharmacist @Alan G :

One of my plans is now active again. The first thing I implemented was Alans request for listing the patches with highest deltaE in the check&refine tab because this one is the easiest.
Now I am implementing Pharmacists TC9.24 targets. When this is done and I made a short test myself I will update the releases on GitHub.

But the averaging feature will be in the beta version only at first. This one really changes things and I would kindly ask you Alan to do some testing with it once I release the beta with the feature in it.
Here is Claudes Plan for it. It does not sound bad but it requires some testing to be sure it does not mess up anything else:


⏺ Here's the implementation plan. I've grounded it in the actual code paths


(success handler, signal routing, runner pattern). No code touched yet.





---


Plan: Average repeated measurements in the Measure tab





Approach





Use ArgyllCMS's native average (already on disk, 3.5.0). It auto-averages the


measured XYZ_* + all SPEC_* bands and leaves device RGB/labels untouched —


verified on a real ChromIQ .ti3. Default to plain mean; offer geometric median


(-X) as a robust option. Do not port the colleague's Python tool (wrong axis


for printer targets, drops spectral data).





File-naming scheme





- Reads: <base>_read1.ti3, <base>_read2.ti3, … (accumulate)


- Averaged result: <base>_average.ti3


- Detection ignores any stem with a pre_ / cal_ prefix (those belong to


refinement/calibration, not averaging).





---


Phase 1 — Argyll average wrapper





New file workflow/average_runner.py (mirrors printcal_runner.py):


- @dataclass AverageParams: inputs: list[Path], output: Path, method: str


("mean" | "median_xyz" | "median_lab").


- _build_args(): average [-X|-L] in1.ti3 in2.ti3 … out.ti3 (no flag for mean,


-X/-L for median).


- run(params, on_finish): dispatch through the singleton ArgyllRunner.run()


like every other tool (respects the is_running guard).


- Small error-pattern list (e.g. field/patch-count mismatch, unreadable input)


→ friendly messages, same idiom as _PRINTCAL_ERROR_PATTERNS.





Phase 2 — FileManager naming helpers





core/file_manager.py — add static helpers (keeps naming logic in one place):


- read_variant_path(base_ti3: Path, n: int) -> Path → <base>_read{n}.ti3


- average_path(base_ti3: Path) -> Path → <base>_average.ti3


- existing_read_variants(work_dir, base_stem) -> list[Path] → sorted _readN


files whose stem does not start with pre_/cal_.


- next_read_index(...) → max existing N + 1.





Phase 3 — Success dialog + "Measure again" flow (the core)





ui/tabs/tab_measure.py, _on_measure_done — today the normal-success branch


(ui/tabs/tab_measure.py:3287) just writes [OK] to the log and auto-proceeds.


Change only the full-success, non-cal, non-partial case (ti3_exists and


self._all_done_shown and not is_cal); leave partial-resume and cal_ paths


exactly as they are.





New QDialog (sized to fit, setMinimumWidth(~560), word-wrapped explanatory


text), styled via existing tint_dialog_primary(dlg, _TAB_COLOR). Contents


depend on how many reads exist:





- Only the base read so far (no _readN):


- Continue to Build Profile (current default action)


- Measure again to average — explains noise-reduction; on click: rename


<base>.ti3 → <base>_read1.ti3, set self._averaging_active = True, re-run the


normal measurement (same ti1/ti2/settings via the existing start path). The


next completion writes <base>.ti3 → renamed to <base>_read2.ti3.


- ≥2 _readN files exist:


- Averaging method dropdown: Mean (default) / Geometric median


- Average all reads & build → runs average_runner over all _readN →


<base>_average.ti3, then proceeds with that file


- Use last read only & build → proceeds with <base>_read{N}.ti3


- Measure again → accumulate one more (_read{N+1})





State to add: self._averaging_active: bool, and a counter derived from


existing_read_variants() (recomputed, not just held, so re-entering a project


is consistent).





"Proceed with X" = emit measure_finished(X). Routing already exists:


measure_finished → main_window._on_measure_done →


_tab_profile.set_ti3_path(...) (ui/main_window.py:342). So whichever path we


emit becomes the profile's active .ti3. No profile-tab changes needed for the


happy path.





Phase 4 Averaging method setting





Add "average_method": "mean" to core/settings.py DEFAULTS so the dialog


dropdown remembers the last choice across runs. (Dialog reads/writes it; no


separate settings-dialog UI needed unless you want one.)





Phase 5 — Detection on entering a project (your Q2 follow-up)





When a project that already holds ≥2 _readN.ti3 files is opened, offer to


average. Lightest hook: in the measure tab's set_ti1_path (or when the working


dir is established), call existing_read_variants(); if ≥2 and no _average.ti3


yet, surface a non-modal prompt/banner: "Found N prior reads — average them,


or add another?" I'd build this after Phase 1–4 land and the core flow is


proven.





---


Edge cases to handle





1. Matching .ti2 for the chosen file. Profile tab emits ti2_found only when


<stem>.ti2 sits next to the .ti3 (ui/tabs/tab_profile.py:230).


<base>_average.ti2 won't exist → "print again"/refinement linkage breaks. Fix:


make the ti2 lookup strip a trailing _readN/_average suffix (preferred), or


copy <base>.ti2 → <base>_average.ti2. I lean toward suffix-stripping in the


lookup.


2. Profile output name becomes <base>_average.icc (colprof uses the .ti3


stem). That's descriptive — fine, but confirm you're happy with it vs. forcing


<base>.icc.


3. is_running guard — "Measure again" must wait for chartread to fully exit


before relaunching; the dialog only appears post-_on_measure_done, so this is


naturally satisfied.


4. Field/patch mismatch between reads (e.g. a partial read mixed in) — average


errors out; surface it and don't emit success.


5. Cleanup — _readN/_average files should survive clean_folder between steps


but be removable on a true "start over"; confirm against the existing


extension-based cleanup in chart_creator.py.





Verification before shipping





- Two real back-to-back reads of one chart → confirm average produces sane


averaged XYZ/spectral and the profile builds from _average.ti3.


- Mean vs. -X median sanity check on the same pair.





Open question for you





Edge case #2 — averaged profile named <base>_average.icc (descriptive) vs.


forcing canonical <base>.icc? I recommend leaving it as _average for


traceability.





---


Want me to save this to docs/dev_averaging.md (matches your docs/dev_*


convention), and should I sequence Phases 1–4 as the first deliverable with


Phase 5 as a follow-up?
 
Top