Attaching Metadata to Data

The DataComponent class allows attaching arbitrary metadata to any instance of Data. The example below demonstrates this by attaching an ImageInfoDataComponent, which can hold information such as patient ID, patient name, etc., to Data , e.g. a SharedImageSet.

>>> import imfusion
>>> sis = imfusion.SharedImageSet()
>>> sis.components.add(imfusion.ImageInfoDataComponent())
ImageInfoDataComponent(
        patient_id="",
        patient_name="",
    ...
)
>>> sis.components.image_info.patient_id = "my_patient_id"

There is a variety of built-in DataComponent available in the ImFusion SDK. The full list of DataComponent can be obtained with available_data_components().

Note

The available_data_components() function returns all components that are implemented in the ImFusion C++ SDK, including those that are not yet available in the Python bindings and hence do not have a corresponding specialized class in the imfusion Python module.

Regardless of whether Python bindings exist, create_data_component() enables creating every DataComponent in Python:

>>> prop = imfusion.Properties({"patientId": "my_patient_id"})
>>> image_info_data_comp = imfusion.create_data_component("ImageInfoDataComponent", prop)
>>> image_info_data_comp
ImageInfoDataComponent(
        patient_id="my_patient_id",
        patient_name="",
    ...
)

As with any DataComponent, the actual data fields of ImageInfoDataComponent are accessible through Python attributes:

>>> image_info_data_comp.patient_id
'my_patient_id'

Note

There is a difference in naming conventions between the DataComponent’s Python attributes (e.g., patient_id) and the corresponding Properties keys (e.g., patientId). The former are listed in the documentation of the specific class, e.g., ImageInfoDataComponent. To view the keys used in Properties, you can instantiate the respective data component with its default constructor and inspect the parameters of its Properties, as in the example below:

>>> image_info_data_comp = imfusion.create_data_component("ImageInfoDataComponent")
>>> prop: imfusion.Properties = image_info_data_comp.configuration()
>>> prop.params()
['patientId', 'patientName', ...]

Properties may also be used to modify the data component after it has been created:

>>> image_info_data_comp = imfusion.create_data_component("ImageInfoDataComponent")
>>> prop = imfusion.Properties({"patientId": "my_patient_id"})
>>> image_info_data_comp.configure(prop)

Provided the data component is available in our Python bindings, you can of course also instantiate it directly:

>>> image_info_data_comp = imfusion.ImageInfoDataComponent()

The data component system is also extensible from Python. You can create your own data components by subclassing DataComponent:

>>> class MyComponent(imfusion.DataComponent, accessor_name="my_component"):
...     def __init__(self, custom_info: str=""):
...         super().__init__()
...         self.custom_info: str = custom_info
...
...     def configure(self, properties: imfusion.Properties) -> None:
...         self.custom_info = str(properties["custom_info"])
...
...     def configuration(self) -> imfusion.Properties:
...         return imfusion.Properties({"custom_info": self.custom_info})
...
...     def __eq__(self, other: "MyComponent") -> bool:
...         return self.custom_info == other.custom_info
...
...     def __repr__(self) -> str:
...         return f"MyComponent(custom_info={self.custom_info!r})"

With the above class definition, you can attach your customized information to, e.g., any SharedImageSet:

>>> sis = imfusion.SharedImageSet()
>>> sis.components.add(MyComponent(custom_info="my_custom_info"))
MyComponent(custom_info='my_custom_info')
>>> assert sis.components.my_component.custom_info == "my_custom_info"

The value assigned to accessor_name will be used as the name of the Python attribute that is automatically created on the list of data components, i.e., my_component is now an attribute of sis.components.

The methods configure() and configuration() are part of the Configurable interface and are required to support serialization/deserialization of the component. If they are not implemented, the component will not persist when the respective Data instance is, e.g., written to disk.