@@ -125,6 +125,9 @@ class load_data(param.ParameterizedFunction):
125
125
126
126
Currently, we are using a forgiving reader to load the image where a corrupted file
127
127
will not block reading other data.
128
+
129
+ The rotation angles are extracted from the filenames if possible, otherwise from the
130
+ metadata embedded in the tiff files. If both failed, the angle will be set to None.
128
131
"""
129
132
130
133
#
@@ -544,7 +547,7 @@ def _get_filelist_by_dir(
544
547
def _extract_rotation_angles (
545
548
filelist : List [str ],
546
549
metadata_idx : int = 65039 ,
547
- ) -> np .ndarray :
550
+ ) -> Optional [ np .ndarray ] :
548
551
"""
549
552
Extract rotation angles in degrees from filename or metadata.
550
553
@@ -558,40 +561,106 @@ def _extract_rotation_angles(
558
561
Returns
559
562
-------
560
563
rotation_angles
564
+ Array of rotation angles if successfully extracted, None otherwise.
561
565
"""
562
566
# sanity check
563
- if filelist == [] :
567
+ if not filelist :
564
568
logger .error ("filelist is []." )
565
569
raise ValueError ("filelist cannot be empty list." )
566
570
567
- # extract rotation angles from file names
571
+ # process one file at a time
572
+ rotation_angles = []
573
+ for filename in filelist :
574
+ file_ext = Path (filename ).suffix .lower ()
575
+ angle = None
576
+ if file_ext == ".tiff" :
577
+ # first, let's try to extract the angle from the filename
578
+ angle = extract_rotation_angle_from_filename (filename )
579
+ if angle is None :
580
+ # if failed, try to extract from metadata
581
+ angle = extract_rotation_angle_from_tiff_metadata (filename , metadata_idx )
582
+ if angle is None :
583
+ # if failed, log a warning and move on
584
+ logger .warning (f"Failed to extract rotation angle from { filename } ." )
585
+ elif file_ext in (".tif" , ".fits" ):
586
+ # for tif and fits, we can only extract from filename as the metadata is not reliable
587
+ angle = extract_rotation_angle_from_filename (filename )
588
+ if angle is None :
589
+ # if failed, log a warning and move on
590
+ logger .warning (f"Failed to extract rotation angle from { filename } ." )
591
+ else :
592
+ # if the file type is not supported, raise value error
593
+ logger .error (f"Unsupported file type: { file_ext } " )
594
+ raise ValueError ("Unsupported file type." )
595
+
596
+ rotation_angles .append (angle )
597
+
598
+ # this means we have a list of None
599
+ if all (angle is None for angle in rotation_angles ):
600
+ logger .warning ("Failed to extract any rotation angles." )
601
+ return None
602
+
603
+ # warn users if some angles are missing
604
+ if any (angle is None for angle in rotation_angles ):
605
+ logger .warning ("Some rotation angles are missing. You will see nan in the rotation angles array." )
606
+
607
+ return np .array (rotation_angles , dtype = float )
608
+
609
+
610
+ def extract_rotation_angle_from_filename (filename : str ) -> Optional [float ]:
611
+ """
612
+ Extract rotation angle in degrees from filename.
613
+
614
+ Parameters
615
+ ----------
616
+ filename:
617
+ Filename to extract rotation angle from.
618
+
619
+ Returns
620
+ -------
621
+ rotation_angle
622
+ Rotation angle in degrees if successfully extracted, None otherwise.
623
+ """
624
+ # extract rotation angle from file names
568
625
# Note
569
626
# ----
570
627
# For the following file
571
628
# 20191030_ironman_small_0070_300_440_0520.tif(f)
629
+ # 20191030_ironman_small_0070_300_440_0520.fits
572
630
# the rotation angle is 300.44 degrees
573
- # If all given filenames follows the pattern, we will use the angles from
574
- # filenames. Otherwise, we will use the angles from metadata.
575
- regex = r"\d{8}_\S*_\d{4}_(?P<deg>\d{3})_(?P<dec>\d{3})_\d*\.tif{1,2}"
576
- matches = [re .match (regex , Path (f ).name ) for f in filelist ]
577
- if all (matches ):
578
- logger .info ("Using rotation angles from filenames." )
579
- rotation_angles = np .array ([float ("." .join (m .groups ())) for m in matches ])
631
+ regex = r"\d{8}_\S*_\d{4}_(?P<deg>\d{3})_(?P<dec>\d{3})_\d*\.(?:tiff?|fits)"
632
+ match = re .match (regex , Path (filename ).name )
633
+ if match :
634
+ rotation_angle = float ("." .join (match .groups ()))
580
635
else :
581
- # extract rotation angles from metadata
582
- file_exts = set (Path (f ).suffix .lower () for f in filelist )
583
- if not file_exts .issubset ({".tiff" , ".tif" }):
584
- logger .error ("Only .tiff and .tif files are supported." )
585
- raise ValueError ("Rotation angle from metadata is only supported for .tiff and .tif files." )
636
+ rotation_angle = None
637
+ return rotation_angle
638
+
639
+
640
+ def extract_rotation_angle_from_tiff_metadata (filename : str , metadata_idx : int = 65039 ) -> Optional [float ]:
641
+ """
642
+ Extract rotation angle in degrees from metadata of a tiff file.
643
+
644
+ Parameters
645
+ ----------
646
+ filename:
647
+ Filename to extract rotation angle from.
648
+ metadata_idx:
649
+ Index of metadata to extract rotation angle from, default is 65039.
650
+
651
+ Returns
652
+ -------
653
+ rotation_angle
654
+ Rotation angle in degrees if successfully extracted, None otherwise.
655
+ """
656
+ try :
586
657
# -- read metadata
587
658
# img = tifffile.TiffFile("test_with_metadata_0.tiff")
588
659
# img.pages[0].tags[65039].value
589
660
# >> 'RotationActual:0.579840'
590
- rotation_angles = np .array (
591
- [float (tifffile .TiffFile (f ).pages [0 ].tags [metadata_idx ].value .split (":" )[- 1 ]) for f in filelist ],
592
- dtype = "float" ,
593
- )
594
- return rotation_angles
661
+ return float (tifffile .TiffFile (filename ).pages [0 ].tags [metadata_idx ].value .split (":" )[- 1 ])
662
+ except Exception :
663
+ return None
595
664
596
665
597
666
def _save_data (filename : Path , data : np .ndarray , rot_angles : np .ndarray = None ) -> None :
0 commit comments