Stitching

class controllers.Stitching

Bases: handle

STITCHING - Controller for the Image Stitching tool.

Assembles a mosaic from overlapping 2D/3D tile images using a grid dialog, a position file, or a MIB2 filename pattern. Registration is performed by pairwise phase correlation + global weighted least-squares optimisation (MIST/BigStitcher approach). Fusion supports in-memory (Standard dataset) and streaming OME-Zarr3 (BigData) outputs.

Available from Ribbon -> Dataset -> Stitching.

Constructor Summary
Stitching(mibModel, varargin)

STITCHING - Constructor.

Syntax:
obj = controllers.Stitching(mibModel)
obj = controllers.Stitching(mibModel, BatchOpt)
obj = controllers.Stitching(mibModel, NaN)
Input Arguments:
  • mibModel - handle to MibModel

  • varargin{1} (optional) - BatchOpt struct (batch run), or NaN (return BatchOpt to mibBatchController)

Property Summary
BatchOpt

cell array of listener handles

automaticOptions

K-by-3 double - per-slice mosaic corrections [z dy dx] from the inspector’s Fix Z: every output slice >= z shifts in-plane by [dy dx] (cumulative over rows). Applied by planCanvas/fusers, persisted in the project sidecar; [] = none

canvas

logical - true while obj.layout comes from a loaded project sidecar rather than from BatchOpt. The widgets may then describe a completely different job (a project carries no layout source/grid before schema v3), so anything that would silently re-derive the layout from BatchOpt must stand down. Cleared by buildLayoutFromBatchOpt, i.e. by every deliberate rebuild

edges

struct array - tile layout (nomOrigin, tileSize, etc.)

inspector

cell array of ROIMoved listener handles for tileROIs

inspectorListeners

handle to the seam-inspector child controller (controllers.StitchingInspector), [] when not open

intensityCorrection

cell array of listeners on the inspector (SeamsUpdated / CloseEvent)

layout

structure compatible with batch processing

layoutFromProject

struct from the last global solve (rmseTotal, nPruned, disconnectedTiles). Kept on the controller - not just inside optimizePositions_Callback - so the alignment-quality chip can be re-rendered whenever the edges change (e.g. the inspector excluding a seam) without re-solving; [] until the first solve

listener

handle to the view (StitchingGUI), empty in batch mode

mibModel
positions

struct array - measured pairwise edges (i, j, direction, measured, quality, valid)

resolvePending

struct from utils.stitch.estimateIntensityCorrection - the intensity correction every tile read is made with. Estimating it costs one pass over the tiles, so it is computed LAZILY by ensureIntensityCorrection and kept here; [] means “not estimated yet”, which is not the same as BatchOpt.IntensityCorrection = ‘None’ (that estimates to a neutral struct). Dropped whenever the layout is rebuilt or the method changes

roiListeners

array of images.roi.Rectangle - draggable tile handles in edit mode

seamScoresStamp

logical - an inspector edit (manual fix, exclude, undo) has changed the edge set while Auto re-solve was off, so obj.positions no longer follow from obj.edges. Lives HERE rather than on the inspector because the debt outlives the window: closing the inspector used to drop the flag with it, after which the chip stopped warning and Stitch fused the stale placement. Cleared by any solve (optimizePositions_Callback); stitchBtn_Callback settles it before fusing

solverInfo

N-by-1 cell of 3x3 doubles - solved per-tile affine transforms (tile-local xy -> global xy) when TransformType is not Translation; {} for the translation solve

tforms

N-by-3 double - solved tile origins [y x z]

tileROIs

struct - feature-detector tuning (per-detector params, RANSAC, rotation invariance, downsampling) for the Feature-based method; same shape as controllers.Alignment.defaultAutomaticOptions

tileStack

struct - output canvas plan (size, tilePlacement, etc.)

view

handle to MibModel

zSliceFixes

[1 x N] double or [] - the order Overwrite draws the tiles in, bottom first, as set in the seam inspector. [] = the default order (see utils.stitch.tileDrawOrder). Cleared with the layout; saved in the project.

zarrExportOptions

struct - what obj.edges’ seamScores were computed for (.positions, .numEdges, .correctionMethod), so the same overlaps are not re-read by the next consumer that wants them. Set by controllers.Stitching.ensureSeamScores and by a project load whose sidecar already carried a complete score set; [] = not scored (yet). Compared against live state rather than explicitly invalidated, so nothing can forget to clear it

Method Summary
static ViewListner_Callback2(~, evnt)

VIEWLISTNER_CALLBACK2 - Static model-event listener guard.

