Skip to content

Instance

pyorthanc.Instance

Bases: Resource

Represent an instance that is in an Orthanc server

This object has many getters that allow the user to retrieve metadata or the entire DICOM file of the Instance

Source code in pyorthanc/_resources/instance.py
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
class Instance(Resource):
    """Represent an instance that is in an Orthanc server

    This object has many getters that allow the user to retrieve metadata
    or the entire DICOM file of the Instance
    """

    def get_dicom_file_content(self) -> bytes:
        """Retrieves DICOM file

        This method retrieves bytes corresponding to DICOM file.

        Returns
        -------
        bytes
            Bytes corresponding to DICOM file

        Examples
        --------
        ```python
        from pyorthanc import Instance
        instance = Instance('instance_identifier', Orthanc('http://localhost:8042'))

        dicom_file_bytes = instance.get_dicom_file_content()
        with open('your_path', 'wb') as file_handler:
            file_handler.write(dicom_file_bytes)
        ```
        """
        return self.client.get_instances_id_file(self.id_)

    def download(self, filepath: Union[str, BinaryIO], with_progres: bool = False) -> None:
        """Download the DICOM file to a target path or buffer

        This method is an alternative to the `.get_dicom_file_content()` method for large files.
        The `.get_dicom_file_content()` method will pull all the data in a single GET call,
        while `.download()` stream the data to a file or a buffer.
        Favor the `.download()` method to avoid timeout and memory issues.

        Examples
        --------
        ```python
        from pyorthanc import Orthanc, Instance
        instance = Instance('instance_identifier', Orthanc('http://localhost:8042'))

        # Download the dicom file
        instance.download('instance.dcm')

        # Download the file and show progress
        instance.download('instance.dcm', with_progres=True)

        # Or download in a buffer in memory
        buffer = io.BytesIO()
        instance.download(buffer)
        # Now do whatever you want to do
        buffer.seek(0)
        dicom_bytes = buffer.read()
        ```
        """
        self._download_file(f'{self.client.url}/instances/{self.id_}/file', filepath, with_progres)

    @property
    def uid(self) -> str:
        """Get SOPInstanceUID"""
        return self._get_main_dicom_tag_value('SOPInstanceUID')

    def get_main_information(self) -> Dict:
        """Get instance information

        Returns
        -------
        Dict
            Dictionary with tags as key and information as value
        """
        return self.client.get_instances_id(self.id_)

    @property
    def file_size(self) -> int:
        """Get the file size

        The output is in bytes. Divide by 1_000_000 to
        get it in Mb.

        Returns
        -------
        int
            The file size in bytes.
        """
        return self.get_main_information()['FileSize']

    @property
    def creation_date(self) -> datetime:
        """Get creation date

        The date have precision to the second.

        Returns
        -------
        datetime
            Creation Date
        """
        date_string = self._get_main_dicom_tag_value('InstanceCreationDate')
        time_string = self._get_main_dicom_tag_value('InstanceCreationTime')

        return util.make_datetime_from_dicom_date(date_string, time_string)

    @property
    def series_identifier(self) -> str:
        """Get the parent series identifier"""
        return self.get_main_information()['ParentSeries']

    @property
    def parent_series(self) -> Series:
        from . import Series
        return Series(self.series_identifier, self.client)

    @property
    def parent_study(self) -> Study:
        return self.parent_series.parent_study

    @property
    def parent_patient(self) -> Patient:
        return self.parent_study.parent_patient

    @property
    def acquisition_number(self) -> int:
        return int(self._get_main_dicom_tag_value('AcquisitionNumber'))

    @property
    def image_index(self) -> int:
        return int(self._get_main_dicom_tag_value('ImageIndex'))

    @property
    def image_orientation_patient(self) -> List[float]:
        orientation = self._get_main_dicom_tag_value('ImageOrientationPatient')

        return [float(i) for i in orientation.split('\\')]

    @property
    def image_position_patient(self) -> List[float]:
        position = self._get_main_dicom_tag_value('ImagePositionPatient')

        return [float(i) for i in position.split('\\')]

    @property
    def image_comments(self) -> str:
        return self._get_main_dicom_tag_value('ImageComments')

    @property
    def instance_number(self) -> int:
        return int(self._get_main_dicom_tag_value('InstanceNumber'))

    @property
    def number_of_frames(self) -> int:
        return int(self._get_main_dicom_tag_value('NumberOfFrames'))

    @property
    def temporal_position_identifier(self) -> str:
        return self._get_main_dicom_tag_value('TemporalPositionIdentifier')

    @property
    def first_level_tags(self) -> Any:
        """Get first level tags"""
        return self.client.get_instances_id_content_tags_path(self.id_, '')

    @property
    def tags(self) -> Dict:
        """Get tags"""
        return dict(self.client.get_instances_id_tags(self.id_))

    @property
    def simplified_tags(self) -> Dict:
        """Get simplified tags"""
        return dict(self.client.get_instances_id_tags(self.id_, params={'simplify': True}))

    @property
    def labels(self) -> List[str]:
        """Get instance labels"""
        return self.get_main_information()['Labels']

    def add_label(self, label: str) -> None:
        """Add label to resource"""
        self.client.put_instances_id_labels_label(self.id_, label)

    def remove_label(self, label):
        """Remove label from resource"""
        self.client.delete_instances_id_labels_label(self.id_, label)

    def get_content_by_tag(self, tag: str) -> Any:
        """Get content by tag

        Parameters
        ----------
        tag
            Tag like 'ManufacturerModelName' or '0008-1090' or a group element like '' or '0008-1110/0/0008-1150'.

        Returns
        -------
        Any
            Content corresponding to specified tag.
        """
        result = self.client.get_instances_id_content_path(id_=self.id_, path=tag)

        try:
            return result.decode('utf-8').strip().replace('\x00', '')
        except AttributeError:
            return result

    def anonymize(self, remove: List = None, replace: Dict = None, keep: List = None,
                  keep_private_tags: bool = False, keep_source: bool = True,
                  private_creator: str = None, force: bool = False, dicom_version: str = None) -> bytes:
        """Anonymize Instance

        If no error has been raise, then it creates a new anonymous instance.
        Documentation: https://book.orthanc-server.com/users/anonymization.html

        Parameters
        ----------
        remove
            List of tag to remove
        replace
            Dictionary of {tag: new_content}
        keep
            List of tag to keep unchanged
        force
            Some tags can't be changed without forcing it (e.g. SOPInstanceUID) for security reason
        keep_private_tags
            If True, keep the private tags from the DICOM instances.
        keep_source
            If False, instructs Orthanc to the remove original resources.
            By default, the original resources are kept in Orthanc.
        private_creator
            The private creator to be used for private tags in replace.
        dicom_version
            Version of the DICOM standard to be used for anonymization.
            Check out configuration option DeidentifyLogsDicomVersion for possible values.

        Returns
        -------
        bytes
            Raw bytes of the anonymized instance.
        """
        remove = [] if remove is None else remove
        replace = {} if replace is None else replace
        keep = [] if keep is None else keep

        data = {
            'Remove': remove,
            'Replace': replace,
            'Keep': keep,
            'Force': force,
            'KeepPrivateTags': keep_private_tags,
            'KeepSource': keep_source,
        }
        if private_creator is not None:
            data['PrivateCreator'] = private_creator
        if dicom_version is not None:
            data['DicomVersion'] = dicom_version

        return self.client.post_instances_id_anonymize(self.id_, data)

    def modify(self, remove: List = None, replace: Dict = None, keep: List = None,
               remove_private_tags: bool = False, keep_source: bool = True,
               private_creator: str = None, force: bool = False) -> bytes:
        """Modify Instance

        If no error has been raise, then it creates a new modified instance.
        Documentation: https://book.orthanc-server.com/users/anonymization.html

        Parameters
        ----------
        remove
            List of tag to remove
        replace
            Dictionary of {tag: new_content}
        keep
            Keep the original value of the specified tags, to be chosen among the StudyInstanceUID,
            SeriesInstanceUID and SOPInstanceUID tags. Avoid this feature as much as possible,
            as this breaks the DICOM model of the real world.
        force
            Some tags can't be changed without forcing it (e.g. SOPInstanceUID) for security reason
        remove_private_tags
            If True, remove the private tags from the DICOM instances.
        keep_source
            If False, instructs Orthanc to the remove original resources.
            By default, the original resources are kept in Orthanc.
        private_creator
            The private creator to be used for private tags in replace.

        Returns
        -------
        bytes
            Raw bytes of the modified instance.
        """
        remove = [] if remove is None else remove
        replace = {} if replace is None else replace
        keep = [] if keep is None else keep

        if 'SOPInstanceUID' in replace and not force:
            raise errors.ModificationError('If SOPInstanceUID is replaced, `force` must be `True`')

        data = {
            'Remove': remove,
            'Replace': replace,
            'Keep': keep,
            'Force': force,
            'RemovePrivateTags': remove_private_tags,
            'KeepSource': keep_source,
        }
        if private_creator is not None:
            data['PrivateCreator'] = private_creator

        return self.client.post_instances_id_modify(self.id_, data)

    def get_pydicom(self) -> pydicom.FileDataset:
        """Retrieve a pydicom.FileDataset object corresponding to the instance."""
        return util.get_pydicom(self.client, self.id_)

