ScanImage Coordinate System Instances

This page is the map of the coordinate system tree that ScanImage® builds at startup: which node exists, which object owns it, what its units are, and what its transformation to its parent means. These are the nodes you name when you write hPts.transform(...).

Run

hSI.hCoordinateSystems.plot();

on your own system to see the live tree - the branches below the Reference node depend on which scanners, cameras and SLMs are configured.


The tree

World                                       hSI.hCoordinateSystems.hCSWorld
└── Reference space                         hSI.hCoordinateSystems.hCSReference
    ├── Focus                        [lock] hSI.hCoordinateSystems.hCSFocus
    ├── <Scanner> Z Affine Alignment        hSI.hScan2D.hCSZAffineLut          (one per Scan2D)
    ├── Camera - <name>                     hSI.hCameraManager.hCameraWrappers(k).hCSZAffineLut
    └── Motor Root Coordinates              hSI.hMotors.hCSCoordinateSystem
        └── Motor Micron                    hSI.hMotors.hCSMicron
            └── Motor Alignment             hSI.hMotors.hCSAlignment
                └── Motor Rotation   [lock] hSI.hMotors.hCSRotation
                    └── Motor Scaling            [lock] hSI.hMotors.hCSAxesScaling
                        └── Motor Axes Coordinates [lock] hSI.hMotors.hCSAxesPosition
                            └── Motor Anti Scaling     [lock]
                                └── Motor Anti Rotation  [lock]
                                    └── Motor Anti Alignment [lock] hSI.hMotors.hCSAntiAlignment
                                        └── Sample Absolute  hSI.hMotors.hCSSampleAbsolute
                                            └── Sample Relative  hSI.hMotors.hCSSampleRelative

[lock] marks nodes whose lock property is true: they are computed at run time from other nodes or from hardware, and are neither loaded from nor reset by the coordinate system class data file.


Root and focus

Node

Handle

Meaning

World

hSI.hCoordinateSystems.hCSWorld

The root. It exists so that every other node has a common ancestor and any two nodes can therefore be related. Nothing is expressed in World directly.

Reference space

hSI.hCoordinateSystems.hCSReference

The common space. Units are optical degrees in X and Y and microns in Z. The origin is the ideal focal point of the objective with all scanners and FastZ at zero. Every scanner, camera and the stage chain hang off this node. See Reference Coordinates.

Focus

hSI.hCoordinateSystems.hCSFocus

Reference space translated in Z by the current FastZ position, so the origin of Focus is always the point currently being focused on. ScanImage rewrites its toParentAffine from a listener on hSI.hFastZ.position; do not set it yourself.

% the point currently in focus, in sample-relative microns
hPt = scanimage.mroi.coordinates.Points(hSI.hCoordinateSystems.hCSFocus,[0 0 0]);
hPt = hPt.transform(hSI.hCoordinateSystems.hCSSampleRelative);

Scanner nodes

Every scanimage.components.Scan2D owns exactly one coordinate system, hCSZAffineLut, named '<scanner name> Z Affine Alignment'. It is a CSZAffineLut, and it is attached to hCSReference in the scanner’s reinit().

hSI.hScan2D.hCSZAffineLut        % the coordinate system of the active imaging system
hSI.hResScan.hCSZAffineLut       % ... of a specific scanner, by its configured name
hSI.hLinScan.hCSZAffineLut

This node is the scanner space described in ScanImage® Coordinate Systems: units are optical degrees in X and Y as produced by that scanner’s X and Y scanners, and microns in Z as produced by its FastZ. Its lookup table is what the scannerToReference alignment writes, which is why the alignment can be different at every depth.

% 2D affine that maps this scanner into reference space at a given reference z
zRef = 20;
T = hSI.hScan2D.hCSZAffineLut.toParentLutEntries.makeZAffines(zRef);

% corner points of the current field of view, in reference space
pts = hSI.hScan2D.fovCornerPoints(zRef);

A freshly constructed Scan2D seeds the node with a single identity entry (LUTEntry(0,0,eye(3)) on fromParentLutEntries), so an unaligned scanner behaves as if scanner space and reference space were the same.