Syntax:
controllers.Stitching.ViewListner_Callback2(obj, src, evnt)
Input Arguments:
  • obj - handle to the Stitching controller

  • evnt - event data from the model

addCallbacks()

ADDCALLBACKS - Wire all GUI widget callbacks for the Stitching controller.

Syntax:
obj.addCallbacks()

BatchOpt-linked widgets are named exactly as their BatchOpt fields (ChildView copies the component name into the Tag, which utils.updateBatchOptFromGUI_Shared uses as the field name).

applyProjectSettings(settings, skipFields)

APPLYPROJECTSETTINGS - Restore tool parameters from a project sidecar into BatchOpt.

Syntax:
appliedFields = obj.applyProjectSettings(settings)
appliedFields = obj.applyProjectSettings(settings, skipFields)

Reverses the flattening done by controllers.Stitching.collectProjectSettings(): each saved value is written back into the {1} slot of a dropdown / numeric BatchOpt cell, or straight into a logical / char field, guided by the SHAPE of the current default. Unknown fields, dropdown items no longer offered, and type mismatches are ignored rather than raising - a project saved by an older MIB must still load into a newer dialog.

The method only touches obj.BatchOpt and obj.automaticOptions: it never rebuilds the layout or touches measurements, so the caller decides what the new settings mean for the current tiles (see controllers.Stitching.loadProjectBtn_Callback()).

Input Arguments:
  • settings - struct as returned by utils.stitch.loadProject() (8th output); an empty struct applies nothing

  • skipFields (optional) - [cell] field names to leave untouched. Pass {'InputPath', 'OutputPath'} for the “settings only” load, where the parameters are reused on a DIFFERENT set of tiles.

Output Arguments:
  • appliedFields - [cell] names of the fields actually written

Example - reuse a project’s parameters on the tiles selected right now:

[~, ~, ~, ~, ~, ~, ~, settings] = utils.stitch.loadProject(projectPath);
obj.applyProjectSettings(settings, {'InputPath', 'OutputPath'});
obj.buildLayoutFromBatchOpt();
askImportMode(inputPath)

ASKIMPORTMODE - Ask how much of an acquisition’s own stitch to reuse.

Syntax:
accepted = obj.askImportMode(inputPath)

Called from controllers.Stitching.selectInputBtn_Callback() when the Position file source is pointed at a file that can carry a finished stitch - a Fibics Atlas .ve-mif or a SerialEM .mdoc - rather than a position text file.

Both formats record the same three stages of the same job: a nominal placement, the pairwise seam measurements taken from it, and the final solved positions. Only where they are written differs - Atlas puts each stage in its own sidecar file, SerialEM keeps all three in the one .mdoc. The choice offered is therefore identical, and only the WORDING is vendor-specific.

Both later stages are worth having: MIB can solve from the recorded measurements without re-registering a pixel, or take the finished placement and go straight to fusing. Neither is worth having blindly, though - under difficult imaging conditions the stage positions a vendor works from can be wrong enough that its stitch is bad while the tiles themselves are perfectly stitchable. So the choice is the user’s, and Nominal grid only (register everything from scratch) is always offered.

The answer is written into BatchOpt.LayoutImport, which controllers.Stitching.buildLayoutFromBatchOpt() reads when it builds the layout. Nothing is asked when the file carries no stitch (there is nothing to choose) or when the tool has no window (batch runs take the BatchOpt value as given).

Input Arguments:
  • inputPath - [char] full path to the .ve-mif / .mdoc about to be opened

Output Arguments:
  • accepted - [logical] false only when the user cancelled the dialog; the caller should then abandon the whole selection

See also utils.stitch.findAtlasSidecars, utils.stitch.findMdocSidecar, utils.stitch.buildLayoutAtlas, utils.stitch.buildLayoutMdoc

buildFeatureOptions()

BUILDFEATUREOPTIONS - Assemble the options struct for utils.stitch.featureShift from the current detector selection and automaticOptions (used when RegistrationMethod = Feature-based).

rotationInvariance is DERIVED from BatchOpt.AllowRotation rather than stored: it is MATLAB’s extractFeatures Upright flag, so upright descriptors (true) cannot match rotated content at all and would silently veto a rotating solve. The two are therefore one user decision - “are the tiles rotated?” - and the single Allow rotation checkbox owns it. Deriving it here, the one place every consumer (measure, stitch, previewFeatureMatch()) goes through, means the two cannot drift and no stale value can survive a project load.

Syntax:
featureOptions = obj.buildFeatureOptions()
buildLayoutFromBatchOpt()

