Feature: Add -u switch to chartread for real-time row colour JSON output on stdout #1

Closed
opened 2026-08-20 22:32:57 +01:00 by gronod · 2 comments
Owner

Feature: Real-Time Row-Level Colour JSON Output for UI Integration (-u switch)

Summary & Intent

When using ArgyllCMS's chartread tool inside an external graphical user interface (e.g. Electron, web, or native desktop applications), the UI spawns chartread as a child process. Currently, chartread only writes final measurement data into the .ti3 CGATS file upon complete session exit.

This feature adds a new command-line switch (-u) that instructs chartread to emit a single-line, structured JSON event on stdout immediately upon completing the measurement of each row/strip. This allows the host application to parse stdout in real time, giving users live interactive feedback, real-time patch color rendering, and row-by-row progress visualization.


1. Command-Line Switch Specification

  • Flag: -u
  • Default Behavior (without -u): 100% backward compatible. Standard output and console prompts remain completely unmodified.
  • Enabled Behavior (with -u): After completing and validating each row, chartread prints a line prefixed with ROW_COLORS_JSON: to stdout followed immediately by fflush(stdout).

2. JSON Stream Schema & Format

Output Protocol

  • Prefix: ROW_COLORS_JSON:
  • Suffix: \n followed by fflush(stdout)

Sample Payload

ROW_COLORS_JSON: {
  "event": "row_complete",
  "row_id": "A",
  "row_index": 0,
  "total_rows": 12,
  "patch_count": 21,
  "patches": [
    {
      "id": "1",
      "loc": "A1",
      "is_pad": false,
      "device": [0.0, 100.0, 50.0],
      "expected": {
        "XYZ": [12.3456, 23.4567, 34.5678],
        "Lab": [55.54, 14.21, -8.32]
      },
      "measured": {
        "XYZ": [12.3456, 23.4567, 34.5678],
        "Lab": [55.54, 14.21, -8.32],
        "spectral": {
          "bands": 36,
          "start_nm": 380.0,
          "end_nm": 730.0,
          "norm": 100.0,
          "values": [0.0123, 0.0145, 0.0189, 0.0210]
        }
      }
    }
  ]
}

3. Data Field Specifications & Color Conversions

From research into spectro/chartread.c and xicc/:

  1. Device Channels (device):
    • scb[i]->dev[0 .. nchan-1] * 100.0
    • Supplies input RGB / CMYK percentages (0.0 \dots 100.0\%) to render the target patch design colors.
  2. Expected Colors (expected):
    • Extracted from scb[i]->eXYZ when available in .ti2.
    • Expected D50 Lab computed via icmXYZ2Lab(&icmD50, eLab, eXYZ / 100.0).
  3. Measured XYZ:
    • scb[i]->XYZ[0..2] on reference scale 0.0 \dots 100.0.
  4. Measured Lab (D50):
    • Crucial Scaling Rule: icmXYZ2Lab(&icmD50, lab, scaled_xyz) expects XYZ normalized to 0.0 \dots 1.0 (scaled_xyz = XYZ / 100.0), yielding L^* \in [0, 100], a^*, b^*.
  5. Spectral Bands (spectral):
    • From scb[i]->sp (xspect): spec_n (bands count), spec_wl_short (e.g. 380.0), spec_wl_long (e.g. 730.0), norm (100.0), and spec[0 .. spec_n-1].
    • Omitted if spectral reading is not enabled (-n) or unavailable from the instrument.
  6. Padding Patches (is_pad):
    • Test charts often contain padding patches where strcmp(scb[i]->id, "0") == 0.
    • Set "is_pad": true for padding patches to allow UIs to maintain spatial alignment without rendering false measurement data.

4. Architectural Hook Locations (spectro/chartread.c)

  1. Strip Mode (rmode == 1):
    • Located in read_strips() around line 1951, immediately after all valid patches in the strip are copied to scb = &scols[oroi * stipa] and marked scb[i]->rr = 1.
    • Emit: emit_row_json_colors(paix->aix(paix, oroi), oroi, totpa, stipa, scb, nchan).
    • Note: chartread already resolves bi-directional scan reversal (bdir != 0) and DTP51 offset fixes (boff) before copying to scb, guaranteeing canonical left-to-right patch order.
  2. XY Table Mode (rmode == 2):
    • Located around line 1342, after sheet readings are transferred to scols.
    • Loop over sheet rows and call emit_row_json_colors for each completed row.
  3. Chart Mode (rmode == 3):
    • Located around line 1043, after whole-chart readings are transferred to scols.
    • Loop over chart rows and call emit_row_json_colors for each completed row.
  4. Spot / Patch-by-Patch Mode (rmode == 0 / -p):
    • Located around line 2426, after val.XYZ is saved into scols[pix].
    • Emit patch/row update event.