acquisition_number: int property

creation_date: datetime property

Get creation date

The date have precision to the second.

Returns:

Type Description
datetime

Creation Date

file_size: int property

Get the file size

The output is in bytes. Divide by 1_000_000 to get it in Mb.

Returns:

Type Description
int

The file size in bytes.

first_level_tags: Any property

Get first level tags

image_comments: str property

image_index: int property

image_orientation_patient: List[float] property

image_position_patient: List[float] property

instance_number: int property

labels: List[str] property

Get instance labels

number_of_frames: int property

parent_patient: Patient property

parent_series: Series property

parent_study: Study property

series_identifier: str property

Get the parent series identifier

simplified_tags: Dict property

Get simplified tags

tags: Dict property

Get tags

temporal_position_identifier: str property

uid: str property

Get SOPInstanceUID

add_label(label)

Add label to resource

Source code in pyorthanc/_resources/instance.py
194
195
196
def add_label(self, label: str) -> None:
    """Add label to resource"""
    self.client.put_instances_id_labels_label(self.id_, label)

anonymize(remove=None, replace=None, keep=None, keep_private_tags=False, keep_source=True, private_creator=None, force=False, dicom_version=None)

Anonymize Instance

If no error has been raise, then it creates a new anonymous instance. Documentation: https://book.orthanc-server.com/users/anonymization.html