BUILDLAYOUTFROMBATCHOPT - Build the tile layout from current BatchOpt settings (headless).

Syntax:
obj.buildLayoutFromBatchOpt()

Builds obj.layout from BatchOpt.LayoutSource and BatchOpt.InputPath without opening any dialogs, so it works both in batch mode and from the GUI after the input path was chosen. Errors when the path is missing or contains no tiles. Downstream state (edges, positions, canvas) is reset.

The Position file source covers three file kinds, told apart by extension: MIB’s own filename X Y [Z] text file, a Fibics Atlas mosaic (MosaicInfo_*.ve-mif), and a SerialEM montage (*.mdoc, or the .mrc it describes). All three answer the same question - where does each tile go - so they share one layout source rather than three dropdown entries.

The two vendor formats can bring more than a layout: alongside the acquisition record they may carry the vendor’s own finished stitch, and BatchOpt.LayoutImport decides how much of it to take (nothing / the seam measurements / the measurements and the solved placement). Importing here rather than in the GUI callback keeps batch runs and the dialog on one path.

closeWindow()

CLOSEWINDOW - Close the Stitching GUI and clean up listeners.

Syntax:
obj.closeWindow()
collectProjectSettings()

COLLECTPROJECTSETTINGS - Flatten the tool’s own parameters for the project sidecar.

Syntax:
settings = obj.collectProjectSettings()

Produces the settings block written by utils.stitch.saveProject() (schema v3): one plain value per persisted BatchOpt field - dropdown and numeric-limit cells are reduced to their {1} entry, since the item lists and spinner limits belong to the controller, not to the saved project - plus the nested FeatureOptions (the feature-detector tuning behind the Settings… dialog). controllers.Stitching.applyProjectSettings() reverses the flattening on load.

Deliberately NOT persisted: showWaitbar and the mibBatch* fields (batch plumbing, not user settings) and id (the active dataset).

Output Arguments:
  • settings - struct with one field per persisted setting

Example - save the current dialog state with the project:

utils.stitch.saveProject(projectPath, obj.layout, obj.edges, obj.positions, ...
    struct(), outputInfo, obj.tforms, obj.zSliceFixes, obj.collectProjectSettings());
configureFeaturesBtn_Callback()

CONFIGUREFEATURESBTN_CALLBACK - Open the feature-detector settings dialog.

Syntax:
obj.configureFeaturesBtn_Callback()

Pops the shared utils.align.detectorSettingsDlg() (also used by controllers.Alignment) to edit the currently selected FeatureDetectorType parameters, the upright-descriptor flag (automaticOptions.rotationInvariance, which holds MATLAB’s Upright value - see utils.align.detectorSettingsDlg()), the detection downsampling factor, and the RANSAC (estgeotform2d) settings. The edited values are stored in obj.automaticOptions and applied on the next Measure overlaps / Stitch run of the Feature-based method. When the dialog is accepted and a layout is loaded, previewFeatureMatch() renders the resulting keypoint matches on a representative tile pair so the effect of the change is visible immediately (as in the Alignment feature preview).

currentSeamScoreStamp()

CURRENTSEAMSCORESTAMP - What a seam-score pass over the current state would be valid for.

Syntax:
stamp = obj.currentSeamScoreStamp()

One builder shared by everything that records the stamp, so the fields compared in controllers.Stitching.seamScoresAreCurrent() and the fields written after scoring cannot drift apart.

Output Arguments:
  • stamp - [struct] .positions, .numEdges, .correctionMethod

defaultFeatureOptions(~)

DEFAULTFEATUREOPTIONS - Default feature-detector tuning for the Feature-based registration method. Same shape as controllers.Alignment.defaultAutomaticOptions, tuned for stitching: full-resolution detection (factor 1) and a lower SURF threshold (more blobs in thin overlaps).

Syntax:
options = obj.defaultFeatureOptions()
ensureIntensityCorrection()

ENSURETILECORRECTION - The intensity correction every tile read is made with.

Syntax:
correction = obj.ensureIntensityCorrection()

Returns the correction implied by BatchOpt.IntensityCorrection, estimating it on first use and caching it in obj.intensityCorrection. Every stage that builds a tile reader - overlap estimation, measurement, seam scoring, the seam inspector and both fusers - passes the result through as options.correction, which is what makes them all see the SAME pixels. A stage that skipped it would measure or score one set of intensities and fuse another.

