StitchingInspector

class controllers.StitchingInspector

Bases: handle

STITCHINGINSPECTOR - Seam inspector: manual QC + fixing of a stitch.

Reviews the measured tile-pair seams worst-first and lets the user confirm, exclude, or (Phase C) fix them; fixes re-steer the global solve through high-weight user edges. Launched from the Stitching tool (inspectSeamsBtn) with the parent controllers.Stitching handle - the parent’s layout/edges/positions/tforms are the single source of truth and are mutated in place. Design + phasing: development/stitching/plan_inspector.md.

Seams are ranked by utils.stitch.scoreSeams (pixel NCC at the SOLVED positions - catches confidently-wrong measurements that solver residuals miss on chain graphs), pruned edges first.

Constructor Summary
StitchingInspector(mibModel, stitchController, options)

STITCHINGINSPECTOR - Constructor.

Syntax:
obj = controllers.StitchingInspector(mibModel, stitchController)
obj = controllers.StitchingInspector(mibModel, stitchController, struct('createView', false))
Input Arguments:
  • mibModel - handle to MibModel

  • stitchController - handle to the launching controllers.Stitching; must hold a measured edges set and solved positions

  • options (optional) - [struct] with field createView [logical]: false builds the inspector WITHOUT its window - the seams are scored and ranked and every review/fix method works, but nothing is rendered. Used by the unit tests; every widget access in this class is guarded by hasWidget(), so the review logic is the same code the GUI drives. Default true

Property Summary
autoBackup

which flicker image is visible (1 = tile i, 2 = tile j)

currentEdgeIdx

worst-first edge review order (indices into stitching.edges)

flickerState

[2x1] image handles on pairAxes for the flicker overlay ([] otherwise)

listener

handle to the view (StitchingInspectorGUI)

mibModel
pairImageHandles

what the correction readerFcn was built with looked like - method and, for ‘Re-exposure damage’, the positions its footprints were placed at; tileReader rebuilds the reader when the live correction no longer matches

pairStrip

cell (per edge) with the original automatic edge before the first user fix - Z / undoFixBtn restores it (in-memory only, not persisted)

pairZoom

line handle of the hover ROI box on pairAxes ([] until first shown)

ranking

handle to the parent controllers.Stitching (source of truth for layout / edges / positions / tforms / BatchOpt)

readerCachedFcn

shared LRU tile reader (utils.stitch.makeTileReader); built once by obj.tileReader(), never directly - see there

readerCorrectionStamp

true once the current right-button press has moved >= 3 screen px, i.e. turned into a pan (pairViewButtonDown); fillTileOrderMenu then leaves the menu empty so a pan does not end with a menu popping up

readerFcn

index (into stitching.edges) of the seam shown in the pair view

resolvePending
rightDragMoved

uicontextmenu of the pair view (tile order) - created in addCallbacks, set as the ContextMenu of pairAxes and of every pair image, and refilled by fillTileOrderMenu each time it opens

roiBoxHandle

true while Shift is held over the inspector - the pair-view cursor becomes the correlation ROI box and a click runs click-to-correlate

shiftDown

.active, .stage (1|2), .scale, .leftWidth, .gap, .clickA ([x y] in tile-i full-res pixels)

Type:

two-click landmark match state

stitching

cell array of listener handles

thumbScale

cell (per tile) of low-res greyscale thumbnails for the mini-map fused preview, jointly normalised to [0 1]; built lazily once by ensureTileThumbs ({} until then, {} forever if tiles are too big)

tileOrderMenu

is that tile resident? Used to put a progress dialog around the reads that will actually stall on disk

Type:

companion predicate of readerFcn

tileThumbs

struct .edgeIdx (the seam it belongs to), .xLim, .yLim - re-applied across re-renders of the same seam so nudges/drags keep the zoom; [] = fit to view. Selecting ANOTHER seam keeps the magnification and re-centres it on that pair’s overlap (renderPairView/carriedZoomOnNewSeam), rewriting .edgeIdx - only fitView_Callback (F) and a fix-mode switch clear it

Type:

wheel-zoom state of the pair view

twoClick

struct with the current pair view geometry (.bboxA = tile-i strip [rowStart rowEnd; colStart colEnd], .deltaYX) - maps clicks on the strip back to tile-i pixels; [] when no overlap is rendered

view

handle to MibModel

viewSlice

full-res pixels per thumbnail pixel (NaN until thumbs are built)