Parameters:

Name Type Description Default
remove List

List of tag to remove

None
replace Dict

Dictionary of {tag: new_content}

None
keep List

List of tag to keep unchanged

None
force bool

Some tags can't be changed without forcing it (e.g. SOPInstanceUID) for security reason

False
keep_private_tags bool

If True, keep the private tags from the DICOM instances.

False
keep_source bool

If False, instructs Orthanc to the remove original resources. By default, the original resources are kept in Orthanc.

True
private_creator str

The private creator to be used for private tags in replace.

None
dicom_version str

Version of the DICOM standard to be used for anonymization. Check out configuration option DeidentifyLogsDicomVersion for possible values.

None

Returns:

Type Description
bytes

Raw bytes of the anonymized instance.

Source code in pyorthanc/_resources/instance.py
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
def anonymize(self, remove: List = None, replace: Dict = None, keep: List = None,
              keep_private_tags: bool = False, keep_source: bool = True,
              private_creator: str = None, force: bool = False, dicom_version: str = None) -> bytes:
    """Anonymize Instance

    If no error has been raise, then it creates a new anonymous instance.
    Documentation: https://book.orthanc-server.com/users/anonymization.html

    Parameters
    ----------
    remove
        List of tag to remove
    replace
        Dictionary of {tag: new_content}
    keep
        List of tag to keep unchanged
    force
        Some tags can't be changed without forcing it (e.g. SOPInstanceUID) for security reason
    keep_private_tags
        If True, keep the private tags from the DICOM instances.
    keep_source
        If False, instructs Orthanc to the remove original resources.
        By default, the original resources are kept in Orthanc.
    private_creator
        The private creator to be used for private tags in replace.
    dicom_version
        Version of the DICOM standard to be used for anonymization.
        Check out configuration option DeidentifyLogsDicomVersion for possible values.

    Returns
    -------
    bytes
        Raw bytes of the anonymized instance.
    """
    remove = [] if remove is None else remove
    replace = {} if replace is None else replace
    keep = [] if keep is None else keep

    data = {
        'Remove': remove,
        'Replace': replace,
        'Keep': keep,
        'Force': force,
        'KeepPrivateTags': keep_private_tags,
        'KeepSource': keep_source,
    }
    if private_creator is not None:
        data['PrivateCreator'] = private_creator
    if dicom_version is not None:
        data['DicomVersion'] = dicom_version

    return self.client.post_instances_id_anonymize(self.id_, data)

