Skip to content

Commit 12573fa

Browse files
committed
resolve merge conflicts
2 parents dcbd21c + 2624b63 commit 12573fa

7 files changed

Lines changed: 243 additions & 68 deletions

File tree

11.1 KB
Binary file not shown.
11 KB
Binary file not shown.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ readme = "README.md"
1010
requires-python = ">=3.10"
1111
authors = [
1212
{ name = "Markus D. Herrmann" },
13+
{ name = "Christopher P. Bridge" },
1314
]
1415
maintainers = [
1516
{ name = "Markus D. Herrmann" },

src/highdicom/sr/templates.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4076,12 +4076,6 @@ def _generate_cross_sectional_descriptors(
40764076
relationship_type=RelationshipTypeValues.HAS_ACQ_CONTEXT,
40774077
unit=codes.UCUM.Millimeter
40784078
),
4079-
NumContentItem(
4080-
name=codes.DCM.SpacingBetweenSlices,
4081-
value=dataset.SpacingBetweenSlices,
4082-
relationship_type=RelationshipTypeValues.HAS_ACQ_CONTEXT,
4083-
unit=codes.UCUM.Millimeter
4084-
),
40854079
NumContentItem(
40864080
name=codes.DCM.SliceThickness,
40874081
value=dataset.SliceThickness,
@@ -4167,6 +4161,16 @@ def _generate_cross_sectional_descriptors(
41674161
)
41684162
)
41694163
]
4164+
if hasattr(dataset, 'SpacingBetweenSlices'):
4165+
descriptors.append(
4166+
NumContentItem(
4167+
name=codes.DCM.SpacingBetweenSlices,
4168+
value=dataset.SpacingBetweenSlices,
4169+
relationship_type=RelationshipTypeValues.HAS_ACQ_CONTEXT,
4170+
unit=codes.UCUM.Millimeter
4171+
),
4172+
)
4173+
41704174
return descriptors
41714175

41724176
def _is_cross_sectional(self, ds: Dataset) -> bool:

src/highdicom/sr/utils.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,17 @@ def search_tree(
9090
matched_content_items = []
9191
for content_item in node.ContentSequence:
9292
name_code = content_item.ConceptNameCodeSequence[0]
93+
if hasattr(name_code, "CodeValue"):
94+
code_value = name_code.CodeValue
95+
elif hasattr(name_code, "LongCodeValue"):
96+
code_value = name_code.LongCodeValue
97+
else:
98+
code_value = name_code.URNCodeValue
99+
93100
item = ContentItem(
94101
value_type=content_item.ValueType,
95102
name=CodedConcept(
96-
value=name_code.CodeValue,
103+
value=code_value,
97104
scheme_designator=name_code.CodingSchemeDesignator,
98105
meaning=name_code.CodeMeaning
99106
),

tests/test_io.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,26 @@ def test_read_multi_frame_sm_image_native(self):
7676
)
7777
np.testing.assert_array_equal(frame, pixel_array[i, ...])
7878

79+
def test_read_multi_frame_sm_image_jpegls(self):
80+
filename = str(self._test_dir.joinpath('sm_image_jpegls.dcm'))
81+
dataset = dcmread(filename)
82+
pixel_array = dataset.pixel_array
83+
with ImageFileReader(filename) as reader:
84+
assert reader.number_of_frames == 25
85+
indices = list(range(reader.number_of_frames))
86+
shuffle(indices)
87+
for i in indices:
88+
frame = reader.read_frame(i, correct_color=False)
89+
assert isinstance(frame, np.ndarray)
90+
assert frame.ndim == 3
91+
assert frame.dtype == np.uint8
92+
assert frame.shape == (
93+
reader.metadata.Rows,
94+
reader.metadata.Columns,
95+
reader.metadata.SamplesPerPixel,
96+
)
97+
np.testing.assert_array_equal(frame, pixel_array[i, ...])
98+
7999
def test_read_multi_frame_sm_image_numbers_native(self):
80100
filename = str(self._test_dir.joinpath('sm_image_numbers.dcm'))
81101
dataset = dcmread(filename)
@@ -216,6 +236,90 @@ def test_read_single_frame_ct_image_dicom_file_like_opened(self):
216236
)
217237
np.testing.assert_array_equal(frame, pixel_array)
218238

