Skip to content

Add stitch refinement and raw-tile illumination correction worker - #174

Merged
arjunrajlab merged 4 commits into
masterfrom
codex/stitch-refinement-worker
Sep 1, 2026
Merged

Add stitch refinement and raw-tile illumination correction worker#174
arjunrajlab merged 4 commits into
masterfrom
codex/stitch-refinement-worker

Conversation

@arjunrajlab

Copy link
Copy Markdown
Collaborator

Summary

  • add a CPU-routed image-processing worker that resolves a composite's original ND2 through multi-source2.json
  • refine translations only with metadata-seeded coarse/fine NCC, reject pairs below 0.5, and solve confident constraints globally with a zero-mean shift
  • fit the supplied raw-tile overlap-DCT flat field per channel, optionally with regularized per-position gains
  • write corrected raw TIFF pages with explicit C/Z/T frame metadata, stream them through large_image_converter, and upload a new pyramidal TIFF with complete Girder provenance
  • add 15 Docker regression tests, Compose registration, worker documentation, registry entry, and a follow-up TODO for the shared image-processing base's pyvips/libvips mismatch

This branch starts fresh from master as requested. It implements the raw-ND2 workflow and intentionally does not implement the stitched-image/light-mode fallback from #172; no frontend files or compositing geometry were changed.

Review fixes included

  • avoid GirderClient's infinite limit=0 pagination path
  • preserve output C/Z/T axes by describing every scratch TIFF page explicitly
  • pin and assert the matching bundled pyvips/libvips wheel after reproducing a converter segmentation fault
  • add a real multi-source-to-pyramid conversion regression
  • reject stale/missing interface values before heavy work and re-raise after sendError
  • surface both the annotation-coordinate warning and the measured >16 px outer-boundary warning

