Advanced Structure Resolution Manipulation with ESAPI using the Simple Structure Builder
- mschmidt33
- Feb 11
- 8 min read
Background
One of the first ESAPI applications introduced by Gateway Scripts was the Simple Structure Builder, an open-source Boolean-based structure creation tool designed to streamline structure manipulation in Eclipse Scripting API (ESAPI). The tool allows users to perform Boolean operations on structures and automate various structure-related tasks within the treatment planning system.
In a previous post, Structure Builder - An Open-Source Boolean-Based Structure Creation Tool, the core functionality of the Simple Structure Builder is introduced and its ability to automate structure creation through scripting discussed. Since its initial release, the tool has evolved to support additional features such as the ability to remove steps, define structure details such as structure codes and colors, and integration with the Structure ID Dictionary in Visual Scripting.
In this post, the Simple Structure Builder is improved further through the introduction of a new feature for advanced structure manipulation—the ability to convert structures between high and low resolution. This feature expands the Simple Structure Builder’s capabilities by allowing users to refine structure resolution based on clinical needs, leveraging a method inspired by recent scripting research.
Introduction
A common challenge when working with Boolean contour operations in Eclipse is that structures with different segment resolutions (high vs low) cannot be combined. High-resolution structures are typically used for small, high precision anatomical features such as optic nerves, cochlea, and brachial plexus.