239+
def test_read_single_frame_sm_image_jpegls_dicom_bytes_io(self):
240+
filename = str(self._test_dir.joinpath("sm_image_jpegls.dcm"))
241+
dcm = DicomBytesIO(open(filename, "rb").read())
242+
243+
dataset = dcmread(filename)
244+
pixel_array = dataset.pixel_array
245+
with ImageFileReader(dcm) as reader:
246+
assert reader.number_of_frames == 25
247+
for fno in range(reader.number_of_frames):
248+
frame = reader.read_frame(fno)
249+
assert isinstance(frame, np.ndarray)
250+
assert frame.ndim == 3
251+
assert frame.dtype == np.uint8
252+
assert frame.shape == (
253+
reader.metadata.Rows,
254+
reader.metadata.Columns,
255+
3,
256+
)
257+
np.testing.assert_array_equal(frame, pixel_array[fno])
258+
259+
def test_read_single_frame_sm_image_jpegls_nobot_dicom_bytes_io(self):
260+
filename = str(self._test_dir.joinpath("sm_image_jpegls_nobot.dcm"))
261+
dcm = DicomBytesIO(open(filename, "rb").read())
262+
263+
dataset = dcmread(filename)
264+
pixel_array = dataset.pixel_array
265+
with ImageFileReader(dcm) as reader:
266+
assert reader.number_of_frames == 25
267+
for fno in range(reader.number_of_frames):
268+
frame = reader.read_frame(fno)
269+
assert isinstance(frame, np.ndarray)
270+
assert frame.ndim == 3
271+
assert frame.dtype == np.uint8
272+
assert frame.shape == (
273+
reader.metadata.Rows,
274+
reader.metadata.Columns,
275+
3,
276+
)
277+
np.testing.assert_array_equal(frame, pixel_array[fno])
278+
279+
def test_read_single_frame_sm_image_jpegls_dicom_file_like_opened(self):
280+
# Test reading frames from an opened DicomFileLike file
281+
filename = self._test_dir.joinpath("sm_image_jpegls.dcm")
282+
dcm = DicomFileLike(filename.open("rb"))
283+
284+
dataset = dcmread(filename)
285+
pixel_array = dataset.pixel_array
286+
with ImageFileReader(dcm) as reader:
287+
assert reader.number_of_frames == 25
288+
for fno in range(reader.number_of_frames):
289+
frame = reader.read_frame(fno)
290+
assert isinstance(frame, np.ndarray)
291+
assert frame.ndim == 3
292+
assert frame.dtype == np.uint8
293+
assert frame.shape == (
294+
reader.metadata.Rows,
295+
reader.metadata.Columns,
296+
3,
297+
)
298+
np.testing.assert_array_equal(frame, pixel_array[fno])
299+
300+
def test_read_single_frame_sm_image_jpegls_nobot_dicom_file_like_opened(
301+
self
302+
):
303+
# Test reading frames from an opened DicomFileLike file
304+
filename = self._test_dir.joinpath("sm_image_jpegls_nobot.dcm")
305+
dcm = DicomFileLike(filename.open("rb"))
306+
307+
dataset = dcmread(filename)
308+
pixel_array = dataset.pixel_array
309+
with ImageFileReader(dcm) as reader:
310+
assert reader.number_of_frames == 25
311+
for fno in range(reader.number_of_frames):
312+
frame = reader.read_frame(fno)
313+
assert isinstance(frame, np.ndarray)
314+
assert frame.ndim == 3
315+
assert frame.dtype == np.uint8
316+
assert frame.shape == (
317+
reader.metadata.Rows,
318+
reader.metadata.Columns,
319+
3,
320+
)
321+
np.testing.assert_array_equal(frame, pixel_array[fno])
322+
219323
def test_read_rle_no_bot(self):
220324
# This image is RLE compressed but has no BOT, requiring searching
221325
# through the pixel data for delimiter tags

tests/test_sr.py

Lines changed: 120 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -4560,6 +4560,38 @@ def test_find_content_items(self):
45604560
items = find_content_items(self._sr_document)
45614561
assert len(items) == 8
45624562