download(filepath, with_progres=False)

Download the DICOM file to a target path or buffer

This method is an alternative to the .get_dicom_file_content() method for large files. The .get_dicom_file_content() method will pull all the data in a single GET call, while .download() stream the data to a file or a buffer. Favor the .download() method to avoid timeout and memory issues.

Examples:

from pyorthanc import Orthanc, Instance
instance = Instance('instance_identifier', Orthanc('http://localhost:8042'))

# Download the dicom file
instance.download('instance.dcm')

# Download the file and show progress
instance.download('instance.dcm', with_progres=True)

# Or download in a buffer in memory
buffer = io.BytesIO()
instance.download(buffer)
# Now do whatever you want to do
buffer.seek(0)
dicom_bytes = buffer.read()
Source code in pyorthanc/_resources/instance.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def download(self, filepath: Union[str, BinaryIO], with_progres: bool = False) -> None:
    """Download the DICOM file to a target path or buffer

    This method is an alternative to the `.get_dicom_file_content()` method for large files.
    The `.get_dicom_file_content()` method will pull all the data in a single GET call,
    while `.download()` stream the data to a file or a buffer.
    Favor the `.download()` method to avoid timeout and memory issues.

    Examples
    --------
    ```python
    from pyorthanc import Orthanc, Instance
    instance = Instance('instance_identifier', Orthanc('http://localhost:8042'))

    # Download the dicom file
    instance.download('instance.dcm')

    # Download the file and show progress
    instance.download('instance.dcm', with_progres=True)

    # Or download in a buffer in memory
    buffer = io.BytesIO()
    instance.download(buffer)
    # Now do whatever you want to do
    buffer.seek(0)
    dicom_bytes = buffer.read()
    ```
    """
    self._download_file(f'{self.client.url}/instances/{self.id_}/file', filepath, with_progres)

get_content_by_tag(tag)

Get content by tag

Parameters:

Name Type Description Default
tag str

Tag like 'ManufacturerModelName' or '0008-1090' or a group element like '' or '0008-1110/0/0008-1150'.

required

Returns:

Type Description
Any

Content corresponding to specified tag.

Source code in pyorthanc/_resources/instance.py
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
def get_content_by_tag(self, tag: str) -> Any:
    """Get content by tag

    Parameters
    ----------
    tag
        Tag like 'ManufacturerModelName' or '0008-1090' or a group element like '' or '0008-1110/0/0008-1150'.

    Returns
    -------
    Any
        Content corresponding to specified tag.
    """
    result = self.client.get_instances_id_content_path(id_=self.id_, path=tag)

    try:
        return result.decode('utf-8').strip().replace('\x00', '')
    except AttributeError:
        return result

get_dicom_file_content()

Retrieves DICOM file

This method retrieves bytes corresponding to DICOM file.

Returns:

Type Description
bytes

Bytes corresponding to DICOM file

Examples:

from pyorthanc import Instance
instance = Instance('instance_identifier', Orthanc('http://localhost:8042'))

dicom_file_bytes = instance.get_dicom_file_content()
with open('your_path', 'wb') as file_handler:
    file_handler.write(dicom_file_bytes)