Method Summary
addCallbacks()

ADDCALLBACKS - Wire all view callbacks (called once from the constructor).

Syntax:
obj.addCallbacks()

CloseRequestFcn is set first so the window can always be closed even if a later wiring line errors. Widgets that may not exist in the mlapp yet are guarded with isfield so the controller runs against a partial GUI.

advanceToNextUnreviewed()

ADVANCETONEXTUNREVIEWED - Select the worst seam not yet reviewed.

Syntax:
obj.advanceToNextUnreviewed()

“Reviewed” = source ‘confirmed’ or ‘user’, or excluded (valid = false). When everything is reviewed, stays on the current seam.

applyUserFix(newOffsetYX, description, deferResolve)

APPLYUSERFIX - Write a user-fixed pair offset into the current edge.

Syntax:
obj.applyUserFix(newOffsetYX, description)
obj.applyUserFix(newOffsetYX, description, deferResolve)

The single write path shared by every fixing tool (drag, click-to-correlate, two-click match): backs up the original automatic edge once (for Z undo), replaces the measurement by the user’s offset with source = 'user' / quality = 1 - the solvers weight user edges at userEdgeWeight and never prune them - and re-solves globally unless deferred by the caller or auto-re-solve is off.

Input Arguments:
  • newOffsetYX - [1x2 or 1x3 double] pair offset [dy dx] (or [dy dx dz]) in the positions(j,:) - positions(i,:) convention

  • description - [char] short provenance text for the status line

  • deferResolve (optional) - [logical] skip the auto re-solve (default: false)

applyZBoundaryFix(deltaYX, description)

APPLYZBOUNDARYFIX - Set the per-slice mosaic correction at the viewed Z boundary.

Syntax:
obj.applyZBoundaryFix(deltaYX, description)

Fix-Z counterpart of applyUserFix(): instead of editing a seam, it records that every mosaic output slice >= z (the boundary on screen, viewSlice.sliceB) shifts in-plane by deltaYX relative to the slices below - “align slice z to slice z-1 and carry everything above along”. Stored in the parent’s zSliceFixes (one row per boundary, replaced on re-fix, dropped when the correction returns to zero), applied by planCanvas/the fusers at the next fuse, and saved in the project sidecar. The solver is NOT involved - tile positions are untouched.

Input Arguments:
  • deltaYX - [1x2 double] [dy dx] shift of slices >= z relative to the slices below

  • description - [char] human-readable source of the fix (status line)

autoResolveEnabled()

AUTORESOLVEENABLED - Re-solve automatically after each fix? Defaults to ON (the solve is milliseconds at inspector sizes) when the checkbox is absent from the mlapp.

boundaryDelta(zBoundary)

BOUNDARYDELTA - Current [dy dx] mosaic correction at slice boundary z (slices >= z relative to the ones below); [0 0] when none is stored in the parent’s zSliceFixes.

boundaryModeActive()

BOUNDARYMODEACTIVE - True when the pair view shows a mosaic Z BOUNDARY (Fix Z): the same tile at consecutive slices z-1 (cyan) vs z (magenta), fully overlapping - mostly white when the mosaic is Z-aligned. Fixes then edit the per-slice mosaic correction (applyZBoundaryFix), not a seam. renderPairView engages it by storing viewSlice.boundaryTile.

closeWindow()

CLOSEWINDOW - Close the inspector window and free its resources.

Syntax:
obj.closeWindow()
confirmSeam_Callback()

CONFIRMSEAM_CALLBACK - Mark the current seam as reviewed-OK and advance.

Syntax:
obj.confirmSeam_Callback()

Confirmation is bookkeeping only (source = 'confirmed'): the edge weight stays quality-based (design decision in plan_inspector.md). A user-fixed edge keeps its 'user' provenance. Afterwards the review jumps to the next worst unreviewed seam.

correlateAtPoint(stripPointXY)

CORRELATEATPOINT - Click-to-correlate: snap the pair offset from a click.

Syntax:
obj.correlateAtPoint(stripPointXY)

The “human picks WHERE, machine finds EXACTLY” tool: maps a click on the pair-view strip back to tile-i pixels and runs utils.stitch.localCorrelate() (ROI normxcorr2 around the click, searched in tile j near the current offset). A confident peak is applied as a user fix - or, in the Fix-Z boundary view (both layers = the SAME tile at slices z-1 / z), as the per-slice mosaic correction via applyZBoundaryFix(). A weak/ambiguous match only reports why and never moves anything. ROI size and search radius come from ROIsizeSpinner / SearchradiusSpinner when present (defaults 128 / 64 px).

