Skip to content

Commit 9759b63

Browse files
committed
ENH: Add series_uid arg to imread() for dicom series selection
If filename is a directory of DICOM files, and series_uid is a string, it will read the DICOM series that matches this series_uid name. If series_uid is an integer, it will read the series_uid'th DICOM series in the directory (using python notation, e.g., -1 is the last DICOM series in the directory). If unspecified, it will read the first series in the directory. This does represent a change in the default behavior in that it used to raise an exception if a DICOM directory with more than one series was passed to imread, and now it will read the first series in that directory.
1 parent 92c17bc commit 9759b63

File tree

3 files changed

+41
-11
lines changed

3 files changed

+41
-11
lines changed

Wrapping/Generators/Python/Tests/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ if(ITK_WRAP_unsigned_char AND WRAP_2)
6363
${ITK_TEST_OUTPUT_DIR}/extras-mushroom.vtk
6464
${ITK_TEST_OUTPUT_DIR}
6565
${ITK_TEST_OUTPUT_DIR}/LinearTransformWritten.h5
66+
${ExternalData_BINARY_ROOT}/Testing/Data/Input/DicomSeries
67+
)
68+
set_tests_properties( PythonExtrasTest PROPERTIES DEPENDS
69+
PythonDICOMSeries
6670
)
6771
itk_python_add_test(NAME PythonXarrayTest
6872
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_xarray.py

Wrapping/Generators/Python/Tests/extras.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,15 @@ def custom_callback(name, progress):
163163
image = itk.imread(filename, imageio=itk.PNGImageIO.New())
164164
assert type(image) == itk.Image[itk.RGBPixel[itk.UC], 2]
165165

166+
# imread using a dicom series
167+
image = itk.imread(sys.argv[8])
168+
image0 = itk.imread(sys.argv[8], series_uid=0)
169+
imageS = itk.imread(sys.argv[8], series_uid="1.2.840.113619.2.133.1762890640.1886.1055165015.999.31.625625620030625")
170+
assert itk.size(image) == itk.size(image0)
171+
assert itk.size(image) == itk.size(imageS)
172+
assert image[1,10,10] == image0[1,10,10]
173+
assert image[1,10,10] == imageS[1,10,10]
174+
166175
# Test serialization with pickle
167176
array = np.random.randint(0, 256, (8, 12)).astype(np.uint8)
168177
image = itk.image_from_array(array)

Wrapping/Generators/Python/itk/support/extras.py

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,6 +1182,7 @@ def imread(
11821182
pixel_type: Optional["itkt.PixelTypes"] = None,
11831183
fallback_only: bool = False,
11841184
imageio: Optional["itkt.ImageIOBase"] = None,
1185+
series_uid: Optional[Union[int, str]] = None,
11851186
) -> "itkt.ImageBase":
11861187
"""Read an image from a file or series of files and return an itk.Image.
11871188
@@ -1202,6 +1203,9 @@ def imread(
12021203
imageio :
12031204
Use the provided itk.ImageIOBase derived instance to read the file.
12041205
1206+
series_uid :
1207+
If filename is a directory of DICOM files, and series_uid is a string, it will read the DICOM series that matches this series_uid name. If series_uid is an integer, it will read the series_uid'th DICOM series in the directory (using python notation, e.g., -1 is the last DICOM series in the directory). If unspecified, it will read the first series in the directory.
1208+
12051209
Returns
12061210
-------
12071211
@@ -1212,10 +1216,11 @@ def imread(
12121216
`pixel_type` is not provided (default). The dimension of the image is
12131217
automatically deduced from the dimension stored on disk.
12141218
1215-
If the filename provided is a directory then the directory is assumed to
1216-
be for a DICOM series volume. If there is exactly one DICOM series
1217-
volume in that directory, the reader will use an itk.ImageSeriesReader
1218-
object to read the the DICOM filenames within that directory.
1219+
If the filename provided is a directory then the directory is assumed
1220+
to be for a DICOM series volume. If the argument series_uid is
1221+
specified, it will read that series from that directory. If the
1222+
specified series_uid doesn't exist, an error is thrown. If no
1223+
series_uid is given, the first series in that directory is read.
12191224
12201225
If the given filename is a list or a tuple of file names, the reader
12211226
will use an itk.ImageSeriesReader object to read the files.
@@ -1247,14 +1252,26 @@ def imread(
12471252
names_generator.SetUseSeriesDetails(True)
12481253
names_generator.AddSeriesRestriction("0008|0021") # Series Date
12491254
names_generator.SetDirectory(f"{filename}")
1250-
series_uid = names_generator.GetSeriesUIDs()
1251-
if len(series_uid) == 0:
1255+
series_uid_list = names_generator.GetSeriesUIDs()
1256+
if len(series_uid_list) == 0:
12521257
raise FileNotFoundError(f"no DICOMs in: {filename}.")
1253-
if len(series_uid) > 1:
1254-
raise OSError(
1255-
f"the directory: {filename} contains more than one DICOM series."
1256-
)
1257-
series_identifier = series_uid[0]
1258+
if series_uid != None:
1259+
if isinstance(series_uid, int):
1260+
try:
1261+
series_identifier = series_uid_list[series_uid]
1262+
except IndexError:
1263+
raise OSError(
1264+
f"cannot access DICOM series {series_uid} in the directory {filename}."
1265+
)
1266+
else:
1267+
if series_uid in series_uid_list:
1268+
series_identifier = series_uid
1269+
else:
1270+
raise OSError(
1271+
f"the directory {filename} does not contain the DICOM series {series_uid}."
1272+
)
1273+
else:
1274+
series_identifier = series_uid_list[0]
12581275
filename = names_generator.GetFileNames(series_identifier)
12591276
if type(filename) in [list, tuple]:
12601277
template_reader_type = itk.ImageSeriesReader

0 commit comments

Comments
 (0)