From the Image Registration and Segmentation Reference Guide:
The default segment resolution of structures is low (half the resolution of the related image). You can, however, change to high segmentation resolution (full resolution of the related image) if a structure represents a small volume with great significance in planning or plan evaluation. Such small volumes can be optic nerves, artificial implants, screws, or similar.
The manual continues to describe how the high resolution segmentation is not reversable and that structure templates use low resolution for structures.
The Simple Structure Builder application takes into account differences in resolution. When the method runs to execute structure creation, OnRunSteps(), the steps configured in the UI or imported from a template are iterated. During this iteration, there is a number of places where structure resolution is mentioned. First, if the selected base structure is high resolution, then the new structure is immediately converted to high resolution.
//if base structure is high resolution make the new structure high resolution.
if (baseStructure.IsHighResolution)
{
newStructure.ConvertToHighResolution();
}This is primarily considered as the application moves into the step operations of Margin and Asymmetric Margin. If the boolean operation applied includes AND, OR, or SUB, the structure resolution of the target structure is determined, and if the resolution of the target structure does not match that of the base structure an error is displayed to the user.
var targetStructure = _structureSet.Structures.First(s => s.Id.Equals(step.SelectedTargetStructure));
//check that target structure and base structure are the same resolution.
if ((targetStructure.IsHighResolution && !baseStructure.IsHighResolution) || (!targetStructure.IsHighResolution && baseStructure.IsHighResolution))
{
System.Windows.MessageBox.Show($"Base Structure ({baseStructure.Id} - {(baseStructure.IsHighResolution ? "High Res" : "Standard Res")}) and Target Structure ({targetStructure.Id} - {(targetStructure.IsHighResolution ? "High Res" : "Standard Res")}) are not the same resolution.\nPlease close the app and update the structures.");
return;
}
//Other steps require the operation and a target structure.
if (targetStructure.IsHighResolution && !newStructure.IsHighResolution)
{
newStructure.ConvertToHighResolution();
}
//Convert target to high resolution if it is not BODY
if (newStructure.IsHighResolution && !targetStructure.IsHighResolution && targetStructure.DicomType != "EXTERNAL")
{
targetStructure.ConvertToHighResolution();
}However, an issue occurs where the tool incorrectly flags mismatched resolutions even when the base and target structures match. This blog post aims to refine structure resolution handling to (a) prevent unintended modifications to existing structure resolutions and (b) allow Boolean operations on structures of either resolution.
Updates to Structure Builder
The first step to updating any application is to create a new branch in the source control management system. The change to this application can be found in the hires branch on github.
Notification of High Resolution
The first step in handling high resolution structures is to notify the user of the current resolution of the base and target structures. Previously, the structure selection drop-downs used List<string>, which only stored structure names. To track resolution status, we introduce a new StructureModel class that stores both the Structure ID and its high-resolution flag.
public class StructureModel
{
public string StructureId { get; set; }
public bool bHiRes { get; set; }
/// <summary>
/// Constructor getting properties from eSAPI structure
/// </summary>
/// <param name="structure">ESAPI structure.</param>
public StructureModel(Structure structure)
{
StructureId = structure.Id;
bHiRes = structure.IsHighResolution;
}
/// <summary>
/// parameterless constructor to add properties
/// </summary>
public StructureModel()
{
}
}This will be facilitated by changing the Structures property and the SelectedBaseStructure and SelectedTargetStructure properties in the StructureStepViewModel to the new StructureModel class.
private StructureModel _selectedBaseStructure;
public StructureModel SelectedBaseStructure
{
get { return _selectedBaseStructure; }
set { SetProperty(ref _selectedBaseStructure,value); }
}
private StructureModel _selectedTargetStructure;
public StructureModel SelectedTargetStructure
{
get { return _selectedTargetStructure; }
set { SetProperty(ref _selectedTargetStructure,value); }
}
public ObservableCollection<StructureModel> Structures { get; set; }This leads to a few errors in the code that must also be addressed, including updating the BaseStructure and TargetStructure property in the StructureCreationModel class to also be of type StructureModel, and adding a new StructureModel class to the Structures collection instead of simply the structure's Id property. Follow the red error blocks to discover and resolve any errors. Please note that the original StructureCreationModel class is copied to a new class in the same file StructureCreationModelTransfer. This new model should be used in all JSON Conversion in or out of the application so users won't have to change their templates.
Converters are a great way to manifest changes in properties into stylistic or visualization updates.
In the view, we want the tool to show a visualization that a structure is high resolution on selection. If the structure is standard resolution, then nothing needs to be shown. One way to control the visibility is using converters. Converters are a great way to manifest changes in properties into stylistic or visualization updates. In the converters folder a class called BoolVisibility Converter is created. This class will implement interface IValueConverter which contains two properties, Convert and ConvertBack. The code will look as follows:
public class BoolVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return Visibility.Collapsed;
}
else
{
StructureModel smValue = value as StructureModel;
if (smValue.bHiRes)
{
return Visibility.Visible;
}
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}This converter can now be added to the UserControl Resources in the StructureStepView.xaml file. The other converters are already in the Resource Dictionary.
<UserControl.Resources>
<ResourceDictionary>
<converters:MarginVisibilityConverter x:Key="localMarginVisibiltyConverter"/>
<converters:TargetStructureVisibilityConverter x:Key="localTargetVisiblityConverter"/>
<converters:AsymmetricMarginVisibilityConverter x:Key="localAsymmetricMarginVisibilityConverter"/>
<converters:BoolVisibilityConverter x:Key="localBoolVisibilityConverter"/>
</ResourceDictionary>
</UserControl.Resources>Within the UI, the Combobox holding the Base Structure and Target Structure will need to be updated. Now that the ComboBox is not binded directly to a string object, the DisplayMemberPath attribute can display a specific property of a class object bound to the ItemsSource of the ComboBox control. Additionally, a TextBlock is created that has a Visibility attribute bound to the SelectedBaseStructure property using a converter to control whether the text is visible or not. Similar changes should be made for the Target Structure as well.
<StackPanel Grid.Column="1">
<TextBlock Text="Base Structure" Margin="2" HorizontalAlignment="Center" FontWeight="Bold"/>
<ComboBox ItemsSource="{Binding Structures}" SelectedItem="{Binding SelectedBaseStructure}" Margin="2"
DisplayMemberPath="StructureId"/>
<TextBlock Text="Hi" Foreground="Green" Margin="5,2,2,2" FontWeight="Bold"
Visibility="{Binding SelectedBaseStructure, Converter={StaticResource localBoolVisibilityConverter}}"/>
</StackPanel>the DisplayMemberPath attribute can display a specific property of a class object bound to the ItemsSource of the ComboBox control.
Testing the application, a new row can be generated with a mixture of high resolution and low resolution structures can be selected, and the Hi resolution flag should become available only when high resolution structures are selected.

