"# The Timage data can be manipulated through an ndarray:\n",
"fixed.data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### The TImage domain"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Domain(2580 x 2987, offset=0, tx=7, storage=mem)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# The domain of an image is represented by the Domain object, which\n",
"# is responsible for all coordinate operations in TIRL.\n",
"fixed.domain"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The string representation of a Domain object contains:\n",
"\n",
"- domain shape (as a rectangular grid)\n",
"- number of internal domain transformations (*offset*)\n",
"- number of external domain transformations (*chain*)\n",
"- where the coordinates are stored for further calculations (memory or disk)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 0, 0],\n",
" [ 0, 1],\n",
" [ 0, 2],\n",
" ...,\n",
" [2579, 2984],\n",
" [2579, 2985],\n",
" [2579, 2986]], dtype=int16)"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Retrieve the pixel coordinates of the fixed image:\n",
"fixed.domain.get_voxel_coordinates()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Domain transformations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The pixel coordinates are mapped into *physical space* (a term in TIRL) by a\n",
"transformation chain. The transformation chain consists of elementary\n",
"Transformation objects. Consecutive Transformations map the coordinates in\n",
"the order they appear in the chain, e.g. tx1 will map the pixel coordinates,\n",
"and tx2 will map the results of the first transformation. TIRL divides the\n",
"transformation chain into two parts: offset and chain. The \"offset\" part\n",
"consists of \"internal\" transformations that are added automatically by TIRL\n",
"on certain TImage operations (e.g. downsampling/upsampling) to keep the image\n",
"fixed in physical space. The \"chain\" stores \"external\" transformations that\n",
"are added by the user who writes the registration script."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Internal transformations of the fixed image:\n",
"Chain[]\n"
]
}
],
"source": [
"print(\"Internal transformations of the fixed image:\")\n",
"print(fixed.domain.offset)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The empty Chain object indicates that the fixed image has no internal transformations. This is because the registration script did not preform any image operations on the output, that would have changed the physical coordinates of the TImage, such as downscaling/upscaling or padding."
"Note that the distinction between internal and external transformations and the splitting of the chain is based on purely practical reasons, allowing certain parts of TIRL to find transformations more easily. The two parts of the chain can be merged and assigned entirely to the \"chain\" attribute (leaving the \"offset\" chain empty) without any change in the mapping. In many cases, the offset part will be empty."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Slicing chains"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The \\[:\\] slicer expression creates a new Chain object from an existing Chain object. The new chain will share all existing Transformations with the old chain, but removing or adding Transformations to one will leave the other unaffected. Changing the parameters of the shared tr\n",
"\n",
"This can be used instead of fixed.domain.chain.copy(), when it is unnecessary to duplicate the transformations in memory, which may include a large deformation field (>100 MB).\n",
"\n",
"The slicing syntax can be used more generally to extract certain parts from a transformation chain, and paste it elsewhere."
"The \\[:\\] slicer expression creates a new Chain object from an existing Chain object. The new chain will share all existing Transformations with the old chain, but removing or adding Transformations to the new chain will leave the old one unaffected.\n",
"\n",
"We use this instead of fixed.domain.chain.copy(), because it is unnecessary to duplicate the transformations, including a large deformation field (>100 MB)."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"# Skip this step if the alternative fixed image has the same shape as the original fixed image.\n",
"\n",
"# Otherwise, we need to prepend the transformation chain with a transformation that scales the\n",
"# pixels of the alternative fixed image to the pixels of the original fixed image.\n",
"### Applying transformations to obtain registered images"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As all transformations are in place, the alt_fixed and alt_moving TImages are technically registered, because their points map onto the same domain. To obtain actual images that look alike, one of the TImages need to be evaluated on the other's domain, and the decision is up to you."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Choose either this:"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [],
"source": [
"# Register the moving image to the fixed image\n",
"# This uses the existing transformation chain for the fixed image, and the inverse of the moving image.\n",
"# Since the moving chain consists of only linear transformations, the inversion is quick, and resampling\n",