5. Serializer Implementation Strategy

  • Implement a self-contained C formatting helper emit_row_json_colors(...) directly in spectro/chartread.c using standard fprintf(stdout, ...) and fflush(stdout).
  • Avoids introducing extra external library linkage dependencies (libyajl) into spectro/Jamfile, preserving clean standalone builds across Linux, macOS, and Windows.

6. Acceptance Criteria

  • chartread without -u behaves completely unchanged.
  • chartread -u emits valid single-line JSON (ROW_COLORS_JSON: ...) on stdout with immediate fflush after each row.
  • JSON payload contains id, loc, is_pad, device, expected (when present), measured.XYZ, measured.Lab, and measured.spectral (when active).
  • Bi-directional strips are correctly ordered left-to-right.
  • doc/chartread.html and usage() string in spectro/chartread.c document the -u switch.
# Feature: Real-Time Row-Level Colour JSON Output for UI Integration (`-u` switch) ## Summary & Intent When using ArgyllCMS's `chartread` tool inside an external graphical user interface (e.g. Electron, web, or native desktop applications), the UI spawns `chartread` as a child process. Currently, `chartread` only writes final measurement data into the `.ti3` CGATS file upon complete session exit. This feature adds a new command-line switch (`-u`) that instructs `chartread` to emit a single-line, structured JSON event on `stdout` immediately upon completing the measurement of each row/strip. This allows the host application to parse `stdout` in real time, giving users live interactive feedback, real-time patch color rendering, and row-by-row progress visualization. --- ## 1. Command-Line Switch Specification - **Flag**: `-u` - **Default Behavior (without `-u`)**: 100% backward compatible. Standard output and console prompts remain completely unmodified. - **Enabled Behavior (with `-u`)**: After completing and validating each row, `chartread` prints a line prefixed with `ROW_COLORS_JSON: ` to `stdout` followed immediately by `fflush(stdout)`. --- ## 2. JSON Stream Schema & Format ### Output Protocol - Prefix: `ROW_COLORS_JSON: ` - Suffix: `\n` followed by `fflush(stdout)` ### Sample Payload ```json ROW_COLORS_JSON: { "event": "row_complete", "row_id": "A", "row_index": 0, "total_rows": 12, "patch_count": 21, "patches": [ { "id": "1", "loc": "A1", "is_pad": false, "device": [0.0, 100.0, 50.0], "expected": { "XYZ": [12.3456, 23.4567, 34.5678], "Lab": [55.54, 14.21, -8.32] }, "measured": { "XYZ": [12.3456, 23.4567, 34.5678], "Lab": [55.54, 14.21, -8.32], "spectral": { "bands": 36, "start_nm": 380.0, "end_nm": 730.0, "norm": 100.0, "values": [0.0123, 0.0145, 0.0189, 0.0210] } } } ] } ``` --- ## 3. Data Field Specifications & Color Conversions From research into `spectro/chartread.c` and `xicc/`: 1. **Device Channels (`device`)**: - `scb[i]->dev[0 .. nchan-1] * 100.0` - Supplies input RGB / CMYK percentages ($0.0 \dots 100.0\%$) to render the target patch design colors. 2. **Expected Colors (`expected`)**: - Extracted from `scb[i]->eXYZ` when available in `.ti2`. - Expected D50 Lab computed via `icmXYZ2Lab(&icmD50, eLab, eXYZ / 100.0)`. 3. **Measured XYZ**: - `scb[i]->XYZ[0..2]` on reference scale $0.0 \dots 100.0$. 4. **Measured Lab (D50)**: - **Crucial Scaling Rule**: `icmXYZ2Lab(&icmD50, lab, scaled_xyz)` expects XYZ normalized to $0.0 \dots 1.0$ (`scaled_xyz = XYZ / 100.0`), yielding $L^* \in [0, 100]$, $a^*$, $b^*$. 5. **Spectral Bands (`spectral`)**: - From `scb[i]->sp` (`xspect`): `spec_n` (bands count), `spec_wl_short` (e.g. 380.0), `spec_wl_long` (e.g. 730.0), `norm` (100.0), and `spec[0 .. spec_n-1]`. - Omitted if spectral reading is not enabled (`-n`) or unavailable from the instrument. 6. **Padding Patches (`is_pad`)**: - Test charts often contain padding patches where `strcmp(scb[i]->id, "0") == 0`. - Set `"is_pad": true` for padding patches to allow UIs to maintain spatial alignment without rendering false measurement data. --- ## 4. Architectural Hook Locations (`spectro/chartread.c`) 1. **Strip Mode (`rmode == 1`)**: - Located in `read_strips()` around line 1951, immediately after all valid patches in the strip are copied to `scb = &scols[oroi * stipa]` and marked `scb[i]->rr = 1`. - Emit: `emit_row_json_colors(paix->aix(paix, oroi), oroi, totpa, stipa, scb, nchan)`. - *Note*: `chartread` already resolves bi-directional scan reversal (`bdir != 0`) and DTP51 offset fixes (`boff`) before copying to `scb`, guaranteeing canonical left-to-right patch order. 2. **XY Table Mode (`rmode == 2`)**: - Located around line 1342, after sheet readings are transferred to `scols`. - Loop over sheet rows and call `emit_row_json_colors` for each completed row. 3. **Chart Mode (`rmode == 3`)**: - Located around line 1043, after whole-chart readings are transferred to `scols`. - Loop over chart rows and call `emit_row_json_colors` for each completed row. 4. **Spot / Patch-by-Patch Mode (`rmode == 0` / `-p`)**: - Located around line 2426, after `val.XYZ` is saved into `scols[pix]`. - Emit patch/row update event. --- ## 5. Serializer Implementation Strategy - Implement a self-contained C formatting helper `emit_row_json_colors(...)` directly in `spectro/chartread.c` using standard `fprintf(stdout, ...)` and `fflush(stdout)`. - Avoids introducing extra external library linkage dependencies (`libyajl`) into `spectro/Jamfile`, preserving clean standalone builds across Linux, macOS, and Windows. --- ## 6. Acceptance Criteria - [ ] `chartread` without `-u` behaves completely unchanged. - [ ] `chartread -u` emits valid single-line JSON (`ROW_COLORS_JSON: ...`) on `stdout` with immediate `fflush` after each row. - [ ] JSON payload contains `id`, `loc`, `is_pad`, `device`, `expected` (when present), `measured.XYZ`, `measured.Lab`, and `measured.spectral` (when active). - [ ] Bi-directional strips are correctly ordered left-to-right. - [ ] `doc/chartread.html` and `usage()` string in `spectro/chartread.c` document the `-u` switch.
gronod added this to the v3.5.1 - UI Subprocess Integration milestone 2026-08-20 22:32:57 +01:00
gronod added the Kind/Feature
Reviewed
Confirmed
1
Area/UI
labels 2026-08-20 22:32:57 +01:00
Author
Owner