Note

Scan2D still carries a legacy scannerToRefTransform property, a single 3x3 affine. hCSZAffineLut supersedes it; new code should use the coordinate system.


Camera nodes

Each camera managed by hSI.hCameraManager is wrapped in a scanimage.components.cameramanager.CameraWrapper, and each wrapper creates a CSZAffineLut named 'Camera - <camera name>' parented directly to hCSReference.

hWrapper = hSI.hCameraManager.hSelectedCameraWrapper;
hWrapper.hCSZAffineLut

Camera space is defined so that the imaged square has corner points [-.5 -.5], [.5 -.5], [.5 .5], [-.5 .5]; the wrapper’s pixelToCameraTransform gets you from pixels into that square, and the coordinate system gets you from there into reference space.


Motor nodes and the sample space

scanimage.components.Motors builds the longest chain in the tree. The chain exists so that each physically distinct correction lives in its own node and can be calibrated, inspected and reset independently. All of these properties are hidden but publicly readable on hSI.hMotors.

Node

Handle

Meaning

Motor Root Coordinates

hCSCoordinateSystem

Attachment point of the whole stage chain to reference space.

Motor Micron

hCSMicron

Converts reference-space optical degrees into microns. This is the objective calibration: hSI.objectiveResolution is derived from the mean of its XY scaling, and an uncalibrated system defaults to diag([1/20 1/20 1 1]) - that is, 20 microns per optical degree, with Z already in microns.

Motor Alignment

hCSAlignment

Rotation of the stage axes relative to the reference axes, as measured by the stage-to-scanner alignment.

Motor Rotation

hCSRotation

Additional deliberate rotation - a tilted objective, or a yaw applied by the user. Locked; written through the azimuth / elevation API.

Motor Scaling

hCSAxesScaling

Applies hSI.hMotors.scaleXYZ so that device units become microns. Locked; recomputed whenever scaleXYZ changes.

Motor Axes Coordinates

hCSAxesPosition

The hardware boundary. Positions read from the motor controllers enter the tree here. Going up leads to reference space; going down leads to sample space.

Motor Anti Scaling

(private)

Undoes Motor Scaling.

Motor Anti Rotation

(private)

Undoes Motor Rotation.

Motor Anti Alignment

hCSAntiAlignment

Undoes Motor Alignment.

Sample Absolute

hCSSampleAbsolute

End of the chain. Microns, axes parallel to the reference axes, origin fixed in space.

Sample Relative

hCSSampleRelative

Sample Absolute offset by the user’s relative zero. This is the space users see in the GUI and the space tiles, cycles and annotations are stored in. See Sample Coordinate System.

The three “anti” nodes are what make sample space well behaved. Each mirrors the corresponding correction node, so the corrections cancel on the way down the chain: sample space is expressed as though the stage had been perfect, in microns, with axes aligned to the reference axes, while the corrections still apply between reference space and the hardware. ScanImage keeps them synchronized through listeners on the changed event of Motor Alignment, Motor Rotation and Motor Scaling; that is why they are locked.

% read the stage
hPtSample = hSI.hMotors.getPosition(hSI.hCoordinateSystems.hCSSampleRelative);
hPtRef    = hSI.hMotors.getPosition(hSI.hCoordinateSystems.hCSReference);

% move the stage to a point defined in any coordinate system
hPt = scanimage.mroi.coordinates.Points(hSI.hCoordinateSystems.hCSSampleRelative,[10 0 -5]);
hSI.hMotors.move(hPt);

% how many microns per optical degree is the objective calibrated for?
hSI.objectiveResolution

Note

Because Motor Micron sits between the stage and reference space, changing the objective calibration silently changes the size of everything expressed in microns. That is the intent - it is a single place to describe the objective - but it also means the calibration belongs in the coordinate system class data file for that objective.


SLM and photostimulation nodes

A scanimage.components.scan2d.SlmScan adds a chain of its own. The chain begins at the paired linear scanner when one is configured, because the SLM deflects the beam that the galvos are already pointing.

