Skip to content

ENH: Write Image color table, axis flip, and physical scale bar#1671

Open
imikejackson wants to merge 23 commits into
BlueQuartzSoftware:developfrom
imikejackson:topic/write_images_color_table
Open

ENH: Write Image color table, axis flip, and physical scale bar#1671
imikejackson wants to merge 23 commits into
BlueQuartzSoftware:developfrom
imikejackson:topic/write_images_color_table

Conversation

@imikejackson

@imikejackson imikejackson commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Extends the Write Image filter with color-table generation, broader type support, format-aware validation, an output flip, and a physical scale bar. Closes #1617. Closes #786.

  • Inline color table — a Create Color Table option converts a single-component array to an RGB image using a color preset at write time, removing the need to chain a separate Create Color Map filter. Colorization is streamed one slice at a time (no full RGB volume held in memory) and shares the normalization/interpolation code path with Create Color Map, so the output is identical to running the two filters in sequence.
  • Optional mask — masked voxels are written with a user-specified color; the mask is genuinely optional in color-table mode.
  • All numeric input types for color-table mode (int8 … float64).
  • Format-aware write-type validation — preflight now rejects a (format, data type) combination the backend cannot write (for example, a float32 array to PNG) instead of failing partway through writing. The image backends are the single source of truth for their writable types (stb = uint8; libtiff = uint8/uint16/float32).
  • Optional output-image flipNone / Flip About X Axis / Flip About Y Axis, applied only to the written image files; the input Image Geometry and its data arrays are left unmodified. Works across the raw-write path, the color-table path, and all three slice planes.
  • Physical scale bar (ENH: Filter to add a physical measurement bar to an image #786) — an Add Physical Scale Bar option appends a white band below each written image containing a left-justified black bar with the length label on the same line (for example 50 µm). The bar length is chosen automatically (largest 1/2/5×10ⁿ value ≤ 25% of the physical image width, from the Image Geometry's spacing) and the label units come from the geometry's length unit, rescaled to a readable SI prefix. The band is appended — image pixels are never covered — and the written file becomes 8-bit RGB. Requires uint8 input (1/3/4 components) or Create Color Table; validated during preflight along with the geometry spacing. Flip composes correctly: the bar stays upright at the bottom.
  • Filter documentation updated for all of the above.

Internally, the control-point interpolation and normalization were extracted into shared ColorTableUtilities helpers (with a min == max divide-by-zero guard), and Create Color Map was refactored onto them so both filters use one implementation. For the scale bar, the vendored canvas_ity 2D canvas library and the Lato Regular embedded font moved from OrientationAnalysis to the core simplnx library (build-tree include only, nothing new installed), and the measurement/rendering logic lives in a new unit-tested ScaleBarRenderer core utility.

Test Plan

  • SimplnxCore unit tests pass (906/906), including new Write Image cases: color-map parity against the Create Color Map + Write Image chain, all ten numeric input types, the format/type preflight matrix, optional mask, constant-array (min == max) handling, and X/Y flip round-trips read back from disk.
  • Scale-bar coverage: 4 ScaleBarRenderer core test cases (nice-length picker, SI-prefix label formatter, band height, band rendering with exact pixel probes) and 4 new Write Image cases (grayscale/RGB/color-table/XZ-plane write→read-back with exact bar-position assertions, flip composition, preflight accept/reject matrix). 12/12 WriteImage + 4/4 renderer tests pass.
  • Out-of-core build/tests (not yet run in this branch)

@imikejackson
imikejackson requested a review from nyoungbq July 14, 2026 21:22
@imikejackson imikejackson changed the title ENH: Inline color table, format-aware types, and flip for Write Image ENH: Implement Write Image color table, format-aware types, and axis flip Jul 15, 2026
@imikejackson imikejackson changed the title ENH: Implement Write Image color table, format-aware types, and axis flip ENH: Write Image color table, axis flip, and physical scale bar Jul 16, 2026
imikejackson and others added 23 commits July 16, 2026 14:10
…1617)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…put (BlueQuartzSoftware#1617)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…re#1617)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ip tests (BlueQuartzSoftware#1617)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tware#1617)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Rename k_FlipMode_Key value "flip_mode" -> "flip_mode_index" so the
  ChoicesParameter key satisfies the FilterValidation parameter-key
  convention (ChoicesParameter keys must end with "_index"), fixing the
  failing "nx::core::FilterValidation::Filter Parameter Keys" test.
* Update the parametersVersion history comment accordingly.

Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
* Guard NormalizeValue against non-finite inputs and signed-integer
  overflow for wide integer types; constant and degenerate ranges map
  deterministically to the first control color
* Guard NormalizeBinPoints against single-control-color presets and
  reject presets with fewer than 2 control colors in WriteImage and
  CreateColorMap, preventing an out-of-bounds read
* Add IImageIO::supportedWriteComponentCounts and validate component
  count during non-color preflight so unsupported counts fail before
  execution instead of writing non-conforming files
* Declare EXTRASAMPLES for 4-component TIFF output
* Validate the selected color preset during preflight

Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
* Add 3D multi-slice color-table roundtrips for XY, XZ, and YZ planes
  with independent index derivation
* Span negative values in signed-type color roundtrips
* Cover FlipAboutYAxis with 3-component pixels and odd-height FlipAboutXAxis
* Exercise the uint8 mask branch alongside bool
* Extend the format/type preflight matrix (.bmp, .jpg, float64) and add
  component-count validation cases with error-code assertions

Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
* Exposes the header via the simplnx target BUILD_INTERFACE so any
  plugin can draw with it; SimplnxCore needs it for the Write Image
  scale bar (BlueQuartzSoftware#786)
* WritePoleFigureFilter keeps its CANVAS_ITY_IMPLEMENTATION define;
  each shared library compiles its own copy of the single-header
  implementation

Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
* fonts::Base64Decode and the embedded Lato Regular TTF are needed by
  the core ScaleBarRenderer for the Write Image scale bar (BlueQuartzSoftware#786)
* LatoBold and FiraSansRegular stay in OrientationAnalysis (only used
  by WritePoleFigureFilter)

Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
* ComputeNiceBarLength picks the largest 1/2/5 x 10^n length that fits
  25% of the physical image width
* FormatLengthLabel renders metric units with SI engineering prefixes
  (0.0001 m -> "100 µm"); non-metric units label as-is; Unspecified
  omits the suffix
* ComputeBandHeight sizes the padded band (8% of height, min 24 px)
* Band rendering lands in a follow-up commit

Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
* White band with a centered black bar at integer pixel coordinates
  and the length label in embedded Lato Regular above it
* Band-only rendering: image pixels never pass through the canvas so
  the written image region stays byte-for-byte identical

Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
* New optional 'Add Physical Scale Bar' bool (add_scale_bar);
  parameters version bumped to 5
* Preflight requires uint8 (1/3/4 components) or color-table mode and
  positive finite horizontal spacing; reports the padded output size

Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
* Band renders once per run and is appended below each slice after the
  flip, so the bar is always upright at the bottom
* Slice pixels convert to RGB (grayscale replicated, alpha dropped);
  image region stays byte-for-byte identical to bar-off output
* Bar-off path is unchanged

Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
* Align docs heading with sibling "(Optional)" sections
* Add XZ-plane scale-bar read-back test locking per-plane dims and
  horizontal-spacing selection
* Document ConvertUInt8ToRgb's uint8 precondition; hoist loop-invariant
  stride locals out of writeSlice; minor const/reuse tidies in preflight

Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
* Bar sits one margin from the band's left edge, vertically centered;
  the length label renders inline to the right of the bar
* Degenerate narrow images clamp the bar start so it stays on-canvas
* Renderer and Write Image test probes updated to the new layout;
  filter docs describe the left-justified inline arrangement

Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
* Swaps all 12 ITKImageWriterFilter instances across 7 example
  pipelines to WriteImageFilter; every instance writes uint8 RGB PNGs,
  which Write Image supports natively without the ITK dependency
* Args carry the full parameters_version 5 key set with defaults so
  the pipelines load without missing-parameter warnings

Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
@imikejackson
imikejackson force-pushed the topic/write_images_color_table branch from df74cff to 01892d7 Compare July 16, 2026 18:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add ColorTable option to the "Write Image" filter (both of them) ENH: Filter to add a physical measurement bar to an image

1 participant