Skip to content

Commit b9a6a3e

Browse files
CPBridgeshishirpy
andauthored
Doctests (#165)
* Update examples to pass doctests * Update doc requirements, fix doc build warning in ko.sop module * Add doctests to github workflows * Combine doctests with main tests Co-authored-by: Shishir Pandey <[email protected]>
1 parent 47bd633 commit b9a6a3e

File tree

8 files changed

+127
-87
lines changed

8 files changed

+127
-87
lines changed

.github/workflows/run_unit_tests.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,5 @@ jobs:
3333
flake8 --exclude='bin,build,.eggs,src/highdicom/_*'
3434
- name: Test with pytest
3535
run: |
36-
pytest --cov=highdicom --cov-fail-under=80 tests
36+
pytest --cov=highdicom --cov-fail-under=80
3737

requirements_docs.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
sphinx-autodoc-typehints==1.12.0
1+
sphinx-autodoc-typehints==1.17.0
22
sphinx-pyreverse==0.0.17
33
sphinx-rtd-theme==1.0.0
44
sphinxcontrib-autoprogram==0.1.7

setup.cfg

+1
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ ignore_missing_imports = True
1818
[tool:pytest]
1919
python_files = tests/*.py
2020
log_cli_level = INFO
21+
addopts = --doctest-modules

src/highdicom/io.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -224,12 +224,17 @@ class ImageFileReader(object):
224224
225225
Examples
226226
--------
227-
>>> from highdicom.io import ImageFileReader
228-
>>> with ImageFileReader('/path/to/file.dcm') as image:
229-
... print(image.metadata)
227+
>>> from pydicom.data import get_testdata_file
228+
>>> test_filepath = get_testdata_file('eCT_Supplemental.dcm')
229+
>>>
230+
>>> with ImageFileReader(test_filepath) as image:
231+
... print(image.metadata.SOPInstanceUID)
230232
... for i in range(image.number_of_frames):
231233
... frame = image.read_frame(i)
232234
... print(frame.shape)
235+
1.3.6.1.4.1.5962.1.1.10.3.1.1166562673.14401
236+
(512, 512)
237+
(512, 512)
233238
234239
"""
235240

src/highdicom/ko/sop.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ def content(self) -> KeyObjectSelection:
164164
def resolve_reference(self, sop_instance_uid: str) -> Tuple[str, str, str]:
165165
"""Resolve reference for an object included in the document content.
166166
167-
Parameter
168-
---------
167+
Parameters
168+
----------
169169
sop_instance_uid: str
170170
SOP Instance UID of a referenced object
171171

src/highdicom/seg/content.py

+21-3
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,12 @@ def get_index_position(self, pointer: str) -> int:
555555
--------
556556
>>> dimension_index = DimensionIndexSequence("SLIDE")
557557
>>> i = dimension_index.get_index_position("ReferencedSegmentNumber")
558-
>>> segment_numbers = dimension_index[i]
558+
>>> dimension_description = dimension_index[i]
559+
>>> dimension_description
560+
(0020, 9164) Dimension Organization UID ...
561+
(0020, 9165) Dimension Index Pointer AT: (0062, 000b)
562+
(0020, 9167) Functional Group Pointer AT: (0062, 000a)
563+
(0020, 9421) Dimension Description Label LO: 'Segment Number'
559564
560565
"""
561566
indices = [
@@ -642,11 +647,24 @@ def get_index_keywords(self) -> List[str]:
642647
643648
Examples
644649
--------
645-
>>> dimension_index = DimensionIndexSequence("SLIDE")
646-
>>> values = dimension_index.get_index_values(...)
650+
>>> dimension_index = DimensionIndexSequence('SLIDE')
651+
>>> plane_positions = [
652+
... PlanePositionSequence('SLIDE', [10.0, 0.0, 0.0], [1, 1]),
653+
... PlanePositionSequence('SLIDE', [30.0, 0.0, 0.0], [1, 2]),
654+
... PlanePositionSequence('SLIDE', [50.0, 0.0, 0.0], [1, 3])
655+
... ]
656+
>>> values, indices = dimension_index.get_index_values(plane_positions)
647657
>>> names = dimension_index.get_index_keywords()
658+
>>> for name in names:
659+
... print(name)
660+
ColumnPositionInTotalImagePixelMatrix
661+
RowPositionInTotalImagePixelMatrix
662+
XOffsetInSlideCoordinateSystem
663+
YOffsetInSlideCoordinateSystem
664+
ZOffsetInSlideCoordinateSystem
648665
>>> index = names.index("XOffsetInSlideCoordinateSystem")
649666
>>> print(values[:, index])
667+
[10. 30. 50.]
650668
651669
"""
652670
return [

src/highdicom/seg/sop.py

+53-48
Original file line numberDiff line numberDiff line change
@@ -1522,28 +1522,33 @@ def get_segment_numbers(
15221522
generated by an automatic algorithm from a segmentation object ``seg``:
15231523
15241524
>>> from pydicom.sr.codedict import codes
1525-
>>> from highdicom.seg import SegmentAlgorithmTypeValues
1526-
>>>
1525+
>>> from highdicom.seg import SegmentAlgorithmTypeValues, Segmentation
1526+
>>> from pydicom import dcmread
1527+
>>> ds = dcmread('data/test_files/seg_image_sm_control.dcm')
1528+
>>> seg = Segmentation.from_dataset(ds)
15271529
>>> segment_numbers = seg.get_segment_numbers(
1528-
>>> segmented_property_type=codes.SCT.Tumor,
1529-
>>> algorithm_type=SegmentAlgorithmTypeValues.AUTOMATIC
1530-
>>> )
1531-
[1, 2, 3]
1530+
... segmented_property_type=codes.SCT.ConnectiveTissue,
1531+
... algorithm_type=SegmentAlgorithmTypeValues.AUTOMATIC
1532+
... )
1533+
>>> segment_numbers
1534+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
15321535
15331536
Get segment numbers of all segments identified by a given
15341537
institution-specific tracking ID:
15351538
15361539
>>> segment_numbers = seg.get_segment_numbers(
1537-
>>> tracking_id='Tumor #1234567'
1538-
>>> )
1539-
[13]
1540+
... tracking_id='Segment #4'
1541+
... )
1542+
>>> segment_numbers
1543+
[4]
15401544
15411545
Get segment numbers of all segments identified a globally unique
15421546
tracking UID:
15431547
1544-
>>> uid = '1.2.826.0.1.3680043.10.511.3.73025483745501512180439199223117347'
1548+
>>> uid = '1.2.826.0.1.3680043.8.498.42540123542017542395135803252098380233'
15451549
>>> segment_numbers = seg.get_segment_numbers(tracking_uid=uid)
1546-
[5]
1550+
>>> segment_numbers
1551+
[13]
15471552
15481553
""" # noqa: E501
15491554
filter_funcs = []
@@ -1627,13 +1632,12 @@ def get_tracking_ids(
16271632
16281633
List the tracking IDs and UIDs present in the segmentation image:
16291634
1630-
>>> seg.get_tracking_ids()
1631-
[('Spine', '1.2.826.0.1.3680043.10.511.3.10042414969629429693880339016394772'),
1632-
('Bone', '1.2.826.0.1.3680043.10.511.3.83271046815894549094043330632275067')]
1635+
>>> sorted(seg.get_tracking_ids(), reverse=True) # otherwise its a random order
1636+
[('Spine', '1.2.826.0.1.3680043.10.511.3.10042414969629429693880339016394772'), ('Bone', '1.2.826.0.1.3680043.10.511.3.83271046815894549094043330632275067')]
16331637
16341638
>>> for seg_num in seg.segment_numbers:
1635-
>>> desc = seg.get_segment_description(seg_num)
1636-
>>> print(desc.segmented_property_type.meaning)
1639+
... desc = seg.get_segment_description(seg_num)
1640+
... print(desc.segmented_property_type.meaning)
16371641
Bone
16381642
Spine
16391643
@@ -2130,7 +2134,7 @@ def get_pixels_by_source_instance(
21302134
List the source images for this segmentation:
21312135
21322136
>>> for study_uid, series_uid, sop_uid in seg.get_source_image_uids():
2133-
>>> print(sop_uid)
2137+
... print(sop_uid)
21342138
1.3.6.1.4.1.5962.1.1.0.0.0.1196530851.28319.0.93
21352139
1.3.6.1.4.1.5962.1.1.0.0.0.1196530851.28319.0.94
21362140
1.3.6.1.4.1.5962.1.1.0.0.0.1196530851.28319.0.95
@@ -2139,11 +2143,11 @@ def get_pixels_by_source_instance(
21392143
Get the segmentation array for a subset of these images:
21402144
21412145
>>> pixels = seg.get_pixels_by_source_instance(
2142-
>>> source_sop_instance_uids=[
2143-
>>> '1.3.6.1.4.1.5962.1.1.0.0.0.1196530851.28319.0.93',
2144-
>>> '1.3.6.1.4.1.5962.1.1.0.0.0.1196530851.28319.0.94'
2145-
>>> ]
2146-
>>> )
2146+
... source_sop_instance_uids=[
2147+
... '1.3.6.1.4.1.5962.1.1.0.0.0.1196530851.28319.0.93',
2148+
... '1.3.6.1.4.1.5962.1.1.0.0.0.1196530851.28319.0.94'
2149+
... ]
2150+
... )
21472151
>>> pixels.shape
21482152
(2, 16, 16, 1)
21492153
@@ -2359,6 +2363,7 @@ def get_pixels_by_source_frame(
23592363
List the source image SOP instance UID for this segmentation:
23602364
23612365
>>> sop_uid = seg.get_source_image_uids()[0][2]
2366+
>>> sop_uid
23622367
'1.2.826.0.1.3680043.9.7433.3.12857516184849951143044513877282227'
23632368
23642369
Get the segmentation array for 3 of the frames in the multiframe source
@@ -2367,45 +2372,45 @@ def get_pixels_by_source_frame(
23672372
segments present in this segmentation.
23682373
23692374
>>> pixels = seg.get_pixels_by_source_frame(
2370-
>>> source_sop_instance_uid=sop_uid,
2371-
>>> source_frame_numbers=[4, 5, 6]
2372-
>>> )
2375+
... source_sop_instance_uid=sop_uid,
2376+
... source_frame_numbers=[4, 5, 6]
2377+
... )
23732378
>>> pixels.shape
23742379
(3, 10, 10, 20)
23752380
23762381
This time, select only 4 of the 20 segments:
23772382
23782383
>>> pixels = seg.get_pixels_by_source_frame(
2379-
>>> source_sop_instance_uid=sop_uid,
2380-
>>> source_frame_numbers=[4, 5, 6],
2381-
>>> segment_numbers=[10, 11, 12, 13]
2382-
>>> )
2384+
... source_sop_instance_uid=sop_uid,
2385+
... source_frame_numbers=[4, 5, 6],
2386+
... segment_numbers=[10, 11, 12, 13]
2387+
... )
23832388
>>> pixels.shape
23842389
(3, 10, 10, 4)
23852390
23862391
Instead create a multiclass label map for each source frame. Note
23872392
that segments 6, 8, and 10 are present in the three chosen frames.
23882393
23892394
>>> pixels = seg.get_pixels_by_source_frame(
2390-
>>> source_sop_instance_uid=sop_uid,
2391-
>>> source_frame_numbers=[4, 5, 6],
2392-
>>> combine_segments=True
2393-
>>> )
2395+
... source_sop_instance_uid=sop_uid,
2396+
... source_frame_numbers=[4, 5, 6],
2397+
... combine_segments=True
2398+
... )
23942399
>>> pixels.shape, np.unique(pixels)
2395-
(3, 10, 10), array([0, 6, 8, 10])
2400+
((3, 10, 10), array([ 0, 6, 8, 10]))
23962401
23972402
Now relabel the segments to give a pixel map with values between 0
23982403
and 3 (inclusive):
23992404
24002405
>>> pixels = seg.get_pixels_by_source_frame(
2401-
>>> source_sop_instance_uid=sop_uid,
2402-
>>> source_frame_numbers=[4, 5, 6],
2403-
>>> segment_numbers=[6, 8, 10]
2404-
>>> combine_segments=True,
2405-
>>> relabel=True
2406-
>>> )
2406+
... source_sop_instance_uid=sop_uid,
2407+
... source_frame_numbers=[4, 5, 6],
2408+
... segment_numbers=[6, 8, 10],
2409+
... combine_segments=True,
2410+
... relabel=True
2411+
... )
24072412
>>> pixels.shape, np.unique(pixels)
2408-
(3, 10, 10), array([0, 1, 2, 3])
2413+
((3, 10, 10), array([0, 1, 2, 3]))
24092414
24102415
"""
24112416
# Check that indexing in this way is possible
@@ -2619,7 +2624,7 @@ def get_pixels_by_dimension_index_values(
26192624
Get the default list of dimension index values
26202625
26212626
>>> for tag in seg.get_default_dimension_index_pointers():
2622-
>>> print(keyword_for_tag(tag))
2627+
... print(keyword_for_tag(tag))
26232628
ColumnPositionInTotalImagePixelMatrix
26242629
RowPositionInTotalImagePixelMatrix
26252630
XOffsetInSlideCoordinateSystem
@@ -2630,18 +2635,18 @@ def get_pixels_by_dimension_index_values(
26302635
Use a subset of these index pointers to index the image
26312636
26322637
>>> tags = [
2633-
>>> tag_for_keyword('ColumnPositionInTotalImagePixelMatrix'),
2634-
>>> tag_for_keyword('RowPositionInTotalImagePixelMatrix')
2635-
>>> ]
2638+
... tag_for_keyword('ColumnPositionInTotalImagePixelMatrix'),
2639+
... tag_for_keyword('RowPositionInTotalImagePixelMatrix')
2640+
... ]
26362641
>>> assert seg.are_dimension_indices_unique(tags) # True
26372642
26382643
It is therefore possible to index using just this subset of
26392644
dimension indices
26402645
26412646
>>> pixels = seg.get_pixels_by_dimension_index_values(
2642-
>>> dimension_index_pointers=tags,
2643-
>>> dimension_index_values=[[1, 1], [1, 2]]
2644-
>>> )
2647+
... dimension_index_pointers=tags,
2648+
... dimension_index_values=[[1, 1], [1, 2]]
2649+
... )
26452650
>>> pixels.shape
26462651
(2, 10, 10, 20)
26472652

src/highdicom/spatial.py

+40-29
Original file line numberDiff line numberDiff line change
@@ -217,16 +217,21 @@ class PixelToReferenceTransformer(object):
217217
Examples
218218
--------
219219
220-
>>> transformer = hd.spatial.PixelToReferenceTransformer(
221-
>>> image_position=[56.0, 34.2, 1.0],
222-
>>> image_orientation=[1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
223-
>>> pixel_spacing=[0.5, 0.5]
224-
>>> )
220+
>>> import numpy as np
221+
>>>
222+
>>> # Create a transformer by specifying the reference space of
223+
>>> # an image
224+
>>> transformer = PixelToReferenceTransformer(
225+
... image_position=[56.0, 34.2, 1.0],
226+
... image_orientation=[1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
227+
... pixel_spacing=[0.5, 0.5])
228+
>>>
229+
>>> # Use the transformer to convert coordinates
225230
>>> pixel_indices = np.array([[0, 10], [5, 5]])
226-
>>> ref_coords = transformer(image_coords)
231+
>>> ref_coords = transformer(pixel_indices)
227232
>>> print(ref_coords)
228-
>>> # [[56. 39.2 1. ]
229-
>>> # [58.5 36.7 1. ]]
233+
[[56. 39.2 1. ]
234+
[58.5 36.7 1. ]]
230235
231236
Warning
232237
-------
@@ -352,16 +357,17 @@ class ReferenceToPixelTransformer(object):
352357
Examples
353358
--------
354359
355-
>>> transformer = hd.spatial.ReferenceToPixelTransformer(
356-
>>> image_position=[56.0, 34.2, 1.0],
357-
>>> image_orientation=[1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
358-
>>> pixel_spacing=[0.5, 0.5]
359-
>>> )
360+
>>> transformer = ReferenceToPixelTransformer(
361+
... image_position=[56.0, 34.2, 1.0],
362+
... image_orientation=[1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
363+
... pixel_spacing=[0.5, 0.5]
364+
... )
365+
>>>
360366
>>> ref_coords = np.array([[56., 39.2, 1. ], [58.5, 36.7, 1.]])
361367
>>> pixel_indices = transformer(ref_coords)
362368
>>> print(pixel_indices)
363-
>>> # [[ 0 10 0]
364-
>>> # [ 5 5 0]]
369+
[[ 0 10 0]
370+
[ 5 5 0]]
365371
366372
Warning
367373
-------
@@ -493,16 +499,17 @@ class ImageToReferenceTransformer(object):
493499
Examples
494500
--------
495501
496-
>>> transformer = hd.spatial.ImageToReferenceTransformer(
497-
>>> image_position=[56.0, 34.2, 1.0],
498-
>>> image_orientation=[1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
499-
>>> pixel_spacing=[0.5, 0.5]
500-
>>> )
502+
>>> transformer = ImageToReferenceTransformer(
503+
... image_position=[56.0, 34.2, 1.0],
504+
... image_orientation=[1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
505+
... pixel_spacing=[0.5, 0.5]
506+
... )
507+
>>>
501508
>>> image_coords = np.array([[0.0, 10.0], [5.0, 5.0]])
502509
>>> ref_coords = transformer(image_coords)
503510
>>> print(ref_coords)
504-
>>> # [[55.75 38.95 1. ]
505-
>>> # [58.25 36.45 1. ]]
511+
[[55.75 38.95 1. ]
512+
[58.25 36.45 1. ]]
506513
507514
Warning
508515
-------
@@ -630,16 +637,20 @@ class ReferenceToImageTransformer(object):
630637
Examples
631638
--------
632639
633-
>>> transformer = hd.spatial.ReferenceToImageTransformer(
634-
>>> image_position=[56.0, 34.2, 1.0],
635-
>>> image_orientation=[1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
636-
>>> pixel_spacing=[0.5, 0.5]
637-
>>> )
640+
>>> # Create a transformer by specifying the reference space of
641+
>>> # an image
642+
>>> transformer = ReferenceToImageTransformer(
643+
... image_position=[56.0, 34.2, 1.0],
644+
... image_orientation=[1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
645+
... pixel_spacing=[0.5, 0.5]
646+
... )
647+
>>>
648+
>>> # Use the transformer to convert coordinates
638649
>>> ref_coords = np.array([[56., 39.2, 1. ], [58.5, 36.7, 1.]])
639650
>>> image_coords = transformer(ref_coords)
640651
>>> print(image_coords)
641-
>>> # [[0.5 10.5 0. ]
642-
>>> # [5.5 5.5 0. ]]
652+
[[ 0.5 10.5 0. ]
653+
[ 5.5 5.5 0. ]]
643654
644655
Warning
645656
-------

0 commit comments

Comments
 (0)