Why it is lazy. Estimating reads every tile once, which is worth avoiding until something actually needs pixels: opening a dialog, picking an input or flipping the dropdown must stay instant. The cache is dropped by controllers.Stitching.buildLayoutFromBatchOpt() (new tiles) and by controllers.Stitching.updateBatchOptFromGUI() (new method), so it can never describe a job other than the current one.

'None' still produces a struct (neutral gains, no field) rather than []. Distinguishing “estimated, and the answer is no correction” from “not estimated yet” is what stops the estimate being retried on every stage of a run.

``’Re-exposure damage’`` also depends on the PLACEMENT, which none of the other methods do closely enough to matter: it corrects a sharp-edged patch where an earlier tile’s footprint fell, so its footprints are only as right as the positions they were placed with. Two consequences:

  • Before the first solve it is neutral. Nominal positions can be off by many pixels (117 px vertically on the reference pair), and a correction placed there would paint a sharp false edge into the pixels the registration then measures. Measure overlaps and overlap estimation therefore run on uncorrected pixels - phase correlation is insensitive to a flat offset band anyway - and the damage is estimated as soon as solved positions exist, i.e. before the seams are scored and before anything is fused.

  • A changed placement re-places it. The cached correction records the positions it was placed with; when they no longer match obj.positions it is re-estimated with the old one passed as previous, which for a move of a few pixels (a re-solve, an inspector fix) only re-places the footprints and reads no pixel.

Output Arguments:

See also utils.stitch.estimateIntensityCorrection, utils.stitch.makeTileReader

ensureSeamScores(options)

ENSURESEAMSCORES - Seam scores for the CURRENT placement, computed at most once.

Syntax:
cancelled = obj.ensureSeamScores()
cancelled = obj.ensureSeamScores(options)

Fills obj.edges(k).seamScore / .dzHint with utils.stitch.scoreSeams(), and returns immediately when they already describe the current state. Same lazy-cache shape as controllers.Stitching.ensureIntensityCorrection(), and for the same reason: scoring re-reads every overlap from disk, which on a large mosaic is the slowest thing the tool does short of fusing.

What it stops. Two stages score the seams as part of their own job - optimizePositions_Callback() after a solve and buildLayoutFromBatchOpt() after importing a vendor placement - and the seam inspector scores them again on the way in (controllers.StitchingInspector.scoreAndRank()). Importing an Atlas or SerialEM stitch and then pressing Inspect and fix… therefore read every overlap twice, as did every inspector re-solve (controllers.StitchingInspector.resolveBtn_Callback() calls optimizePositions_Callback and then scoreAndRank). The ranking itself is free to recompute - utils.stitch.rankSeams() touches no pixels.

What counts as “still current” (obj.seamScoresStamp):

  • the solved positions are unchanged - the scores ARE a function of the placement, since the strips are cut at the solved origins;

  • the edge count is unchanged - a re-measure produces a different set;

  • BatchOpt.IntensityCorrection is unchanged - the scores are correlations of CORRECTED pixels, so changing the method changes them;

  • and every edge actually carries a score, so a partially-scored set (a cancelled pass, or a project saved before scoring existed) is redone.

Deliberately NOT invalidated by an edge edit that leaves the positions alone: excluding a seam, or a manual fix awaiting its re-solve, cannot change any seam’s pixels. The re-solve that follows moves the positions, and that is what triggers the rescore.

Input Arguments:
  • options (optional) - struct with fields:

    • .parentFigure - [handle] progress-dialog parent (default: guiFigure(); the inspector passes its own window)

    • .readerFcn - [function_handle] reuse an existing tile reader (optional)

Output Arguments:
  • cancelled - [logical] true when the user cancelled the Scoring seams… dialog; the scores are then cleared, the stamp is NOT set, and the next request scores again. false when scoring ran or was skipped.

See also utils.stitch.scoreSeams, utils.stitch.rankSeams, controllers.Stitching.ensureIntensityCorrection

guiFigure()

GUIFIGURE - Handle of the tool window, or [] without a view.

Syntax:
figureHandle = obj.guiFigure()

Every dialog parent and progress-bar anchor goes through this so the workflow methods run unchanged whether the tool has a window (GUI), was launched from a batch protocol, or is driven headlessly by the test suite. utils.dlgs.* and the utils.stitch progress helpers all accept [] as “no parent”.

Output Arguments:
  • figureHandle - [handle] the StitchingGUI figure, or [] when the controller has no (valid) view

helpBtn_Callback()

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

Syntax:
obj.helpBtn_Callback()
static imageFileFormat(label)

IMAGEFILEFORMAT - Look one imageFileFormats() row up by label.