The JSON row output feature has been implemented behind the -u switch in the feature/chartread-json-output branch. The changes cover Strip mode, XY table mode, Chart mode, and Spot mode, comprehensively providing expected and measured data as requested. The codebase has been successfully compiled and verified.

The JSON row output feature has been implemented behind the `-u` switch in the `feature/chartread-json-output` branch. The changes cover Strip mode, XY table mode, Chart mode, and Spot mode, comprehensively providing expected and measured data as requested. The codebase has been successfully compiled and verified.
Author
Owner

Implemented in commit 6cda572 on branch feature/chartread-json-output.

All acceptance criteria met:

  • -u switch added to command-line options and usage documentation.
  • Real-time row/strip JSON payload (ROW_COLORS_JSON: ...) emitted across Strip, XY Table, Chart, and Spot measurement modes.
  • Modular calculation for expected XYZ/Lab, measured XYZ/Lab (D50 normalized), and spectral data.
  • Full build and binary compilation verified without regressions.
Implemented in commit [`6cda572`](https://git.i3omb.com/gronod/argyllcms/commit/6cda572b932dce57b7432b55b53e8bfb5e01a373) on branch `feature/chartread-json-output`. All acceptance criteria met: - `-u` switch added to command-line options and usage documentation. - Real-time row/strip JSON payload (`ROW_COLORS_JSON: ...`) emitted across Strip, XY Table, Chart, and Spot measurement modes. - Modular calculation for expected XYZ/Lab, measured XYZ/Lab (D50 normalized), and spectral data. - Full build and binary compilation verified without regressions.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: gronod/argyllcms#1