Input Arguments:
  • stripPointXY - [1x2 double] click [x y] in pair-view strip coordinates (the rendered overlap region, pixel 1 = strip origin)

currentDz(edgeIdx)

CURRENTDZ - Current z-offset of an edge’s pair (slices), same display convention as currentOffsetYX: user-fixed edges show the offset the user set, others the solved position difference.

currentLayerTiles()

CURRENTLAYERTILES - Tile indices of the Z-layer the mini-map currently draws: the current seam’s layer (its lower tile for a cross-layer pair), or the lowest layer before anything is selected. Shared by renderMiniMap (what to draw) and jumpToTile (what a mini-map click can land on) so the two never drift apart.

currentOffsetYX(edgeIdx)

CURRENTOFFSETYX - Current [dy dx] of an edge’s pair. User-fixed edges display at the offset the user set (measured), so a fix is visible before (and independent of) the next re-solve; everything else shows the solved position difference.

currentTileStack()

CURRENTTILESTACK - The Overwrite drawing order in force, bottom first.

Syntax:
stack = obj.currentTileStack()

The user’s explicit order (stitching.tileStack) if one was set here, otherwise the default utils.stitch.tileDrawOrder() derives - the SAME function the fusers use, so the tile the pair view colours as “on top” is the one Stitch keeps.

Output Arguments:
  • stack - [1 x N double] tile indices, bottom first

dataValid()

DATAVALID - True while the parent still holds a reviewable edge/position set (a layout rebuild in the Stitching window invalidates the inspector’s session).

edgeAtMiniMapPoint(point)

EDGEATMINIMAPPOINT - Seam (edge index) nearest a mini-map click.

A tile usually touches more than one seam (a grid tile has a neighbour on two, three or four sides), so “jump to this tile’s worst seam” can only ever reach the single worst one - clicking anywhere else on that tile, hoping to land on a DIFFERENT one of its seams, always lands back on the same worst seam instead. Fix: give every seam of the current layer (the subset visibleRanking shows) a location - the midpoint of its two tiles’ overlap rectangle, i.e. where the shared image content actually is - and return whichever seam’s location is nearest the click. Returns [] when there is nothing to match (no seams in this fix mode, or none in the current layer).

ensureTileThumbs()

ENSURETILETHUMBS - Build the per-tile mini-map thumbnails once (lazy).

Syntax:
obj.ensureTileThumbs()

Reads every tile through the shared LRU reader, flattens it for display (first channel, mean over depth) and downsamples it so the whole mosaic spans ~1000 thumbnail pixels; the thumbs are jointly normalised to [0 1]. renderMiniMap composites them at the CURRENT solved positions on every redraw (cheap), so a gross misplacement is visible in the actual image content, not just the score colouring. Built once per session - tiles never change while the inspector is open (dataValid guards layout rebuilds). Datasets whose tiles sum to more than ~1.5 G full-res pixels skip the thumbnail (patches only) instead of stalling the open; any read failure declines the same way.

excludeSeam_Callback()

EXCLUDESEAM_CALLBACK - Exclude the current seam from the solve (or re-include).

Syntax:
obj.excludeSeam_Callback()

Toggles valid on the current edge. Excluding also resets the provenance to 'auto' - a user fix that gets excluded is withdrawn (user edges are otherwise never pruned by the solver). The nominal springs hold the pair together once its measurement is excluded.

The EDGE is the single source of truth for the state: this never reads the button, so the X key and the button behave identically, and controllers.StitchingInspector.refreshExcludeButton() (called from updateWidgets) pushes the resulting state back onto the widget.

fillTileOrderMenu()

FILLTILEORDERMENU - Fill the pair view’s right-click menu for the seam on screen.

Syntax:
obj.fillTileOrderMenu()

The ContextMenuOpeningFcn of obj.tileOrderMenu (set in addCallbacks). One submenu per tile of the seam, the tile on top first, each with Move to top / Move up / Move down / Move to bottom (tileOrder_Callback()); a move that would leave the order unchanged (utils.stitch.moveInTileStack() returns the same stack) is greyed out rather than hidden, so the four entries keep their place.