Syntax:
entry = controllers.Stitching.imageFileFormat(label)
Input Arguments:
  • label - [char] a BatchOpt.OutputFormat value

Output Arguments:
  • entry - [struct] the matching row; the FIRST row when the label is unknown, so a project or batch protocol written by a newer MIB still exports rather than erroring

static imageFileFormats()

IMAGEFILEFORMATS - The image file formats OutputMode = Image files offers.

Syntax:
formats = controllers.Stitching.imageFileFormats()

One table shared by the file picker (controllers.Stitching.selectOutputPath_Callback()), the BatchOpt.OutputFormat item list and the fuse (controllers.Stitching.stitchBtn_Callback()), so a label offered in the dialog cannot name a saver the fuse does not call.

label is what the user picks and what BatchOpt.OutputFormat records; saverFormat + policy are what utils.stitch.fuseToFiles() passes on to io.SaverFactory. The two are not the same string because a TIF format name says nothing about 2-D versus 3-D - MIB’s own Save-as asks that separately, through Saving3DPolicy - and this dialog raises no follow-up questions, so the choice has to be in the label.

Output Arguments:
  • formats - [1xN struct] with fields .label, .extension, .saverFormat, .policy

inspectSeams_Callback()

INSPECTSEAMS_CALLBACK - Open (or focus) the seam inspector.

Syntax:
obj.inspectSeams_Callback()

Launches controllers.StitchingInspector on the current edges/positions for worst-first manual QC (see development/stitching/plan_inspector.md). The inspector mutates this controller’s edge/position state in place; its SeamsUpdated event refreshes this window’s widgets so both stay in sync.

loadProjectBtn_Callback()

LOADPROJECTBTN_CALLBACK - Load a stitching project from a JSON sidecar file.

Syntax:
obj.loadProjectBtn_Callback()

A project file carries two independent things: the STATE of one particular stitch (tiles, seam measurements, solved positions) and the SETTINGS it was produced with (layout source, grid, overlap, registration, output - schema v3 and newer). Both are useful on their own, so the user is asked which to take:

  • Restore everything - obj.layout / obj.edges / obj.positions / obj.tforms / obj.zSliceFixes come back from the file and every widget is reset to the saved settings, reproducing the dialog as it was.

  • Settings only - the saved parameters are applied to the tiles selected HERE (InputPath / OutputPatih are kept), the layout is rebuilt from them, and the file’s tiles/measurements/positions are ignored. This is the “stitch a new acquisition exactly like the previous one” case.

Files written before the settings block existed have nothing to choose from, so they load their state directly without a dialog.

measureOverlaps_Callback()

MEASUREOVERLAPS_CALLBACK - Measure pairwise shifts for all neighbour tile pairs.

Syntax:
obj.measureOverlaps_Callback()

Calls utils.stitch.findNeighborPairs to identify all overlapping tile pairs, then calls utils.stitch.measureAllPairs to compute phase- correlation shifts and quality scores for each pair. Results are cached in obj.edges, the status label is updated and the layout preview is redrawn (previewLayoutBtn_Callback()).

The progress dialog shown during measurement is cancelable: pressing Cancel leaves obj.edges and the status line untouched, as if this call had never been made.

onInspectorClosed()

ONINSPECTORCLOSED - Release the seam-inspector handle and its listeners.

Syntax:
obj.onInspectorClosed()
optimizePositions_Callback()

OPTIMIZEPOSITIONS_CALLBACK - Run global least-squares solve and plan the output canvas.

Syntax:
obj.optimizePositions_Callback()

Calls utils.stitch.solveGlobalLeastSquares to determine optimal tile positions from the measured edge set, then calls utils.stitch.planCanvas to compute the output canvas size and integer tile placements. Results are cached in obj.positions and obj.canvas.

RMSE and residual statistics are displayed in the status label.

previewFeatureMatch()

PREVIEWFEATUREMATCH - Visualise feature matches on a representative tile pair.

Syntax:
obj.previewFeatureMatch()

Feature-based tuning aid, mirroring controllers.Alignment.previewFeaturesBtn_Callback(). Picks the first overlapping tile pair from the current obj.layout, reads both FULL tiles (feature matching uses whole tiles, not the thin nominal overlap strip), detects + matches keypoints with the selected FeatureDetectorType and the parameters in obj.automaticOptions, robust-fits a translation (estgeotform2d RANSAC), and renders the two tiles stitched at the recovered offset (imfuse false-colour: tile i green, tile j magenta, grey where they agree) with the inlier keypoints marked on top. A clean seam and tightly overlapping green/magenta keypoints mean a good registration; the recovered shift and inlier ratio are shown in the title, so the effect of a settings change is visible immediately - the feedback loop the Alignment preview provides.