Validation

  • docker run --rm annotations/illumination_correction:test15 passed
  • flake8 --ignore=E203,E501,W503 ... — passed
  • python3 -m py_compile workers/annotations/illumination_correction/*.py — passed
  • git diff --check — passed
  • final image labels inspected: isGPUWorker=false

Well_2 end to end:

  • uploaded new 3,073,098,448-byte pyramidal TIFF item 6a94a3b2d233ea7fbc4329b0
  • output metadata: 6,586 × 6,582 px, 5 channels × 6 Z planes = 30 frames, 5 pyramid levels
  • 84/84 adjacent pairs accepted; maximum solved residual 1.414 px
  • P24→P31: predicted (0, 920), measured (-4, 930), NCC 0.933
  • exact before/after region confirms the P24/P31 horizontal seam is visually gone
  • fresh NimbusImage page load listed, selected, and rendered the new image from the existing large-image dropdown

Coordinate-stability note

The validated Well_2 measurements expose a conflict in the written acceptance criteria: satisfying all 84 measured edges to ≤1.414 px accumulates to a maximum per-position shift of 33 px and outer-bound changes [-29, -32, +28, +20] px. It is mathematically impossible to preserve all four bounds within ±16 px while retaining the specified ≤2 px edge residual for this dataset. The worker therefore records the full values and emits a prominent warning rather than discarding the validated alignment; existing annotations are not moved.

@arjunrajlab

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-01T01:25:13.994198Z f06e28f Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5a6d4002fc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +183 to +184
position_index = frame // frames_per_position
source_position_indices.append(position_index)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Map frames using explicit position-axis metadata

For an ND2 whose flattened sequence is time-major (or otherwise not P-major), frame // frames_per_position groups frames from different stage positions together; the subsequent consistency check then rejects a valid P×T composite, or associates frames with the wrong translation if their coordinates happen to match. The source records expose axis metadata such as xySet, and the ND2 supplies loop_indices, so derive and validate the position from those explicit indices rather than assuming a P→T→Z→C flattening order. This is especially important because the worker documentation advertises preservation of P×T data rather than limiting support to P-major files.

AGENTS.md reference: AGENTS.md:L49-L57

Useful? React with 👍 / 👎.

@arjunrajlab

Copy link
Copy Markdown
Collaborator Author

Fixed in f9d97f8.

The parser now validates the explicit P×T×Z loop_indices from the ND2 and expands those position values across the C-fastest large-image frames. It no longer infers P using frame // (T×Z×C). We intentionally do not use xySet, since compositing resets it to zero.

Added a time-major P×T×C regression test using TDD. The branch-wide audit found no other production instances of flattened frame-to-position inference; one P-major test fixture was rewritten. All 19 Docker tests, flake8, the real Well_2 metadata check, and GitHub CI pass.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f9d97f8afe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

)
stage_positions = []
for position in range(positions):
position_stack = np.asarray(raw_file.asarray(position))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Read only the training time point from ND2

For a time-lapse ND2, raw_file.asarray(position) materializes the entire (T, Z, C, Y, X) stack even though line 371 immediately discards every time point except the first. Memory therefore scales with the full time series per position; a 100-time-point, 6-Z, 5-channel 2048×2048 uint16 acquisition requires roughly 25 GB just for this temporary array and can OOM before correction begins. Read only the T=0 sequences needed for the reference and model planes instead.

Useful? React with 👍 / 👎.

Comment on lines +211 to +215
output_positions = (
refinement.positions
if settings["refine"]
else np.rint(layout.positions).astype(np.int64)
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve fractional positions when refinement is disabled

When a deployed multi-source document contains fractional x or y translations, clearing Refine stitch positions still rounds every position here, after which corrected_source_document() writes the rounded integers. The supposedly unrefined output can therefore move each tile by up to half a pixel and no longer retains the deployed geometry promised by the worker documentation; preserve the original translations without integer conversion in this path.

AGENTS.md reference: AGENTS.md:L49-L57

Useful? React with 👍 / 👎.

@arjunrajlab

Copy link
Copy Markdown
Collaborator Author

Fixed both findings in f06e28f using TDD.

T=0 training memory: load_training_data() now validates the ND2 P×T×Z loop map and reads only the T=0 P×Z sequences with read_frame(), one camera frame at a time. It incrementally builds the reference max projection and fits the home-Z model plane without materializing the full time series. A time-major T=3 regression forbids asarray() and verifies that only T=0 sequences are read.

Fractional geometry: clearing refinement now copies the deployed translations without rint() or integer conversion, and corrected_source_document() preserves Python numeric scalars, including fractional x/y values.

Branch audits: the full-stack-read pattern had no other production instances. The geometry-coercion sweep found and fixed the downstream x/y int() casts; remaining rounding is intentional for enabled refinement, correlation coordinates, or uint16 pixels.

Verification: 22/22 Docker tests, flake8, GitHub package-tests, worker image 1.0.3 with CPU routing, and a read-only Well_2 integration check (49 reference tiles; all 1,470 sources mapped to all 49 positions).

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: f06e28f8a4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@arjunrajlab
arjunrajlab merged commit 8de303f into master Sep 1, 2026
1 check passed
arjunrajlab added a commit to arjunrajlaboratory/NimbusImageGitBook that referenced this pull request Sep 1, 2026
…analysis (#24)

* Document Stitch Refinement + Illumination Correction tool

Adds documentation for the new image-processing worker that refines tile
positions and corrects raw-tile illumination on composited .nd2 datasets.

- image-processing.md: new section covering the Composite upload
  prerequisite, workflow, parameters, technical details, tuning, and
  limitations (including that existing objects are not moved)
- images-datasets-and-collections/README.md and file-formats.md:
  cross-references from the compositing/.nd2 guidance
- new-features.md: August 2026 changelog entry

Source: arjunrajlaboratory/ImageAnalysisProject#174

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Document August 2026 features: connections, tracking, analysis, and fixes

Documents the August work across both repos, and fixes several
pre-existing defects found along the way.

New features:
- tools-for-connecting-objects.md: new "Browsing and editing connections"
  section — Connections tab (Flat / By track), scope selector, deletion,
  Connect selected, track ID property labeling with staleness badges,
  track-metric filters, and dangling-connection cleanup
- analysis-plots-and-gating.md (new page): Analysis panel, lasso gating,
  chained gating strategies, server-side gating at scale
- time-lapse-mode.md: Time Lapse palette moved out of the Navigator,
  track coloring, "N tracks · M links" readout, clickable connection lines
- interacting-with-objects.md: Measurements tab, chip strip, and
  Color by Property
- large-annotation-datasets.md: zoomed-out raster overview and the
  active-constraint cue on the count indicator

Worker changes:
- Cellpose-SAM Diameter corrected to default 30, range 10-200
- `all` accepted in batch coordinate and Crop range fields

Screenshots (captured live against localhost:5173):
- timelapse-mode-panel.png replaced; the old shot showed the controls
  inside the Navigator, which contradicted the new text
- connections-by-track.png and analysis-scatter-plot.png added

Pre-existing fixes:
- three broken relative links (../documentation/... from inside documentation/)
- stray drafting sentence left in tools-for-connecting-objects.md

Sources: NimbusImage #1298 #1302 #1311 #1312 #1327 #1332 #1333 #1339
#1340 #1341; ImageAnalysisProject #156 #170 #171

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant