top of page

Working with Image Registration in ESAPI

Image Registration

As part of the treatment planning process, the target area may be imaged multiple times. The images may be taken using different modalities (e.g., CT and MRI) or taken at different times, such as in adaptive radiotherapy.

The images, however, may not be in the same coordinate system (or "frame of reference"). Yet this is necessary for various radiotherapy activities, such as calculating the total dose delivered to a structure by multiple treatments.

Image registration is the process of mapping (or "transforming") one frame of reference to another. There are two types of image registration: rigid and deformable. In rigid registration, only linear transformations are applied (e.g., translation, rotation, and scaling). A rigid transformation can be represented mathematically by a matrix.

Deformable registration involves nonlinear transformations that can warp one frame of reference to another. Since ESAPI does not provide an interface to deformable registrations, I will not discuss deformable registrations in this blog post. (Note that deformable registrations are accessible through the SmartAdapt API.)

Registrations in ESAPI

Multiple registrations may be associated with a patient, so ESAPI provides the Registrations property in the Patient class. Each Registration object represents a rigid transformation from a source frame of reference to a destination (or registered) frame of reference.

The Registration class contains the SourceFOR and RegisteredFOR properties, which provide the UIDs of the source and registered frame of references. The UID of the desired frame of reference can be obtained through the FOR property in the Image class.

The Registration class also provides the transformation matrix, but typically it will be more convenient to use the TransformPoint or InverseTransformPoint methods. The TransformPoint method takes a point in the source frame of reference and returns the corresponding point in the registered frame of reference. The InverseTransformPoint does the transformation in the opposite direction.

Registration Example

One case where registration is important is in bio-correcting the dose of a structure in a plan sum. Bio-correction in a plan sum requires that the dose in each component plan is bio-corrected independently. The bio-corrected doses are then added together. If the plan sum contains plans with data sets in different frames of reference, the wrong doses may be added together.

As an example, let's say that you have a plan sum with two plans, each with a data set in a different frame of reference. To get the dose of a structure in the first plan, you can use the algorithm shown in the code sample in the Varian Developers Forum. It uses a SegmentProfile to efficiently obtain the voxels that are inside of the structure of interest.

To get the dose of the same structure in the second plan, we will first write a class called RegisteredSegmentProfile that transforms a segment profile using the given Registration object:

public class RegisteredSegmentProfile
    private readonly Registration _registration;

    public RegisteredSegmentProfile(Registration registration)
        _registration = registration;

    public SegmentProfile GetRegisteredSegmentProfile(SegmentProfile profile)
        VVector start = _registration.TransformPoint(profile.First().Position);
        VVector stop = _registration.TransformPoint(profile.Last().Position);
        VVector step = (stop - start) / (profile.Count - 1);
        BitArray bitArray = new BitArray(profile.Select(p => p.Value).ToArray());
        return new SegmentProfile(start, step, bitArray);

The constructor takes the Registration object that registers the image of the second plan to the first plan. If you have the registration in the opposite direction, you will need to use the InverseTransformPoint method in the GetRegisteredSegmentProfile method.

Notice that we only have to transform the first and last positions of the segment profile, not each point in the segment profile. The reason is that, in rigid transformations, lines are preserved. This is not necessarily the case in deformable transformations.

We can now use this class to obtain the dose at the registered points in the second plan. The code is similar to that in the Varian Developers Forum. You will want to extend it, however, to handle cases where the plan sum contains more than just two plans.


The example above assumes that if a point is inside the structure in the first plan, the corresponding point is also inside the structure in the second plan. This will be the case if the data sets are perfectly registered. If they're not, some of the doses you get in the second plan may be outside of the structure. Conversely, some of the doses outside the structure in the first plan may be inside the structure in the second plan.

Another caveat deals with multiple registrations. How do you find the correct one for two specific plans in a plan sum? You can find it by comparing the SourceFOR and RegisteredFOR properties, but what if there are more than one registration between the same images? It turns out that a plan sum can only have one registration between two specific images, so you don't have to worry about this problem.

Related Posts

See All

A few months ago, I introduced ESAPI Essentials—a toolkit for working with ESAPI (available via NuGet). I've recently added one major feature: asynchronous access to ESAPI for binary plugin scripts. Y

A few months ago, Matt Schmidt started the ESAPI subreddit. It's another useful resource for finding and discussing ESAPI-related topics. According to the description, This community is to post and di

If, for whatever reason, you need to dump out all of the data for a patient from ESAPI, there's a quick and dirty way to do it. Well, there are probably several ways to do it, but here's one I've foun

bottom of page