5555from highdicom .base import SOPClass , _check_little_endian
5656from highdicom .color import ColorManager
5757from highdicom .content import (
58+ _add_icc_profile_attributes ,
59+ _add_palette_color_lookup_table_attributes ,
5860 LUT ,
61+ PaletteColorLUTTransformation ,
5962 PixelMeasuresSequence ,
6063 PlaneOrientationSequence ,
6164 PlanePositionSequence ,
6467from highdicom .enum import (
6568 CoordinateSystemNames ,
6669 DimensionOrganizationTypeValues ,
70+ PhotometricInterpretationValues ,
71+ PixelDataKeywords ,
72+ PixelRepresentationValues ,
73+ PlanarConfigurationValues ,
6774)
6875from highdicom .frame import decode_frame , encode_frame
6976from highdicom .io import ImageFileReader , _wrapped_dcmread
@@ -485,11 +492,11 @@ def __init__(
485492
486493 # Determine what pixel data keyword is present in the image
487494 for kw in [
488- 'PixelData' ,
489- 'FloatPixelData' ,
490- 'DoubleFloatPixelData' ,
495+ PixelDataKeywords . PIXEL_DATA ,
496+ PixelDataKeywords . FLOAT_PIXEL_DATA ,
497+ PixelDataKeywords . DOUBLE_FLOAT_PIXEL_DATA ,
491498 ]:
492- if kw in image :
499+ if kw . value in image :
493500 self .pixel_keyword = kw
494501 break
495502 else :
@@ -504,14 +511,16 @@ def __init__(
504511 ):
505512 if image .BitsAllocated == 32 :
506513 self .input_dtype = np .dtype (np .float32 )
507- self .pixel_keyword = 'FloatPixelData'
514+ self .pixel_keyword = PixelDataKeywords . FLOAT_PIXEL_DATA
508515 elif image .BitsAllocated == 64 :
509516 self .input_dtype = np .dtype (np .float64 )
510- self .pixel_keyword = 'DoubleFloatPixelData'
517+ self .pixel_keyword = (
518+ PixelDataKeywords .DOUBLE_FLOAT_PIXEL_DATA
519+ )
511520 else :
512- self .pixel_keyword = 'PixelData'
521+ self .pixel_keyword = PixelDataKeywords . PIXEL_DATA
513522
514- if self .pixel_keyword == 'PixelData' :
523+ if self .pixel_keyword == PixelDataKeywords . PIXEL_DATA :
515524 if image .PixelRepresentation == 1 :
516525 if image .BitsAllocated == 8 :
517526 self .input_dtype = np .dtype (np .int8 )
@@ -1244,9 +1253,24 @@ def coordinate_system(self) -> CoordinateSystemNames | None:
12441253
12451254 def _init_multiframe_image (
12461255 self ,
1247- source_images : Sequence [Dataset ],
12481256 pixel_array : np .ndarray | Volume ,
12491257 * ,
1258+ source_images : Sequence [Dataset ],
1259+ image_type : Sequence [str ],
1260+ photometric_interpretation : PhotometricInterpretationValues | str ,
1261+ bits_allocated : int ,
1262+ bits_stored : int | None = None ,
1263+ samples_per_pixel : int = 1 ,
1264+ planar_configuration : (
1265+ PlanarConfigurationValues | int
1266+ ) = PlanarConfigurationValues .COLOR_BY_PIXEL ,
1267+ pixel_representation : (
1268+ PixelRepresentationValues | int
1269+ ) = PixelRepresentationValues .UNSIGNED_INTEGER ,
1270+ contains_recognizable_visual_features : bool | None = None ,
1271+ burned_in_annotation : bool | None = None ,
1272+ palette_color_lut_transformation : PaletteColorLUTTransformation | None ,
1273+ icc_profile : bytes | None = None ,
12501274 pixel_measures : PixelMeasuresSequence | None = None ,
12511275 plane_orientation : PlaneOrientationSequence | None = None ,
12521276 plane_positions : Sequence [PlanePositionSequence ] | None = None ,
@@ -1263,7 +1287,7 @@ def _init_multiframe_image(
12631287 pyramid_label : str | None = None ,
12641288 pyramid_uid : str | None = None ,
12651289 use_extended_offset_table : bool = False ,
1266- pixel_data_attr : str = 'PixelData' ,
1290+ pixel_data_keyword : PixelDataKeywords = PixelDataKeywords . PIXEL_DATA ,
12671291 channel_values : Sequence [Any ] | None = None , # TODO generalize
12681292 add_channel_callback : (
12691293 Callable [[Dataset , Any ], Dataset ] | None
@@ -1345,6 +1369,57 @@ def _init_multiframe_image(
13451369 "with the source images."
13461370 )
13471371
1372+ # (Float/DoubleFloat) Image Pixel module
1373+ self .BitsAllocated = bits_allocated
1374+ self .SamplesPerPixel = samples_per_pixel
1375+ if samples_per_pixel > 1 :
1376+ self .PlanarConfiguration = PlanarConfigurationValues (
1377+ planar_configuration
1378+ ).value
1379+ photometric_interpretation = PhotometricInterpretationValues (
1380+ photometric_interpretation
1381+ )
1382+ self .PhotometricInterpretation = photometric_interpretation .value
1383+
1384+ pixel_data_keyword = PixelDataKeywords (pixel_data_keyword )
1385+
1386+ if pixel_data_keyword == PixelDataKeywords .PIXEL_DATA :
1387+ # Attributes in the Image Pixel Module but not the
1388+ # Float/DoubleFloat Image Pixel modules
1389+ if bits_stored is None :
1390+ bits_stored = bits_allocated
1391+ self .BitsStored = bits_stored
1392+ self .HighBit = bits_allocated - 1
1393+ self .PixelRepresentation = PixelRepresentationValues (
1394+ pixel_representation
1395+ ).value
1396+
1397+ # General Image module
1398+ self .ImageType = list (image_type )
1399+ if burned_in_annotation is not None :
1400+ self .BurnedInAnnotation = (
1401+ 'YES' if burned_in_annotation else 'NO'
1402+ )
1403+ if contains_recognizable_visual_features is not None :
1404+ self .RecognizableVisualFeatures = (
1405+ 'YES' if contains_recognizable_visual_features else 'NO'
1406+ )
1407+ self .PresentationLUTShape = (
1408+ 'INVERSE'
1409+ if photometric_interpretation ==
1410+ PhotometricInterpretationValues .MONOCHROME1
1411+ else 'IDENTITY'
1412+ )
1413+
1414+ if palette_color_lut_transformation is not None :
1415+ _add_palette_color_lookup_table_attributes (
1416+ self ,
1417+ palette_color_lut_transformation ,
1418+ )
1419+
1420+ if icc_profile is not None :
1421+ _add_icc_profile_attributes (self , icc_profile )
1422+
13481423 self ._add_source_image_references (
13491424 source_images = source_images ,
13501425 further_source_images = further_source_images ,
@@ -1916,7 +1991,7 @@ def preprocess_channel_callback(
19161991 if len (remainder_pixels ) > 0 :
19171992 frames .append (self ._encode_pixels_native (remainder_pixels ))
19181993
1919- setattr (self , pixel_data_attr , b'' .join (frames ))
1994+ setattr (self , pixel_data_keyword . value , b'' .join (frames ))
19201995
19211996 # Add a null trailing byte if required (can't happen for floating pixel
19221997 # data)
0 commit comments