The menu is left EMPTY - which keeps it from appearing - when there is nothing to offer:

  • the right press became a PAN (obj.rightDragMoved, set by pairViewButtonDown). The menu opens when the right button is RELEASED, so without this every pan would end with a menu popping up;

  • the Fix Z boundary view, whose two images are one tile;

  • a two-click match in progress, or no seam to act on.

Note

Why a real ContextMenu: opening a uicontextmenu by hand with open(menu, x, y) from the right-button-up callback was the first implementation, and on Windows nothing ever appears - the menu is built and opened, then dismissed by the window’s own right-click handling.

See also utils.stitch.moveInTileStack, controllers.StitchingInspector.tileOrder_Callback

fitView_Callback()

FITVIEW_CALLBACK - Reset the pair-view zoom to fit everything rendered.

Syntax:
obj.fitView_Callback()

Clears the wheel-zoom state (scrollWheel_Callback()) and restores tight limits around whatever the pair view currently shows - the composited pair, the flicker stack or the two-click side-by-side - then widens the short side to the axes’ shape so the fit fills the reserved area (StitchingInspector.pairAxesFillLimits()). Wired to fitViewBtn and the F key; it is also the way to re-fill the view after the window has been resized.

fixDz(edgeIdx)

FIXDZ - dz component written with any seam fix: always the current dz - seam fixes never change the Z relation. (Z corrections are per-slice mosaic shifts, applied through applyZBoundaryFix in Fix-Z mode, not edge dz edits.)

fixMode()

FIXMODE - What a fix edits on 3D pairs: ‘xy’ (default; the in-plane offset at the aligned slices) or ‘z’ (match slices across the Z boundary - fixModeDropdown, guarded).

hasWidget(widgetName)

HASWIDGET - Is this widget available to write to?

Syntax:
tf = obj.hasWidget('seamTable')

False both when the mlapp does not (yet) carry the widget - most of the inspector’s UI is optional, see mlapp_widgets.md - and when the controller runs without a view at all (headless construction). Every widget access in this class goes through it, so the review logic runs identically in both cases.

Input Arguments:
  • widgetName - [char] handle name in obj.view.handles

helpBtn_Callback()

HELPBTN_CALLBACK - Open the online help page for the Stitching Inspector tool.

Syntax:
obj.helpBtn_Callback()
jumpToTile(tileIdx)

JUMPTOTILE - Open the worst incident seam of a tile.

Syntax:
obj.jumpToTile(tileIdx)

Programmatic/headless entry point (e.g. tests). The mini-map itself does NOT call this - a click there goes through miniMapButtonDown()/edgeAtMiniMapPoint(), which resolves to whichever SEAM is nearest the click point, since a tile usually touches more than one seam and “its worst one” is not always the one a click was aimed at.

Input Arguments:
  • tileIdx - [double] tile index

keyPress_Callback(evnt)

KEYPRESS_CALLBACK - Keyboard-first review loop.

Syntax:
obj.keyPress_Callback(evnt)
Shortcuts:
  • Space - flicker A/B (Flicker overlay mode)

  • Enter - confirm current seam + jump to next worst unreviewed

  • X - exclude / re-include current seam

  • Down / Up - next / previous seam in the ranking, i.e. one row down / up the seam table

  • Q / W - BROWSE Z, previous / next, exactly like the main MIB (Shift = ±5); ALWAYS view-only, in BOTH fix modes. Fix XY: the dz-aligned slice pair; Fix Z: the mosaic Z boundary (both consecutive slices step together). Keyboard nudging is disabled entirely - offsets are edited by mouse only (drag / Shift+click / two-click)

  • Z - undo the fix on the current seam (restore the auto edge); in Fix Z, remove the boundary correction on screen

  • F - fit the pair view (reset the mouse-wheel zoom)

Input Arguments:
  • evnt - KeyData from WindowKeyPressFcn

keyRelease_Callback(evnt)

KEYRELEASE_CALLBACK - Dismiss the Shift ROI-box state on Shift release.

Syntax:
obj.keyRelease_Callback(evnt)

Releasing Shift hides the hover ROI box and restores the arrow pointer.

Input Arguments:
  • evnt - KeyData from WindowKeyReleaseFcn

keyShortcutsBtn_Callback()

KEYSHORTCUTSBTN_CALLBACK - Show all mouse, Shift+mouse, and keyboard controls available in the pair view / seam table as a single reference list.

Syntax:
obj.keyShortcutsBtn_Callback()
miniMapButtonDown()