<LinScan> Z Affine Alignment   (or the SlmScan's own identity Z Affine Alignment)
└── <SlmScan> Coordinate System              hSlmScan.hCSCoordinateSystem
    └── <SlmScan> SLM Offset from Scanner    hSlmScan.hCSScannerOffset
        └── <SlmScan> SLM Lut Z Alignment    hSlmScan.hCSSlmNative
            └── <SlmScan> SLM Z Alignment Lut 3D  hSlmScan.hCSSlmZAlignmentLut3D
                └── <SLM> Deflection and Vergence  [lock] hSlmScan.hSlm.hCoordinateSystem
                    ├── <SLM> Diffraction Efficiency   hSlm.hCSDiffractionEfficiency
                    └── <SLM> Pixel coordinates [lock] hSlm.hCSPixel

Node

Meaning

<SlmScan> Z Affine Alignment

Present so that SlmScan satisfies the Scan2D contract. Its transformations stay identity when a linear scanner is paired.

<SlmScan> Coordinate System

Identity bridge whose parent is chosen at reinit: the paired linear scanner’s hCSZAffineLut if there is one, otherwise the SlmScan’s own.

<SlmScan> SLM Offset from Scanner

Origin moved to wherever the paired galvos are currently pointing. Units and axes are still those of reference space: optical degrees in XY, microns in Z.

<SlmScan> SLM Lut Z Alignment

CSZAffineLut. This is where the SLM-to-scanner alignment lives. Its fromParentLutEntries map zfrom (microns in the offset space) to zto (SLM vergence in diopters) with a per-plane XY affine. Below this node, XY is in SLM optical degrees and Z is wavefront vergence.

<SlmScan> SLM Z Alignment Lut 3D

CSLut holding the field curvature correction: its third interpolant maps (deg_x, deg_y, diopter_z) to a corrected vergence. Identity when uncalibrated.

<SLM> Deflection and Vergence

The SLM’s native space, and the space the SLM is actually commanded in. XY are deflection in optical degrees, Z is vergence in diopters (m-1); the origin produces a blank phase mask. Locked - it is an identity bridge, and the real alignment lives one level up.

<SLM> Diffraction Efficiency

CSLut whose first interpolant maps a 3D SLM-native coordinate to a scalar diffraction efficiency. Seeded with an analytic sinc2 model and replaced by measured data during the diffraction efficiency calibration.

<SLM> Pixel coordinates

Pixel coordinates on the SLM panel. Useful when the SLM sits in a conjugated image plane. Locked.

hSlmScan = hSI.hSlmScan;

% where would a reference-space point land on the SLM?
hPt = scanimage.mroi.coordinates.Points(hSI.hCoordinateSystems.hCSReference,[1 0.5 0]);
hPtSlm = hPt.transform(hSlmScan.hSlm.hCoordinateSystem);
fprintf('SLM deflection %.3f, %.3f deg, vergence %.4f diopters\n',hPtSlm.points);

Working with the tree from a script

Find a node whose handle you do not have

hCS = hSI.hCoordinateSystems.getCoordinateSystemByName('Motor Alignment');

Enumerate the whole tree

[~,nodes] = hSI.hCoordinateSystems.hCSWorld.getTree();
names = cellfun(@(n)n.name,nodes,'UniformOutput',false);
disp(names(:));

See how two spaces are related

[path,toParent] = hSI.hScan2D.hCSZAffineLut.getRelationship(hSI.hMotors.hCSSampleRelative);
cellfun(@(n)fprintf('%s\n',n.name),path);

React to an alignment change

hL = most.ErrorHandler.addCatchingListener( ...
     hSI.hMotors.hCSAlignment,'changed',@(varargin)disp('stage alignment updated'));

Tip

Keep the units straight. Reference and scanner spaces are optical degrees in XY and microns in Z; sample spaces are microns in all three; SLM-native space is SLM optical degrees in XY and diopters in Z. The transform is what converts between them - reading hPts.points without knowing hPts.hCoordinateSystem tells you nothing.