A downsampleFactor above 1 affects DETECTION ONLY: keypoints are found on resized copies and their locations scaled back, while the composite is always BUILT from the full-resolution tiles (the title notes the detection scale). For real-world tile sizes that composite can exceed a GPU’s max texture side (commonly ~16384 px) - MATLAB then creates the image object without error, but the driver silently fails to rasterize it while the point/tie-line overlay still renders, i.e. only markers show, no image. To avoid this, the composite is downsized for DISPLAY ONLY past a safe cap (title notes the display scale too); detection/matching/the fitted shift are unaffected.

No stitching is applied. Requires a layout with at least one overlapping pair (select input tiles first); shows an informational dialog otherwise.

Shows a Cancelable progress dialog spanning tile reading, feature detection/matching, transform fitting, AND composite building + the display-downsize step above - reading/warping/fusing full-resolution tiles off disk can take a noticeable time for large files, and without it the app would look frozen. Cancel is polled between stages (read A, read B, detect, match, fit, build composite, resize for display); since each stage itself is one blocking call, a click takes effect at the next stage boundary, not mid-call. Cancelling closes the dialog and returns without opening the preview figure.

previewLayoutBtn_Callback()

PREVIEWLAYOUTBTN_CALLBACK - Draw nominal tile positions on the preview axes.

Syntax:
obj.previewLayoutBtn_Callback()

Renders a top-down view of tile nominal positions on the previewAxes widget, colour-coded and labelled with the tile index. The axes are scaled to the full canvas extent.

Two modes, selected by the editLayoutCheckbox state:
  • display (default) - static patch rectangles (fast, read-only). Drawn at the SOLVED positions whenever a solve exists (kept current by optimizePositions_Callback and hence by every inspector re-solve); nominal positions otherwise - the title states which.

  • edit - one draggable images.roi.Rectangle per tile (translate-only, fixed size); dragging a tile writes its new position into layout(i).nomOrigin and invalidates the measured edges / solved positions so the next Measure/Optimize run uses the corrected layout. This is the interactive rough-placement workflow (Phase 3).

For multi-layer (3D) layouts only the FIRST Z-layer is drawn/edited: every layer shares the same XY grid, so drawing them all would stack rectangles.

static projectSettingFields()

PROJECTSETTINGFIELDS - BatchOpt fields persisted in the project sidecar.

Syntax:
fieldNames = controllers.Stitching.projectSettingFields()

The single list shared by controllers.Stitching.collectProjectSettings() (save) and controllers.Stitching.applyProjectSettings() (load), so the two can never drift apart. Excludes showWaitbar / mibBatch* / id - batch plumbing rather than user settings.

Output Arguments:
  • fieldNames - [cell] BatchOpt field names, in dialog order

refreshInputPathWidget()

REFRESHINPUTPATHWIDGET - Show BatchOpt.InputPath in the InputPath widget, handling either a uieditfield (single newline-joined string) or a uilistbox (one item per path - better for multi-folder input). BatchOpt.InputPath stays the newline-joined string in both cases, so batch mode is unaffected.

Syntax:
obj.refreshInputPathWidget()
refreshQualityChip()

REFRESHQUALITYCHIP - Render the alignment-quality chip from the cached state.

Syntax:
obj.refreshQualityChip()

Colour-coded alignment quality (rmseLabel), translating the raw solver RMSE - in pixels, the mean disagreement between the pairwise measurements at the solved positions - into a plain-language rating on a green→red scale that a non-specialist can read at a glance, DOWNGRADED whenever the pixel seam check disagrees with the residual. The exact numbers stay in the text and the tooltip for those who want them.

Reads obj.solverInfo (cached by controllers.Stitching.optimizePositions_Callback(), restored by controllers.Stitching.loadProjectBtn_Callback()) and the seam scores currently on obj.edges. Nothing here re-reads pixels or re-solves, so it is cheap enough to run from updateWidgets - which is what keeps the chip honest when the seam inspector excludes or re-includes an edge: the worst score is a minimum over the VALID edges, so that set changing changes the verdict even though no position moved.

While a global re-solve is owed (obj.resolvePending - a fix made with Auto re-solve off), the cached RMSE no longer describes the current edge set, so the chip says so instead of quoting a stale number. The flag lives on the CONTROLLER, not on the inspector, so the warning survives the inspector being closed - which is exactly when a stale rating would otherwise go unmentioned.

static renameLegacyFields(settings)