MINIMAPBUTTONDOWN - Mini-map click: jump the review to the nearest seam.

Syntax:
obj.miniMapButtonDown()

Wired on miniMapAxes itself (not per tile patch - see renderMiniMap()), so the click point comes straight from miniMapAxes.CurrentPoint, exactly like scrollWheel_Callback() and pairViewButtonDown() already read the pair-view axes. This sidesteps any doubt about what a PATCH’s ButtonDownFcn hit event actually carries (unlike the image objects the pair view uses) and about which patch - of possibly several overlapping ones - would receive it.

The nearest seam to that point (edgeAtMiniMapPoint()) is selected directly; nothing here falls back to “the clicked tile’s worst seam”.

pairAxesFillLimits(xLim, yLim)

PAIRAXESFILLLIMITS - Widen a data window to the pair axes’ shape.

Syntax:
[xLim, yLim] = obj.pairAxesFillLimits()
[xLim, yLim] = obj.pairAxesFillLimits(xLim, yLim)

axis image gives tight limits at 1:1 pixels, so a tall pair (two tiles stacked vertically) drew as a narrow column with the rest of the reserved grid cell empty - and zooming in only made the column taller. This grows the SHORTER side of the requested window until its aspect matches the axes rectangle on screen, so the composite fills the whole cell; the extra span is context around the tiles, never a distortion (the data aspect stays 1:1) and never a crop (the window only ever grows).

InnerPosition is the region available for the plot box - it excludes the title but is NOT shrunk by the letterbox the aspect constraint applies, so reading it here is not circular.

The window is not re-fitted when the user resizes the inspector: the next render, wheel zoom or F picks the new shape up.

Input Arguments:
  • xLim, yLim - [1x2] window to widen; omitted = the axes’ current limits

Return Values:
  • xLim, yLim - [1x2] widened window; the caller writes it to the axes

pairHasDepth(edgeIdx)

PAIRHASDEPTH - True when a z-offset is meaningful for this pair: a cross-layer edge, or either tile is a z-stack.

pairViewButtonDown(evnt)

PAIRVIEWBUTTONDOWN - Click/drag state machine of the pair view.

Syntax:
obj.pairViewButtonDown(evnt)

A Shift+click (SelectionType = 'extend'; the hover ROI box from pairViewMotion() previews the region) triggers click-to-correlate at that spot (correlateAtPoint()) - the automated fine-tune. A plain DRAG switches the axes to a live two-layer overlay - tile i as a grey background, tile j at 50% alpha following the pointer (per the plan, no imfuse recompute per mouse event) - and applies the released delta as a user fix. A plain click without movement does nothing (stray clicks must never move tiles). In two-click landmark mode the point is routed to the landmark collector instead. A right-click drag (SelectionType = 'alt') pans the view instead - it never edits alignment, so it works in every mode (including two-click and Fix Z) and is never mistaken for a tile fix.

A right-click released without moving opens the tile-order menu (obj.tileOrderMenu, the pair view’s ContextMenu, filled by fillTileOrderMenu()). All this function does for it is record whether the right press turned into a pan (obj.rightDragMoved, >= 3 screen pixels), so the menu can stay empty - and therefore closed - after one.

Input Arguments:
  • evnt - hit event from an image ButtonDownFcn (IntersectionPoint in pairAxes data coordinates)

pairViewMotion()

PAIRVIEWMOTION - Hover handler: with Shift held, the cursor becomes the ROI box.

Syntax:
obj.pairViewMotion()

Wired as the figure’s persistent WindowButtonMotionFcn. While Shift is held (tracked by keyPress_Callback() / keyRelease_Callback()) a yellow box of exactly ROIsizeSpinner full-res pixels follows the cursor over the pair view - a live preview of the region that Shift+click hands to click-to-correlate (correlateAtPoint()). The box is click-transparent (PickableParts = 'none') so the click lands on the image beneath it. Hidden when Shift is up or the cursor leaves the axes.

progressParent()

PROGRESSPARENT - Figure to anchor a progress dialog to.

Syntax:
figureHandle = obj.progressParent()

The inspector window once it is VISIBLE (uiprogressdlg refuses an invisible figure, which the window still is while the constructor scores the seams), otherwise the parent Stitching window, otherwise [] - headless, and the caller then skips the progress bar entirely.

refreshExcludeButton()

