2727
2828try :
2929 import highdicom as hd
30+
3031 HIGHDICOM_AVAILABLE = True
3132except ImportError :
3233 HIGHDICOM_AVAILABLE = False
@@ -92,29 +93,29 @@ def binary_to_image(reference_image, label, dtype=np.uint8, file_ext=".nii.gz"):
9293
9394def _extract_label_info (label , label_info ):
9495 """Extract unique labels and model info from label file.
95-
96+
9697 Args:
9798 label: Path to NIfTI label file
9899 label_info: List of dictionaries containing segment information
99-
100+
100101 Returns:
101102 tuple: (unique_labels, model_name) or (None, None) if empty
102103 """
103104 # Load label file using SimpleITK (consistent with conversion pipeline)
104105 mask = SimpleITK .ReadImage (label )
105106 label_array = SimpleITK .GetArrayFromImage (mask )
106-
107+
107108 # Extract unique non-zero labels
108109 unique_labels = np .unique (label_array ).astype (np .int_ )
109110 unique_labels = unique_labels [unique_labels != 0 ]
110-
111+
111112 info = label_info [0 ] if label_info and 0 < len (label_info ) else {}
112113 model_name = info .get ("model_name" , "AIName" )
113-
114+
114115 if not unique_labels .size :
115116 logger .warning ("No non-zero labels found in segmentation" )
116117 return None , None
117-
118+
118119 return unique_labels , model_name
119120
120121
@@ -137,15 +138,15 @@ def _highdicom_nifti_to_dicom_seg(
137138 # Input validation
138139 if label_info is None :
139140 label_info = []
140-
141+
141142 if not os .path .exists (label ):
142143 logger .error (f"Label file not found: { label } " )
143144 return ""
144-
145+
145146 if not os .path .exists (series_dir ):
146147 logger .error (f"Series directory not found: { series_dir } " )
147148 return ""
148-
149+
149150 # Extract label information
150151 unique_labels , model_name = _extract_label_info (label , label_info )
151152 if unique_labels is None :
@@ -229,7 +230,7 @@ def _highdicom_nifti_to_dicom_seg(
229230
230231 # Get consistent timestamp for all DICOM attributes
231232 dt_now = datetime .datetime .now ()
232-
233+
233234 # DICOM series number is 4 digits max (0-9999)
234235 MAX_SERIES_NUMBER = 9999
235236 series_number = int (dt_now .strftime ("%H%M%S" )) % (MAX_SERIES_NUMBER + 1 )
@@ -319,15 +320,15 @@ def _itk_nifti_to_dicom_seg(series_dir, label, label_info) -> str:
319320 # Input validation
320321 if label_info is None :
321322 label_info = []
322-
323+
323324 if not os .path .exists (label ):
324325 logger .error (f"Label file not found: { label } " )
325326 return ""
326-
327+
327328 if not os .path .exists (series_dir ):
328329 logger .error (f"Series directory not found: { series_dir } " )
329330 return ""
330-
331+
331332 # Extract label information (reuse helper function)
332333 unique_labels , model_name = _extract_label_info (label , label_info )
333334 if unique_labels is None :
@@ -344,7 +345,7 @@ def _itk_nifti_to_dicom_seg(series_dir, label, label_info) -> str:
344345
345346 # Check if custom segmentAttribute is provided
346347 segment_attr = info .get ("segmentAttribute" )
347-
348+
348349 if segment_attr :
349350 # Use custom attribute as-is
350351 segment_descriptions .append (segment_attr )
@@ -379,12 +380,12 @@ def _itk_nifti_to_dicom_seg(series_dir, label, label_info) -> str:
379380
380381 # Extract metadata from label_info (use first segment's metadata for study-level info)
381382 first_info = label_info [0 ] if label_info and len (label_info ) > 0 else {}
382-
383+
383384 # Get timestamp-based series number (consistent with highdicom implementation)
384385 dt_now = datetime .datetime .now ()
385386 MAX_SERIES_NUMBER = 9999
386387 series_number = int (dt_now .strftime ("%H%M%S" )) % (MAX_SERIES_NUMBER + 1 )
387-
388+
388389 # Build ITK template with extracted or sensible default values
389390 template = {
390391 "ContentCreatorName" : first_info .get ("creator" , "MONAI Label" ),
@@ -452,7 +453,7 @@ def nifti_to_dicom_seg(
452453
453454def _dcmqi_nifti_to_dicom_seg (label , series_dir , template ) -> str :
454455 """Convert NIfTI to DICOM SEG using dcmqi's itkimage2segimage command-line tool.
455-
456+
456457 This is a low-level wrapper around the dcmqi itkimage2segimage tool.
457458 Called by _itk_nifti_to_dicom_seg() as the actual conversion implementation.
458459 """
@@ -524,21 +525,21 @@ def dicom_seg_to_itk_image(label, output_ext=".seg.nrrd"):
524525
525526 # Convert to SimpleITK image
526527 image = SimpleITK .GetImageFromArray (volume .array )
527-
528+
528529 # Convert spacing from highdicom to SimpleITK order
529530 # highdicom: (slice, row, column) for axes (0, 1, 2)
530531 # SimpleITK: (x, y, z) = (column, row, slice)
531532 # Therefore: reverse the spacing tuple
532533 sitk_spacing = tuple (reversed (volume .spacing ))
533534 image .SetSpacing (sitk_spacing )
534-
535+
535536 # Set origin and direction if available
536- if hasattr (volume , ' position' ) and volume .position is not None :
537+ if hasattr (volume , " position" ) and volume .position is not None :
537538 # Origin (position) is in LPS physical coordinates for voxel (0,0,0)
538539 # Both highdicom and SimpleITK use the same coordinate system, so no conversion needed
539540 image .SetOrigin (volume .position )
540-
541- if hasattr (volume , ' direction' ) and volume .direction is not None :
541+
542+ if hasattr (volume , " direction" ) and volume .direction is not None :
542543 # Direction matrix columns need reordering
543544 # highdicom: columns are [slice_dir, row_dir, col_dir] = [z, y, x]
544545 # SimpleITK: columns must be [x_dir, y_dir, z_dir]
0 commit comments