4563+
def test_find_content_items_with_URN_code_value(self):
4564+
file_path = Path(__file__)
4565+
data_dir = file_path.parent.parent.joinpath('data')
4566+
my_sr_document = dcmread(
4567+
str(data_dir.joinpath('test_files', 'sr_document.dcm'))
4568+
)
4569+
content_sequence = my_sr_document.ContentSequence[0]
4570+
concept_name_code_sequence = content_sequence.ConceptNameCodeSequence[0]
4571+
# remove existing code value
4572+
del concept_name_code_sequence.CodeValue
4573+
# add URN Code Value
4574+
URN_code_value = "http://example.com/my_urn_value"
4575+
concept_name_code_sequence.URNCodeValue = URN_code_value
4576+
items = find_content_items(my_sr_document)
4577+
assert len(items) == 8
4578+
4579+
def test_find_content_items_with_long_code_value(self):
4580+
file_path = Path(__file__)
4581+
data_dir = file_path.parent.parent.joinpath('data')
4582+
my_sr_document = dcmread(
4583+
str(data_dir.joinpath('test_files', 'sr_document.dcm'))
4584+
)
4585+
content_sequence = my_sr_document.ContentSequence[0]
4586+
concept_name_code_sequence = content_sequence.ConceptNameCodeSequence[0]
4587+
# remove existing code value
4588+
del concept_name_code_sequence.CodeValue
4589+
# add Long Code Value
4590+
long_code_value = "Test_A_Long_Code_Value"
4591+
concept_name_code_sequence.LongCodeValue = long_code_value
4592+
items = find_content_items(my_sr_document)
4593+
assert len(items) == 8
4594+
45634595
def test_find_content_items_filtered_by_name(self):
45644596
items = find_content_items(
45654597
self._sr_document,
@@ -5471,79 +5503,101 @@ def test_ct_construction(self):
54715503
image=self._ref_ct_dataset,
54725504
)
54735505
assert len(group) == 17
5474-
assert isinstance(group[0], CodeContentItem)
5475-
assert group[0].name == codes.DCM.Modality
5476-
assert group[0].value == codes.cid29.ComputedTomography
5477-
assert isinstance(group[1], UIDRefContentItem)
5478-
assert group[1].name == codes.DCM.FrameOfReferenceUID
5479-
assert group[1].value == self._ref_ct_dataset.FrameOfReferenceUID
5480-
assert isinstance(group[2], NumContentItem)
5481-
assert group[2].name == codes.DCM.PixelDataRows
5482-
assert group[2].value == self._ref_ct_dataset.Rows
5483-
assert isinstance(group[3], NumContentItem)
5484-
assert group[3].name == codes.DCM.PixelDataColumns
5485-
assert group[3].value == self._ref_ct_dataset.Columns
5486-
assert isinstance(group[4], NumContentItem)
5487-
assert group[4].name == codes.DCM.HorizontalPixelSpacing
5488-
assert group[4].value == self._ref_ct_dataset.PixelSpacing[0]
5489-
value_item = group[4].MeasuredValueSequence[0]
5506+
5507+
# Check Modality
5508+
modality = group.find(name=codes.DCM.Modality)[0]
5509+
assert isinstance(modality, CodeContentItem)
5510+
assert modality.value == codes.cid29.ComputedTomography
5511+
5512+
# Check Frame of Reference UID
5513+
frame_ref = group.find(name=codes.DCM.FrameOfReferenceUID)[0]
5514+
assert isinstance(frame_ref, UIDRefContentItem)
5515+
assert frame_ref.value == self._ref_ct_dataset.FrameOfReferenceUID
5516+
5517+
# Check Pixel Data Rows
5518+
rows = group.find(name=codes.DCM.PixelDataRows)[0]
5519+
assert isinstance(rows, NumContentItem)
5520+
assert rows.value == self._ref_ct_dataset.Rows
5521+
5522+
# Check Pixel Data Columns
5523+
columns = group.find(name=codes.DCM.PixelDataColumns)[0]
5524+
assert isinstance(columns, NumContentItem)
5525+
assert columns.value == self._ref_ct_dataset.Columns
5526+
5527+
# Check Horizontal Pixel Spacing
5528+
h_spacing = group.find(name=codes.DCM.HorizontalPixelSpacing)[0]
5529+
assert isinstance(h_spacing, NumContentItem)
5530+
assert h_spacing.value == self._ref_ct_dataset.PixelSpacing[0]
5531+
value_item = h_spacing.MeasuredValueSequence[0]
54905532
unit_code_item = value_item.MeasurementUnitsCodeSequence[0]
54915533
assert unit_code_item.CodeValue == 'mm'
54925534
assert unit_code_item.CodeMeaning == 'mm'
54935535
assert unit_code_item.CodingSchemeDesignator == 'UCUM'
5494-
assert isinstance(group[5], NumContentItem)
5495-
assert group[5].name == codes.DCM.VerticalPixelSpacing
5496-
assert group[5].value == self._ref_ct_dataset.PixelSpacing[1]
5497-
assert isinstance(group[6], NumContentItem)
5498-
assert group[6].name == codes.DCM.SpacingBetweenSlices
5499-
assert group[6].value == self._ref_ct_dataset.SpacingBetweenSlices
5500-
value_item = group[6].MeasuredValueSequence[0]
5536+
5537+
# Check Vertical Pixel Spacing
5538+
v_spacing = group.find(name=codes.DCM.VerticalPixelSpacing)[0]
5539+
assert isinstance(v_spacing, NumContentItem)
5540+
assert v_spacing.value == self._ref_ct_dataset.PixelSpacing[1]
5541+
5542+
# Check Spacing Between Slices
5543+
slice_spacing = group.find(name=codes.DCM.SpacingBetweenSlices)[0]
5544+
assert isinstance(slice_spacing, NumContentItem)
5545+
assert slice_spacing.value == self._ref_ct_dataset.SpacingBetweenSlices
5546+
value_item = slice_spacing.MeasuredValueSequence[0]
55015547
unit_code_item = value_item.MeasurementUnitsCodeSequence[0]
55025548
assert unit_code_item.CodeValue == 'mm'
55035549
assert unit_code_item.CodeMeaning == 'mm'
55045550
assert unit_code_item.CodingSchemeDesignator == 'UCUM'
5505-
assert isinstance(group[7], NumContentItem)
5506-
assert group[7].name == codes.DCM.SliceThickness
5507-
assert group[7].value == self._ref_ct_dataset.SliceThickness
5508-
assert isinstance(group[8], NumContentItem)
5509-
assert group[8].name == codes.DCM.ImagePositionPatientX
5510-
assert group[8].value == self._ref_ct_dataset.ImagePositionPatient[0]
5511-
assert isinstance(group[9], NumContentItem)
5512-
assert group[9].name == codes.DCM.ImagePositionPatientY
5513-
assert group[9].value == self._ref_ct_dataset.ImagePositionPatient[1]
5514-
assert isinstance(group[10], NumContentItem)
5515-
assert group[10].name == codes.DCM.ImagePositionPatientZ
5516-
assert group[10].value == self._ref_ct_dataset.ImagePositionPatient[2]
5517-
assert isinstance(group[11], NumContentItem)
5518-
assert group[11].name == codes.DCM.ImageOrientationPatientRowX
5519-
assert group[11].value == \
5520-
self._ref_ct_dataset.ImageOrientationPatient[0]
5521-
value_item = group[11].MeasuredValueSequence[0]
5551+
5552+
# Check Slice Thickness
5553+
thickness = group.find(name=codes.DCM.SliceThickness)[0]
5554+
assert isinstance(thickness, NumContentItem)
5555+
assert thickness.value == self._ref_ct_dataset.SliceThickness
5556+
5557+
# Check Image Position Patient
5558+
pos_x = group.find(name=codes.DCM.ImagePositionPatientX)[0]
5559+
assert isinstance(pos_x, NumContentItem)
5560+
assert pos_x.value == self._ref_ct_dataset.ImagePositionPatient[0]
5561+
5562+
pos_y = group.find(name=codes.DCM.ImagePositionPatientY)[0]
5563+
assert isinstance(pos_y, NumContentItem)
5564+
assert pos_y.value == self._ref_ct_dataset.ImagePositionPatient[1]
5565+
5566+
pos_z = group.find(name=codes.DCM.ImagePositionPatientZ)[0]
5567+
assert isinstance(pos_z, NumContentItem)
5568+
assert pos_z.value == self._ref_ct_dataset.ImagePositionPatient[2]
5569+
5570+
# Check Image Orientation Patient Row
5571+
row_x = group.find(name=codes.DCM.ImageOrientationPatientRowX)[0]
5572+
assert isinstance(row_x, NumContentItem)
5573+
assert row_x.value == self._ref_ct_dataset.ImageOrientationPatient[0]
5574+
value_item = row_x.MeasuredValueSequence[0]
55225575
unit_code_item = value_item.MeasurementUnitsCodeSequence[0]
55235576
assert unit_code_item.CodeValue == '{-1:1}'
55245577
assert unit_code_item.CodeMeaning == '{-1:1}'
55255578
assert unit_code_item.CodingSchemeDesignator == 'UCUM'
5526-
assert isinstance(group[12], NumContentItem)
5527-
assert group[12].name == codes.DCM.ImageOrientationPatientRowY
5528-
assert group[12].value == \
5529-
self._ref_ct_dataset.ImageOrientationPatient[1]
5530-
assert isinstance(group[13], NumContentItem)
5531-
assert group[13].name == codes.DCM.ImageOrientationPatientRowZ
5532-
assert group[13].value == \
5533-
self._ref_ct_dataset.ImageOrientationPatient[2]
5534-
assert isinstance(group[14], NumContentItem)
5535-
assert group[14].name == codes.DCM.ImageOrientationPatientColumnX
5536-
assert group[14].value == \
5537-
self._ref_ct_dataset.ImageOrientationPatient[3]
5538-
assert isinstance(group[15], NumContentItem)
5539-
assert group[15].name == codes.DCM.ImageOrientationPatientColumnY
5540-
assert group[15].value == \
5541-
self._ref_ct_dataset.ImageOrientationPatient[4]
5542-
assert isinstance(group[16], NumContentItem)
5543-
assert group[16].name == codes.DCM.ImageOrientationPatientColumnZ
5544-
assert group[16].value == \
5545-
self._ref_ct_dataset.ImageOrientationPatient[5]
5546-
value_item = group[16].MeasuredValueSequence[0]
5579+
5580+
row_y = group.find(name=codes.DCM.ImageOrientationPatientRowY)[0]
5581+
assert isinstance(row_y, NumContentItem)
5582+
assert row_y.value == self._ref_ct_dataset.ImageOrientationPatient[1]
5583+
5584+
row_z = group.find(name=codes.DCM.ImageOrientationPatientRowZ)[0]
5585+
assert isinstance(row_z, NumContentItem)
5586+
assert row_z.value == self._ref_ct_dataset.ImageOrientationPatient[2]
5587+
5588+
# Check Image Orientation Patient Column
5589+
col_x = group.find(name=codes.DCM.ImageOrientationPatientColumnX)[0]
5590+
assert isinstance(col_x, NumContentItem)
5591+
assert col_x.value == self._ref_ct_dataset.ImageOrientationPatient[3]
5592+
5593+
col_y = group.find(name=codes.DCM.ImageOrientationPatientColumnY)[0]
5594+
assert isinstance(col_y, NumContentItem)
5595+
assert col_y.value == self._ref_ct_dataset.ImageOrientationPatient[4]
5596+
5597+
col_z = group.find(name=codes.DCM.ImageOrientationPatientColumnZ)[0]
5598+
assert isinstance(col_z, NumContentItem)
5599+
assert col_z.value == self._ref_ct_dataset.ImageOrientationPatient[5]
5600+
value_item = col_z.MeasuredValueSequence[0]
55475601
unit_code_item = value_item.MeasurementUnitsCodeSequence[0]
55485602
assert unit_code_item.CodeValue == '{-1:1}'
55495603
assert unit_code_item.CodeMeaning == '{-1:1}'
@@ -5643,6 +5697,11 @@ def test_dx_construction(self):
56435697
assert group[8].name == codes.DCM.ContentTime
56445698
assert group[8].value == content_time
56455699

5700+
def test_optional_spacing_between_slices(self):
5701+
del self._ref_ct_dataset.SpacingBetweenSlices
5702+
group = ImageLibraryEntryDescriptors(image=self._ref_ct_dataset)
5703+
assert not group.find(name=codes.DCM.SpacingBetweenSlices)
5704+
56465705

56475706
class TestImageLibrary(unittest.TestCase):
56485707

0 commit comments

Comments
 (0)