REFRESHEXCLUDEBUTTON - Show the current seam’s exclusion state on the Exclude button. The edge is the single source of truth (the X key and a table reload change it too), so the button is always pushed FROM the edge, never read from.

Works with either widget type: an App Designer STATE button (uibutton(...,'state'), has a Value) shows the state as pressed + red, a plain push button only as red - so the mlapp can be upgraded without touching this code.

Syntax:
obj.refreshExcludeButton()
renderMiniMap()

RENDERMINIMAP - Layout overview: tiles coloured by their worst seam score.

Syntax:
obj.renderMiniMap()

Each tile is a patch at its SOLVED position, coloured by the worst score of its incident edges (green → red; grey when all incident edges are excluded). The current pair’s tiles get a bold blue outline. Clicking jumps the review to whichever SEAM of the current layer is nearest the click point - not just the clicked tile’s single worst seam, and not just whichever patch happens to be drawn on top of an overlap - see controllers.StitchingInspector.edgeAtMiniMapPoint().

Only the tiles of ONE Z-layer are drawn - the layer of the current seam (the lower tile of a cross-layer pair). A multi-layer mosaic stacks every layer at the same XY, so drawing them all overlaps the boxes and merges the labels; restricting to the current layer keeps the overview readable and it follows the selected seam. The layer is named in the axes title when there is more than one.

When the per-tile thumbnails are available (ensureTileThumbs()), a low-res FUSED preview is composited at the solved positions behind the patches (which then drop to a light tint), so a gross misplacement shows in the actual image content - re-composited on every redraw, so it tracks each re-solve.

renderPairView()

RENDERPAIRVIEW - Composite the current seam’s COMPLETE tile pair at its offset.

Syntax:
obj.renderPairView()

Renders BOTH FULL TILES placed at the current pair offset (downsampled when the union exceeds ~1400 px - the axes stay in full-resolution tile-i coordinates, so clicks and drags need no unit conversion), according to overlayModeDropdown.

3D pairs are shown as ONE SLICE PAIR (named in the title), selected per fixMode:

  • Fix XY (default) - tile i against tile j, aligned by the current dz (slice a vs a − dz); Q/W browse a through the overlap slab (view-only). A Z misalignment ghosts exactly like an XY one.

  • Fix Z - the mosaic Z-BOUNDARY view: ONE tile (the seam’s tile with Z slices) at consecutive slices z-1 (cyan) vs z (magenta), fully overlapping - mostly WHITE when the mosaic is Z-aligned, and the magenta slice is drawn at the boundary’s current correction. Q/W move the boundary; drag / Shift+click align slice z to slice z-1 and hand the offset to applyZBoundaryFix(), which shifts EVERY mosaic slice >= z. Browsing alone never changes anything.

Overlay modes:

  • Falsecolor (cyan/magenta) - the tile on top (the one 'Overwrite' keeps, per currentTileStack()) magenta, the other cyan: aligned structures add up to WHITE, misaligned ones split into cyan/magenta ghosts

  • Falsecolor (green/red) - the same with the tile on top red and the other green; aligned structures come out YELLOW

  • Preview final - the pair as Stitch will fuse it, in grey: the Stitching window’s blend mode and canvas colour, the drawing order, the corrected pixels (previewFusedPair mirrors utils.stitch.fuseSliceComposite at display scale). The way to judge which tile should be on top

  • Flicker - both tiles stacked; spacebar toggles which is visible

  • Checkerboard / Difference - imfuse composites on the union canvas

The colour follows the DRAWING ORDER, not the tile’s role in the edge: before the order became editable magenta was always tile j, the one a drag moves. A drag still moves tile j, so the title says which tile that is. In the Fix Z boundary view slice z (the one a fix moves) takes the top colour.

The title is a plain colour legend (Cyan: tile 2; Magenta: tile 4 (on top)) with, on 3D pairs, a second line naming the shown slices; the offset label shows the current solved [dy dx] vs the measured one, the seam score and the measurement quality. The drawing order itself is edited from the right-click menu (pairViewButtonDown -> tileOrder_Callback()).

resolveBtn_Callback()

RESOLVEBTN_CALLBACK - Re-run the global solve with the edited edge set.

Syntax:
obj.resolveBtn_Callback()

Delegates to the parent’s optimizePositions_Callback (same solver dispatch, canvas re-plan and quality-chip update as the Stitching window), then re-scores every seam at the NEW positions and re-ranks the review.

restoreSessionSettings()