Source code in pyorthanc/_resources/instance.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
def get_dicom_file_content(self) -> bytes:
    """Retrieves DICOM file

    This method retrieves bytes corresponding to DICOM file.

    Returns
    -------
    bytes
        Bytes corresponding to DICOM file

    Examples
    --------
    ```python
    from pyorthanc import Instance
    instance = Instance('instance_identifier', Orthanc('http://localhost:8042'))

    dicom_file_bytes = instance.get_dicom_file_content()
    with open('your_path', 'wb') as file_handler:
        file_handler.write(dicom_file_bytes)
    ```
    """
    return self.client.get_instances_id_file(self.id_)

get_main_information()

Get instance information

Returns:

Type Description
Dict

Dictionary with tags as key and information as value

Source code in pyorthanc/_resources/instance.py
80
81
82
83
84
85
86
87
88
def get_main_information(self) -> Dict:
    """Get instance information

    Returns
    -------
    Dict
        Dictionary with tags as key and information as value
    """
    return self.client.get_instances_id(self.id_)

get_pydicom()

Retrieve a pydicom.FileDataset object corresponding to the instance.

Source code in pyorthanc/_resources/instance.py
328
329
330
def get_pydicom(self) -> pydicom.FileDataset:
    """Retrieve a pydicom.FileDataset object corresponding to the instance."""
    return util.get_pydicom(self.client, self.id_)

modify(remove=None, replace=None, keep=None, remove_private_tags=False, keep_source=True, private_creator=None, force=False)

Modify Instance

If no error has been raise, then it creates a new modified instance. Documentation: https://book.orthanc-server.com/users/anonymization.html

Parameters:

Name Type Description Default
remove List

List of tag to remove

None
replace Dict

Dictionary of {tag: new_content}

None
keep List

Keep the original value of the specified tags, to be chosen among the StudyInstanceUID, SeriesInstanceUID and SOPInstanceUID tags. Avoid this feature as much as possible, as this breaks the DICOM model of the real world.

None
force bool

Some tags can't be changed without forcing it (e.g. SOPInstanceUID) for security reason

False
remove_private_tags bool

If True, remove the private tags from the DICOM instances.

False
keep_source bool

If False, instructs Orthanc to the remove original resources. By default, the original resources are kept in Orthanc.

True
private_creator str

The private creator to be used for private tags in replace.

None

Returns:

Type Description
bytes

Raw bytes of the modified instance.

Source code in pyorthanc/_resources/instance.py
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
def modify(self, remove: List = None, replace: Dict = None, keep: List = None,
           remove_private_tags: bool = False, keep_source: bool = True,
           private_creator: str = None, force: bool = False) -> bytes:
    """Modify Instance

    If no error has been raise, then it creates a new modified instance.
    Documentation: https://book.orthanc-server.com/users/anonymization.html

    Parameters
    ----------
    remove
        List of tag to remove
    replace
        Dictionary of {tag: new_content}
    keep
        Keep the original value of the specified tags, to be chosen among the StudyInstanceUID,
        SeriesInstanceUID and SOPInstanceUID tags. Avoid this feature as much as possible,
        as this breaks the DICOM model of the real world.
    force
        Some tags can't be changed without forcing it (e.g. SOPInstanceUID) for security reason
    remove_private_tags
        If True, remove the private tags from the DICOM instances.
    keep_source
        If False, instructs Orthanc to the remove original resources.
        By default, the original resources are kept in Orthanc.
    private_creator
        The private creator to be used for private tags in replace.

    Returns
    -------
    bytes
        Raw bytes of the modified instance.
    """
    remove = [] if remove is None else remove
    replace = {} if replace is None else replace
    keep = [] if keep is None else keep

    if 'SOPInstanceUID' in replace and not force:
        raise errors.ModificationError('If SOPInstanceUID is replaced, `force` must be `True`')

    data = {
        'Remove': remove,
        'Replace': replace,
        'Keep': keep,
        'Force': force,
        'RemovePrivateTags': remove_private_tags,
        'KeepSource': keep_source,
    }
    if private_creator is not None:
        data['PrivateCreator'] = private_creator

    return self.client.post_instances_id_modify(self.id_, data)

remove_label(label)

Remove label from resource

Source code in pyorthanc/_resources/instance.py
198
199
200
def remove_label(self, label):
    """Remove label from resource"""
    self.client.delete_instances_id_labels_label(self.id_, label)