Adding New Operations
In order to facilitate making structures high resolution or low resolution, two new operations will be added to the Operation ComboBox. These are HiRes and LowRes, and they are intended to convert a structure to high resolution, and low resolution, respectively. These operations are created in one place, but they are utilized throughout the entire application. The selections are instantiated in the InitializeCollections method in the StructureStepViewModel.
private void InitializeCollections()
{
foreach(var structure in _structureSet.Structures.OrderByDescending(st=>st.DicomType.Contains("TV")).ThenBy(st=>st.Id))
{
Structures.Add(new StructureModel(structure));
}
Operations.Add("Margin");
Operations.Add("Asymmetric Margin");
Operations.Add("And");
Operations.Add("Or");
Operations.Add("Sub");
Operations.Add("HiRes");
Operations.Add("LoRes");
}These new options (HiRes and LoRes) do not require selecting a target structure or margin, so the TargetStructureVisibilityConverter must be updated to handle them appropriately. Therefore, the TargetStructureVisibilityConverter is updated to include a value whose string contains "Res" to include both new options.
The OnRunSteps method is the next method to modify. The new methods will be wedged right inside of the section of code that is checking the step.SelectedOperation for "Asymmetric Margin" and the else statement. Place the following code immediately after the else if (step.SelectedOperation == "Asymmetric Margin") closing parentheses.
else if(step.SelectedOperation == "HiRes")
{
//if base structure is already hi res, then make hi res and copy.
if (baseStructure.IsHighResolution)
{
newStructure.ConvertToHighResolution();
newStructure.SegmentVolume = baseStructure.SegmentVolume;
}
else
{
//if base structure is not hi res, copy then make hi res.
newStructure.SegmentVolume = baseStructure.SegmentVolume;
newStructure.ConvertToHighResolution();
}
}
else if(step.SelectedOperation == "LoRes")
{
if (baseStructure.IsHighResolution)
{
//loop through contours and copy contours to new image.
for(int slice = 0; slice < _structureSet.Image.ZSize; slice++)
{
var baseContours = baseStructure.GetContoursOnImagePlane(slice);
if (baseContours.Any())
{
foreach(var contour in baseContours)
{
baseStructure.AddContourOnImagePlane(contour, slice);
}
}
}
}
else
{
//if structure is already low resolution, simply copy.
newStructure.SegmentVolume = baseStructure.SegmentVolume;
}
}Testing the Application
Now that the structure resolution handling is improved, testing is necessary to confirm that Boolean operations are performed correctly between high and low-resolution structures. The message relayed to the user has been updated. The tool now requests the user to use the HiRes and LoRes conversion tools.

This same request to compare a low resolution and high resolution should first require the low resolution structure to be converted to high resolution. Once the high resolution structure is generated, it can perform boolean operations with another high resolution structure. Please note, usually this intermediary step would be marked with Temp in order to not save this structure back to the structure set, but for review purposes we can leave the structure in the creation. Making the high resolution structure low resolution and then performing a similar operation with the low resolution structure should work similarly.

In the Contouring workspace, these structures can be reviewed. Note that the tools would have created a high resolution structure to mirror the Orbit LT and a low resolution structure to mirror the Optic Nerve RT. Finally, the Optics_LT and Optics_RT structures are successfully created. If reviewed closely, differences in the contours from the resolution can be seen.

Conclusion
The addition of high and low-resolution conversion capabilities to the Simple Structure Builder significantly enhances its flexibility for structure manipulation in ESAPI. By addressing the challenges of Boolean operations between mismatched structure resolutions, this update ensures that users can seamlessly integrate both high and low-resolution structures into their workflows without unnecessary errors or manual adjustments.
Through the introduction of the StructureModel class, we now track resolution status more effectively, and with the BoolVisibilityConverter, users receive clear visual feedback when working with high-resolution structures. The new HiRes and LoRes operations empower users to modify structure resolution as needed, improving compatibility across different contouring scenarios.
With these improvements, the Simple Structure Builder continues to evolve as a powerful tool for automating structure creation and manipulation within ESAPI. Future developments could explore additional refinement of resolution handling, further integration with Visual Scripting, or enhancements in user interaction and validation.
If you're interested in testing these new features or contributing to the project, you can check out the latest updates on the Simple Structure Builder GitHub page. As always, feedback is welcome—let me know how these changes impact your scripting workflow!






Comments