ir-refine-grid C++ → Python Parity Checklist
Audit of the legacy C++ ir-refine-grid tool (SCI trunk, mounted at /legacycode)
mapped against the Python RefineGridMosaic implementation in
nornir-imageregistration/nornir_imageregistration/local_distortion_correction.py.
Golden reference output:
RC2_4Square_Assembled/TEM/0690/TEM/Grid_Cel96_Mes8_sp4_Mes8_Thr0.5.mosaic
(parameters: -cell 96 -mesh 8 8 -sp 4 -displacement_threshold 0.5, -it default 10).
1. CLI and initialization (/legacycode/code/ir-tools/ir-refine-grid.cxx)
# |
C++ behavior (file:line) |
Python equivalent |
Parity |
|---|---|---|---|
1.1 |
Defaults: |
|
OK |
1.2 |
|
|
OK |
1.3 |
Tiles loaded via |
|
OK (shifts must be converted back to full-res before mesh update) |
1.4 |
|
|
OK |
1.5 |
|
|
Port (Python previously used overlap-fraction min_overlap and no median filter) |
Vertex index ordering matches: C++ i = col + row*mesh_cols (col fastest); Python
build_coords_array produces index = row*grid_dims[1] + col (col fastest).
2. Pass structure (refine_mosaic_mt, mosaic_refinement_common.hxx L1440–1803)
# |
C++ behavior |
Python target |
Parity |
|---|---|---|---|
2.1 |
Neighbors = all tiles whose mosaic-space bboxes intersect (L1511–1531); recomputed once before the pass loop |
per-pass neighbor list from |
Port |
2.2 |
Each pass: re-warp every tile in full into mosaic space using current grid transform ( |
|
Port |
2.3 |
Per tile i, per neighbor j: |
|
Port |
2.4 |
Blend: |
identical blend; replaces Python |
Port |
2.5 |
Apply only after all tiles measured: |
buffer per-tile shifts; |
Port |
2.6 |
Convergence metric: `avg = mean( |
sx |
+ |
2.7 |
Dual early stop: |
both conditions |
Port (Python only had threshold) |
2.8 |
No final lattice resample — the grid IS the transform; mosaic saved from updated grid |
skip final |
Port |
3. Measurement loop (calc_displacements, mosaic_refinement_common.hxx L224–395)
# |
C++ behavior |
Python target |
Parity |
|---|---|---|---|
3.1 |
Loops over all |
per-vertex loop over |
Port |
3.2 |
Mosaic-space measurement center: |
|
Port (replaces inverse-warp of FFT-cell centers) |
3.3 |
|
|
Port |
3.4 |
Prewarped path: |
extract aligned cells from the two prewarped images |
Port |
3.5 |
On success: |
same |
Port |
3.6 |
Ends with |
|
Port |
4. FFT cell preprocessing (grid_common.hxx / fft_common.cxx)
C++ chain for the prewarped path:
refine_one_point_fft (grid_common.hxx L1257) → refine_one_point_helper
(neighborhood extraction) → refine_one_point_fft (L820) → match_one_pair
(L269) → find_correlation (fft_common.cxx L467) → estimate_displacement.
# |
C++ behavior |
Python ( |
Parity |
|---|---|---|---|
4.1 |
Neighborhood: |
cells sliced from both prewarped images at the same mosaic window |
Port |
4.2 |
Pixels outside the image buffer or mask → value 0, mask 0 (L651–655, L667–671); no random-noise fill |
zero-fill outside valid region |
Accepted difference: Python keeps its validated random-noise fill for invalid pixels (avoids zero-edge correlation bias); area-ratio gate below limits exposure |
4.3 |
Per-cell area gate: |
identical fraction test on valid masks |
Port |
4.4 |
Skip if center not inside the fixed tile’s buffer (L616–621) |
skip if scaled center outside fixed warped image extent |
Port |
4.5 |
FFT: pad both to common size (equal already → no-op), low-pass filter |
|
Accepted difference: Python translate-path estimator (validated vs |
4.6 |
Peak handling: |
Python |
Accepted difference; the |
4.7 |
|
|
OK (sign convention verified) |
5. Regularization (regularize_displacements, mosaic_refinement_common.cxx L36–171)
# |
C++ behavior |
Python target |
Parity |
|---|---|---|---|
5.1 |
Median filter on |
|
Port |
5.2 |
Gap fill for vertices with |
faithful port including the radius-1 cap and offset pattern |
Port |
5.3 |
Gaussian blur on the entire |
|
Port |
5.4 |
Output: |
per-vertex |
Port |
5.5 |
NOT |
remove |
Port |
6. Removed/retired Python behaviors (mosaic refine path)
Overlap-subcell FFT measurement grid (
grid_dim × subregion_shapecells over the padded overlap ROI) — replaced by per-mesh-vertex measurement (3.1/3.2).SplitDisplacementsd/2 convention — replaced by1/(1+mass)blending (2.4)._filter_weighted_point_pair_updates(estimate_cutoff) — no C++ equivalent (5.5)._merge_weighted_point_pairs/_apply_point_pair_updates_to_grid_transformnearest-node incremental deltas — replaced by full-mesh vertex update (2.5).Final
_resample_transform_to_output_gridinRefineGridMosaic— C++ never resamples; tiles are already on the output lattice after pass 0 (2.8).
These functions remain available for unit tests and the STOS refine path.
7. Coordinate/unit conventions
Quantity |
C++ |
Python |
|---|---|---|
Axis order |
|
|
Mesh vertex source lattice |
|
|
Mesh vertex mosaic position |
|
|
Measured shift |
working-res mosaic pixels |
scaled peak → divide by |
Convergence |
working-res pixels |
working-res pixels (compute before unit conversion) |
|
working-res pixels (golden run: 0.5) |
same units |