-
Notifications
You must be signed in to change notification settings - Fork 16
Stage coordinates
In this document we describe the role of "stage-coordinates" and how to work with them from a libCZI-perspective.
With the term stage-coordinate we refer to the position of the microscope-stage. So, conceptually we can assign a stage-coordinate to every pixel of the document, and this coordinate states "the position the stage has to go to so that the pixel is on the optical axis". The unit of the stage-coordinate system is µm.
Depicted here is the simple case where stage-coordinate system and pixel-coordinate are oriented the same way - i.e. the axes are pointing in the same direction and are parallel.
At the following location the CZI contains information which refer to the stage-coordinate system:
- in subblock-metadata
- in XML-metadata (at Metadata/Information/Image/Dimensions/S/Scenes[]/Scene/CenterPosition )
In the metadata of a subblock the following information gives information about the stage-coordinate corresponding to the center-pixel of the subblock:
Those fields give the stage-coordinate of the center pixel of the subblock in units of µm.
Notes:
- This information is not mandatory - there is no guarantee that this piece of information is present with every CZI-document.
- Only subblocks which originated from a camera-acquisition are expected to contain this information. Especially pyramid-subblocks will not come with this information.
In XML-metadata, at this location information is stored about the theoretical position of the center-point of a scene:
Notes:
- This information is written by the acquisition in advance, i.e. it is the theoretical stage-position of the center of the scene.
- If e.g. the acquisition is cancelled or the course of acquisition is altered by other means, deviations of this theoretical position are to be expected.
As of today, libCZI is not providing a consolidated and abstracted interface to deal with stage-coordinates. The following describes the currently available APIs and procedures relevant here:
The method ISubBlock::GetRawData with parameter type=Metadata gives access to the raw XML-data of the subblock. Parsing this XML for the elements METADATA/Tags/StageXPositionX
and METADATA/Tags/StageXPositionY
now needs to be done in client code.
The subblocks X-Y-position in pixel-coordinate-system is available via the method ISubBlock::GetSubBlockInfo. The property logicalRect now gives the pixel-coordinate of the subblock, where x and y refer to the upper-left-corner.
Note that the stage-coordinate (found in the subblock's XML-metadata) refers to the center-point of the subblock, where the corresponding pixel-coordinate is found as
(logicalRect.x + logicalRect.w / 2.0 , logicalRect.y + logicalRect.h / 2.0)
.
For accessing the Dimensions/S-metadata node there is also no dedicated API available. The element can be accessed with the IXMLNodeRead interface like shown in this code example:
// spReader is an instance of an ICZIReader-object
auto metadata_segment = spReader->ReadMetadataSegment()->CreateMetaFromMetadataSegment();
// this gets the node corresponding to the scene with the attribute "Index=0"
auto centerposition_node = metadata_segment->GetChildNodeReadonly("ImageDocument/Metadata/Information/Image/Dimensions/S/Scenes/Scene[Index=0]/CenterPosition");
if (centerposition_node)
{
wstring centerposition_element;
if (centerposition_node->TryGetValue(¢erposition_element))
{
// if successful, then "centerposition_element" will contain a string like this
// "2068.246,5983.908" - i.e. two comma separated doubles
}
}
The following API is available for retrieving information about the extent of document (and individual scenes) in pixel-coordinate system : ISubBlockRepository::GetStatistics. The object SubBlockStatistics now contains information about the extent, where the extent is determined in two different ways:
- calculating the axis-aligned bounding box of all subblocks including pyramid-subblocks
- calculating the axis-aligned bounding box of all subblocks excluding pyramid-subblocks
boundingBox gives the extent (of the whole document) including pyramid-subblocks, whereas boundingBoxLayer0Only gives the extent excluding pyramid-subblocks. In the same manner, the property sceneBoundingBoxes has extents for individual scenes including pyramid-layers (boundingBox) and excluding pyramid-layers (boundingBoxLayer0).
The technical background here is - for various reasons, the pyramid-subblocks may extend beyond the bounding-box of the layer-0 subblocks. So, it is possible that the extent including pyramid-subblocks is different and larger than the layer-0 extent.
Note that the pixel-coordinates corresponding to the stage-coordinates reported in XML-metadata for the centers of the scenes need to be calculated from the extent excluding pyramid-subblocks.
So far we discussed various ways how to retrieve stage-coordinates for some special points:
- subblock-metadata gives stage-position of center-point of subblock
- XML-metadata gives stage-position of center-point of scene
Under the condition that
- stage-coordinate system and pixel-coordinate system have the same orientation
- the scaling of the document is valid and correct (as reported by ScalingInfo in libCZI)
a general transformation rule between pixel-coordinates and stage-coordinate can be established.
So, we have this situation:
The sought-after stage-coordinate of Q is then found with this procedure:
-
calculate the difference vector from P to Q:
$\vec{d_{pixelcoordinates}}=\vec{Q} -\vec{P}$ -
multiply the components of the difference vector by the scaling:
$\vec{d_{stagecoordinates}}=scaling * \vec{d_{pixelcoordinates}}$ -
add difference_in_stage_coordinate to the stage-coordinate of point Q in order to get the stage-coordinate of point Q:
$\vec{Q_{stagecoordinates}}=\vec{P_{stagecoordinates}} + \vec{d_{stagecoordinates}}$
This code-fragment using libCZI shows a potential implementation of this procedure:
// get stage-coordinate & pixel-coordinate of "center-point of first subblock"
auto first_subblock = spReader->ReadSubBlock(0);
// TODO: we'd need to check whether this is a layer-0 subblock, e.g. like this
// if (first_subblock->GetSubBlockInfo().physicalSize.w == first_subblock->GetSubBlockInfo().logicalRect.w &&
// first_subblock->GetSubBlockInfo().physicalSize.h == first_subblock->GetSubBlockInfo().logicalRect.h)
// get metadata-xml
const char* subblock_metadata; size_t size_subblock_metadata;
first_subblock->DangerousGetRawData(ISubBlock::Metadata, subblock_metadata, size_subblock_metadata);
// Note that the string is NOT null-terminated! This would now have to be parsed for StageXPosition/StageYPosition.
// This is omitted here, we assign simply the resulting values here
double stage_coordinate_first_subblock_x = -037207.0400;
double stage_coordinate_first_subblock_y = 0010491.520;
double pixel_coordinate_first_subblock_x = first_subblock->GetSubBlockInfo().logicalRect.x + first_subblock->GetSubBlockInfo().logicalRect.w / 2.0;
double pixel_coordinate_first_subblock_y = first_subblock->GetSubBlockInfo().logicalRect.y + first_subblock->GetSubBlockInfo().logicalRect.h / 2.0;
// and now, calculate the stage-coordinate corresponding to the pixel-coordinate given by (pixelcoordinate_to_be_transformed_x, pixelcoordinate_to_be_transformed_y)
auto scaling = spReader->ReadMetadataSegment()->CreateMetaFromMetadataSegment()->GetDocumentInfo()->GetScalingInfo();
// Note: both the x- and the y-axis of the stage-coordinate-system are pointing in the opposite direction as the pixel-coordinate-system in our case,
// that's why we have a minus before the product
double stage_coordinate_x = -scaling.scaleX * 1.e6 * (pixel_coordinate_first_subblock_x - pixelcoordinate_to_be_transformed_x) + stage_coordinate_first_subblock_x;
double stage_coordinate_y = -scaling.scaleY * 1.e6 * (pixel_coordinate_first_subblock_y - pixelcoordinate_to_be_transformed_y) + stage_coordinate_first_subblock_y;
The procedure described here has some issues to be aware of, namely
- So far we asssumed that stage-coordinate system and pixel-coordinate system have same orientation, are both Cartesian and axes are parallel.
- There are multiple choices for the point P (i.e. the point for which we have both stage-coordinates and pixel-coordinates).
So, for 2. we usually have in each (layer-0) subblock given a stage-coordinate, and in addition we have the scene-centers given in XML-metadata. This leaves us with ambiguity to which of those points to base the calculation on. It is by no means guaranteed that all points come out to the same result - however, they usually do come out to the same result.
Bullet 1 is even a bit more involved - there is no defined and easy way to find out about the orientation of the stage-coordinate system. The information needs to be reconstructed from a multitude of places, and describing the detailed procedure is out-of-scope here.
In order to overcome those and other issues, a new data-structure in XML-metadata has been developed - however at this point is it not widely adopted in ZEN and therefore probably not applicable.