RESTORESESSIONSETTINGS - Reopen the dialog on the settings it was closed with.

Syntax:
obj.restoreSessionSettings()

Reads mibModel.sessionSettings.stitchingInspector (see storeSessionSettings()). Nothing stored - the first opening of the session - leaves the mlapp / addCallbacks defaults, so the overlay starts on Falsecolor (cyan/magenta). Each value is applied only if the widget would accept it (a dropdown item that exists, a spinner value inside its limits), so a setting from an older build cannot break the window. Fix Z is restored only when some tile has Z slices: on a 2D mosaic that mode cannot engage, and restoring it would open the window on the “Fix Z needs Z slices” dialog.

scoreAndRank()

SCOREANDRANK - Score every seam at the current solved positions and rank.

Syntax:
obj.scoreAndRank()

Makes sure the parent’s edge set carries seamScore for the CURRENT placement, then stores the worst-first review order in obj.ranking. The LRU tile reader is created once and shared with the pair-view rendering.

Scoring goes through controllers.Stitching.ensureSeamScores(), so the overlaps are read only if nothing already scored them at these positions. Both routes into the inspector arrive pre-scored in the normal case - opening it after Optimize positions or after importing a vendor stitch, and resolveBtn_Callback(), which re-solves through the parent before calling here - and re-reading every overlap is the slowest thing in the tool short of fusing. The RANKING is recomputed unconditionally: it is a pure function of the edge fields (utils.stitch.rankSeams()) and costs nothing, so it still follows an edge excluded since the last pass.

scrollWheel_Callback(evnt)

SCROLLWHEEL_CALLBACK - Mouse wheel: zoom the pair view / resize the ROI box.

Syntax:
obj.scrollWheel_Callback(evnt)

While Shift is held (the hover ROI box is showing), the wheel adjusts ROIsizeSpinner - scroll up = larger box - clamped to the spinner’s limits, and the box under the cursor resizes live.

Without Shift, the wheel ZOOMS the pair view about the cursor (scroll up = zoom in), keeping the window shaped like the axes so it fills the whole reserved cell (StitchingInspector.pairAxesFillLimits()). Zooming out beyond the rendered extent IN BOTH DIRECTIONS snaps back to fit; the zoom survives re-renders of the same seam (nudges, drags, fixes - see renderPairView()) and is reset by fitView_Callback() (F) or by selecting another seam. Wheel events outside the pair view are ignored.

Input Arguments:
  • evnt - ScrollWheelData from WindowScrollWheelFcn (VerticalScrollCount > 0 = scroll down)

seamTableSelection_Callback(evnt)

SEAMTABLESELECTION_CALLBACK - Open the seam picked in the ranked table.

Syntax:
obj.seamTableSelection_Callback(evnt)
Input Arguments:
  • evnt - SelectionChanged event data from the uitable (row selection)

selectSeam(edgeIdx)

SELECTSEAM - Make one seam current: render its pair view + sync selection.

Syntax:
obj.selectSeam(edgeIdx)
Input Arguments:
  • edgeIdx - [double] index into obj.stitching.edges

setStatus(text)

SETSTATUS - Write to the status label (no-op without the widget).

storeSessionSettings()

STORESESSIONSETTINGS - Remember the dialog’s settings for its next opening.

Syntax:
obj.storeSessionSettings()

Written by closeWindow() into mibModel.sessionSettings.stitchingInspector and read back by restoreSessionSettings(), like the Stitching dialog’s own sessionSettings.stitching. On close rather than per change: sessionSettings lives in RAM, so an earlier write survives nothing this one does not. Covers overlay mode, fix mode, ROI size, search radius and auto re-solve; the seam, zoom and slice are properties of the mosaic being reviewed, not settings, and are not carried over.

tileOrder_Callback(tileIdx, action)

TILEORDER_CALLBACK - Move a tile of the current seam in the Overwrite drawing order.

Syntax:
obj.tileOrder_Callback(tileIdx, action)

Called from the pair view’s right-click menu (built by pairViewButtonDown when a right-click is released without dragging - a right DRAG still pans). Where tiles overlap, 'Overwrite' keeps the tile drawn last. By default that order is derived (utils.stitch.tileDrawOrder(): the tile imaged first with a re-exposure damage model, otherwise the highest index); this is where the user overrides it. The first move freezes the order in force into stitching.tileStack and edits that from then on, so the default can no longer shift underneath a choice the user made.