RENAMELEGACYFIELDS - Map retired BatchOpt field names onto current ones.

Syntax:
settings = controllers.Stitching.renameLegacyFields(settings)

Applied to anything arriving from OUTSIDE this class - a batch protocol or a project sidecar - before it is merged into BatchOpt, so both entry points age the same way.

AtlasImport became LayoutImport when SerialEM montages joined Fibics Atlas in offering their own stitch for import: the three modes were never Atlas-specific, and the old name made a SerialEM protocol read as an Atlas one. The values were renamed with it ('Atlas seams…' → 'Vendor seams…').

A file carrying BOTH names keeps the current one - an old key alongside a new one means the writer knew about the new name.

Input Arguments:
  • settings - [struct] BatchOpt-shaped struct, possibly using retired field names

Output Arguments:
  • settings - [struct] same struct with retired names replaced

reportError(errorInfo, dlgTitle)

REPORTERROR - Surface a caught error from a workflow step.

Syntax:
obj.reportError(errorInfo, dlgTitle)

With a window: an error dialog, and the caller returns. Without one the error is rethrown, so a batch protocol or a test sees the failure instead of a silently skipped step.

Input Arguments:
  • errorInfo - [MException] the caught error

  • dlgTitle - [char] dialog title

returnBatchOpt(BatchOptOut)

RETURNBATCHOPT - Send BatchOpt to mibBatchController.

Syntax:
obj.returnBatchOpt()
obj.returnBatchOpt(BatchOptOut)
Input Arguments:
  • BatchOptOut (optional) - [struct] BatchOpt to send; defaults to obj.BatchOpt

runOverlapEstimation()

RUNOVERLAPESTIMATION - Estimate the true grid overlap and rebuild the nominal layout.

Syntax:
cancelled = obj.runOverlapEstimation()

Grid-style layout sources only (Grid, Filename pattern - both carry .gridRC). Calls utils.stitch.estimateOverlap() (full-tile phase correlation with peak verification, median over the grid), writes the recovered percentages into BatchOpt.OverlapX/OverlapY and rebuilds the layout so the subsequent tight measurement pass starts from honest nominal positions. Directions that could not be estimated keep the user’s value.

Does nothing for a layout restored from a project file: the rebuild would re-derive it from BatchOpt, which still describes whatever job was set up before the load (a project carries no layout source or grid before schema v3), silently replacing the project’s tiles. Its nominal origins come from the file and need no overlap guess.

Shows a Cancelable progress dialog while the sampled tile pairs are read and registered - reading full-resolution tiles can take a noticeable time for large files. Returns cancelled = true (and leaves BatchOpt untouched) if the user cancels before the estimate finished; callers must check this and stop rather than continue to Measure overlaps with an un-estimated guess.

Output Arguments:
  • cancelled - [logical] true when the user cancelled the estimate.

saveProjectBtn_Callback()

SAVEPROJECTBTN_CALLBACK - Save the current stitching project to a JSON file.

Syntax:
obj.saveProjectBtn_Callback()
seamScoresAreCurrent()

SEAMSCORESARECURRENT - True when obj.edges’ seam scores still describe the current placement, edge set and intensity correction.

Syntax:
tf = obj.seamScoresAreCurrent()

The stamp is COMPARED against live state rather than cleared by whoever changes that state: a rescore that should have happened and did not is a mosaic rated on the wrong pixels, and there is no single place every position change goes through. Every edge must also actually carry a score - a cancelled pass or a pre-scoring project leaves some empty, and a partial set is not a set.

selectInputBtn_Callback()

SELECTINPUTBTN_CALLBACK - Open a file/folder picker and build the tile layout.

Syntax:
obj.selectInputBtn_Callback()

Behaviour depends on BatchOpt.LayoutSource and BatchOpt.SubfolderMode (SubfolderMode = each tile is a FOLDER Z-stack rather than a single file):

  • Bio-Formats metadata - multi-select file picker; one multi-series file (series = tiles) or several single-tile files carrying stage coordinates.

  • Position file - file picker for any kind of file that states where the tiles go: MIB’s position text file (whose filename column may point at images or, with SubfolderMode, at folders), a Fibics Atlas mosaic (MosaicInfo_*.ve-mif), or a SerialEM montage (*.mdoc). Each format offers only the ONE file that states where the tiles go: Atlas’s .ve-tie / .ve-updates are detected from the .ve-mif, and a SerialEM .mrc is reached from its .mdoc - a bare stack has no placement in it and is refused with an explanation. When the acquisition already stitched the mosaic the user is asked how much of that stitch to reuse before the layout is built.

  • Grid / Filename pattern, SubfolderMode OFF - multi-select file picker; the selected image files are the tiles (stored newline-joined in InputPath; the layout builder natural-sorts them, so selection order does not matter). A plain folder path typed/pasted into InputPath still works - its image files become the tiles (batch back-compat).

  • Grid / Filename pattern, SubfolderMode ON - multi-select the tile folders (each a Z-stack); stored newline-joined in InputPath.