The order is used by Stitch and saved in the project; it changes no position, edge or seam score, so nothing is re-solved or re-scored. It matters only to 'Overwrite' - the other blend modes do not depend on the order - which the status line points out when another mode is selected.

Input Arguments:

See also utils.stitch.moveInTileStack, utils.stitch.tileDrawOrder

tileReader()

TILEREADER - The inspector’s one shared LRU tile reader.

Syntax:
readerFcn = obj.tileReader()

Every place that reads pixels goes through this - the pair view, the mini-map thumbnails, the seam scoring, the click-to-correlate fixes - so they share ONE cache. Built lazily on first use and kept for the session; obj.readerCachedFcn comes with it.

It carries the intensity correction. The inspector used to build its reader with makeTileReader(layout) in four separate places, none of them passing one, so with a correction selected the inspector reviewed - and scored - different pixels from the ones the mosaic is measured and fused on. That is exactly the split utils.stitch.makeTileReader exists to prevent.

It is rebuilt when that correction changes. Every other method is fixed for the session, but 'Re-exposure damage' is placed at the solved positions, so a re-solve moves it. A reader kept from before would review the mosaic with the damage patches where the tiles used to be. The rebuild drops the tile cache, so with that method the first read after a re-solve decodes from disk again.

Output Arguments:
tilesAreResident(tileIndices)

TILESARERESIDENT - Would reading these tiles return immediately?

Syntax:
tf = obj.tilesAreResident([edge.i, edge.j])

False (the pessimistic answer) whenever the reader has not been built yet or offers no cache predicate, so a caller that gates a progress dialog on this errs towards showing one.

twoClickBtn_Callback()

TWOCLICKBTN_CALLBACK - Toggle the two-click landmark match mode.

Syntax:
obj.twoClickBtn_Callback()

For offsets too wrong for any search radius (e.g. a tile a whole texture period off): renders BOTH FULL TILES side by side (downsampled when larger than ~1024 px) and collects one click on the same landmark in each - the click difference IS the coarse offset, which a small-radius utils.stitch.localCorrelate() then sharpens. Clicks are routed here by pairViewButtonDown(); twoClickHandlePoint() applies the fix. Pressing the button again (or any re-render / navigation) cancels the mode.

twoClickHandlePoint(pointXY)

TWOCLICKHANDLEPOINT - Collect the two landmark clicks and apply the match.

Syntax:
obj.twoClickHandlePoint(pointXY)

Stage 1 stores the landmark click in tile i (left image); stage 2 takes the same landmark in tile j (right image) - the click difference is the coarse pair offset (dy = rowA - rowB, the pos_j - pos_i convention), then utils.stitch.localCorrelate() refines it within a small, scale-aware radius. An unconfident refinement falls back to the coarse offset (it is the user’s explicit statement) instead of being dropped.

Input Arguments:
  • pointXY - [1x2 double] click [x y] in the side-by-side view’s display coordinates (from pairViewButtonDown())

undoFix_Callback()

UNDOFIX_CALLBACK - Restore the current seam’s original automatic edge.

Syntax:
obj.undoFix_Callback()

Undoes ALL fixes applied to the current seam this session (Z or undoFixBtn): the first applyUserFix() on an edge backs up the original automatic measurement, and this restores it - measurement, quality, validity, transform and provenance alike. In-memory only; a saved project keeps whatever state was current at save time. In the Fix-Z boundary view it instead removes the per-slice mosaic correction at the boundary on screen.

updateWidgets()

UPDATEWIDGETS - Refresh the seam table, mini-map and status from the edges.

Syntax:
obj.updateWidgets()

Table rows follow obj.visibleRanking (worst first, filtered to the current fix mode: in-plane x/y seams in Fix XY, cross-layer z seams in Fix Z). Row background colour encodes the seam score (red → green); the mini-map is redrawn to match.

visibleRanking()

VISIBLERANKING - obj.ranking filtered to the seams the current fixMode edits: the IN-PLANE seams (x/y, tiles side by side or stacked in the same Z-layer) in Fix XY, the CROSS-LAYER seams (z, tiles in adjacent Z-layers) in Fix Z. The seam table and every seam-to-seam navigation (table click, Up/Down, resolve, advance, mini-map jump, the initial pick) follow this subset, so the table never mixes in-plane and cross-layer rows - they read on different axes and made the combined list confusing. A 2D dataset has only in-plane seams, so Fix XY shows them all and Fix Z is empty.