After building the layout, obj.layout is populated and updateWidgets is called to refresh the status display.

selectOutputPath_Callback()

SELECTOUTPUTPATH_CALLBACK - Open a file picker to choose the output path.

Syntax:
obj.selectOutputPath_Callback()
What is offered depends on BatchOpt.OutputMode:
  • OME-Zarr3 (BigData) - a single .zarr3 store.

  • Image files - the formats from controllers.Stitching.imageFileFormats(), whose picked filter is recorded in BatchOpt.OutputFormat. This dialog is the ONLY place the image format is chosen, which is why it also has to survive the cancel path unchanged.

In memory never reaches here - the button is disabled for it.

stitchBtn_Callback(batchModeSwitch)

STITCHBTN_CALLBACK - Fuse all tiles into the output dataset.

Syntax:
obj.stitchBtn_Callback()
obj.stitchBtn_Callback(batchModeSwitch)
Input Arguments:
  • batchModeSwitch (optional) - [logical] true when called headlessly (suppresses interactive dialogs); default false

Fusion path depends on BatchOpt.OutputMode:
  • In memory - calls utils.stitch.fuseInMemory then creates a new core.MibDataset and notifies 'NewDataset'.

  • OME-Zarr3 (BigData) - asks for the pyramid/chunk/compression settings (io.savers.Zarr3Saver.optionsDialog, the same dialog as the standard “Export to Zarr3” action), calls utils.stitch.fuseStreaming to write chunk-wise to an OME-Zarr file, then reopens it via io.loaders.Zarr3VirtualSetupLoader and notifies 'NewDataset'.

  • Image files - calls utils.stitch.fuseToFiles to write the mosaic as ordinary TIF/PNG/Amira files in the format BatchOpt.OutputFormat names. The result is NOT opened: a 2-D sequence is potentially hundreds of files, and re-reading what was just fused would cost as much as the stitch.

The project JSON sidecar is saved if BatchOpt.SaveProject is true.

This is the ONLY fuse entry point - the seam inspector has no button of its own, so a fix made there is baked in by pressing Stitch here (both windows stay usable side by side). When the inspector still owes a global re-solve (auto-re-solve off, or a deferred nudge), that re-solve runs FIRST: the guard lives on the operation, not on one button, so the mosaic can never be fused from stale positions.

stitchedFilename()

STITCHEDFILENAME - Name for the dataset a stitch produces.

Syntax:
filename = obj.stitchedFilename()

<source>_stitch.tif, next to whatever the mosaic was built from: the position file for a Position-file layout, otherwise the first tile. A stitched mosaic has no file of its own until it is saved, and a dataset with no filename makes Save as open on MATLAB’s working folder - somewhere unrelated to the data. Pointing it at the source folder puts the suggested name where the tiles are, which is where the result belongs.

The extension is always .tif regardless of what the tiles were: it names a single assembled 2-D/3-D image, which is not the same kind of thing as an MRC montage container or a .ve-mif mosaic record, and offering to save a mosaic back as its vendor’s acquisition format would be wrong.

Output Arguments:
  • filename - [char] full path, e.g. a montage picked as C:\data\Cell1.mrc.mdoc becomes C:\data\Cell1_stitch.tif.

See also controllers.Stitching.stitchBtn_Callback

updateBatchOptFromGUI(hObject)

UPDATEBATCHOPTFROMGUI - Sync obj.BatchOpt from a changed widget.

Syntax:
obj.updateBatchOptFromGUI(hObject)
Input Arguments:
  • hObject - handle to the AppDesigner widget that changed

updateInfoLabel()

UPDATEINFOLABEL - Set the info label to a short description of what the current LayoutSource does, so the user knows what input to provide. Guarded by isfield - no-op until the infoLabel widget exists in the mlapp.

Syntax:
obj.updateInfoLabel()
updateWidgets()

UPDATEWIDGETS - Refresh all GUI widgets from current BatchOpt state.

Syntax:
obj.updateWidgets()

No-op without a view (batch protocols and headless runs hold the same state in BatchOpt - there is simply nothing to render it into).