77
88from .vendor .dm3 import DM3
99from .vendor .dm4 import DM4File , DM4TagHeader
10+ from tifffile import TiffFile
1011
1112# Disable PIL's maximum image limit.
1213Image .MAX_IMAGE_PIXELS = None
1314
1415
15- class PillowImageReader :
16- def read_array (self , file_name : str , dtype : np .dtype ) -> np .ndarray :
16+ class ImageReader :
17+ def read_array (self , file_name : str , dtype : np .dtype , z_slice : int ) -> np .ndarray :
18+ pass
19+
20+ def read_dimensions (self , file_name : str ) -> Tuple [int , int ]:
21+ pass
22+
23+ def read_channel_count (self , file_name : str ) -> int :
24+ pass
25+
26+ def read_z_slices_per_file (
27+ self , file_name : str # pylint: disable=unused-argument
28+ ) -> int :
29+ return 1
30+
31+
32+ class PillowImageReader (ImageReader ):
33+ def read_array (self , file_name : str , dtype : np .dtype , z_slice : int ) -> np .ndarray :
1734 this_layer = np .array (Image .open (file_name ), dtype )
1835 this_layer = this_layer .swapaxes (0 , 1 )
1936 this_layer = this_layer .reshape (this_layer .shape + (1 ,))
@@ -38,8 +55,8 @@ def to_target_datatype(data: np.ndarray, target_dtype: np.dtype) -> np.ndarray:
3855 return (data / factor ).astype (target_dtype )
3956
4057
41- class Dm3ImageReader :
42- def read_array (self , file_name : str , dtype : np .dtype ) -> np .ndarray :
58+ class Dm3ImageReader ( ImageReader ) :
59+ def read_array (self , file_name : str , dtype : np .dtype , z_slice : int ) -> np .ndarray :
4360 dm3_file = DM3 (file_name )
4461 this_layer = to_target_datatype (dm3_file .imagedata , dtype )
4562 this_layer = this_layer .swapaxes (0 , 1 )
@@ -55,7 +72,7 @@ def read_channel_count(self, _file_name: str) -> int:
5572 return 1
5673
5774
58- class Dm4ImageReader :
75+ class Dm4ImageReader ( ImageReader ) :
5976 def _read_tags (self , dm4file : DM4File ) -> Tuple [DM4File .DM4TagDir , DM4TagHeader ]:
6077 tags = dm4file .read_directory ()
6178 image_data_tag = (
@@ -78,7 +95,7 @@ def _read_dimensions(
7895 )
7996 return width , height
8097
81- def read_array (self , file_name : str , dtype : np .dtype ) -> np .ndarray :
98+ def read_array (self , file_name : str , dtype : np .dtype , z_slice : int ) -> np .ndarray :
8299 dm4file = DM4File .open (file_name )
83100 image_data_tag , image_tag = self ._read_tags (dm4file )
84101 width , height = self ._read_dimensions (dm4file , image_data_tag )
@@ -94,7 +111,6 @@ def read_array(self, file_name: str, dtype: np.dtype) -> np.ndarray:
94111 return data
95112
96113 def read_dimensions (self , file_name : str ) -> Tuple [int , int ]:
97-
98114 dm4file = DM4File .open (file_name )
99115 image_data_tag , _ = self ._read_tags (dm4file )
100116 dimensions = self ._read_dimensions (dm4file , image_data_tag )
@@ -107,25 +123,83 @@ def read_channel_count(self, _file_name: str) -> int:
107123 return 1
108124
109125
110- class ImageReader :
126+ def find_count_of_axis (tif_file : TiffFile , axis : str ) -> int :
127+ assert len (tif_file .series ) == 1 , "only single tif series are supported"
128+ tif_series = tif_file .series [0 ]
129+ index = tif_series .axes .find (axis )
130+ if index == - 1 :
131+ return 1
132+ else :
133+ return tif_series .shape [index ] # pylint: disable=unsubscriptable-object
134+
135+
136+ class TiffImageReader (ImageReader ):
137+ def read_array (self , file_name : str , dtype : np .dtype , z_slice : int ) -> np .ndarray :
138+ with TiffFile (file_name ) as tif_file :
139+ num_channels = self .read_channel_count (file_name )
140+ if len (tif_file .pages ) > num_channels :
141+ data = np .array (
142+ list (
143+ map (
144+ lambda x : x .asarray (),
145+ tif_file .pages [
146+ z_slice * num_channels : z_slice * num_channels
147+ + num_channels
148+ ],
149+ )
150+ ),
151+ dtype ,
152+ )
153+ else :
154+ data = np .array (
155+ list (map (lambda x : x .asarray (), tif_file .pages [0 :num_channels ])),
156+ dtype ,
157+ )
158+ # transpose data to shape(x, y, channel_count)
159+ data = np .transpose (
160+ data ,
161+ (
162+ tif_file .pages [0 ].axes .find ("X" ) + 1 ,
163+ tif_file .pages [0 ].axes .find ("Y" ) + 1 ,
164+ 0 ,
165+ ),
166+ )
167+ data = data .reshape (data .shape + (1 ,))
168+ return data
169+
170+ def read_dimensions (self , file_name : str ) -> Tuple [int , int ]:
171+ with TiffFile (file_name ) as tif_file :
172+ return find_count_of_axis (tif_file , "X" ), find_count_of_axis (tif_file , "Y" )
173+
174+ def read_channel_count (self , file_name : str ) -> int :
175+ with TiffFile (file_name ) as tif_file :
176+ return find_count_of_axis (tif_file , "C" )
177+
178+ def read_z_slices_per_file (self , file_name : str ) -> int :
179+ with TiffFile (file_name ) as tif_file :
180+ return find_count_of_axis (tif_file , "Z" )
181+
182+
183+ class ImageReaderManager :
111184 def __init__ (self ) -> None :
112185 self .readers : Dict [
113- str , Union [PillowImageReader , Dm3ImageReader , Dm4ImageReader ]
186+ str ,
187+ Union [TiffImageReader , PillowImageReader , Dm3ImageReader , Dm4ImageReader ],
114188 ] = {
115- ".tif" : PillowImageReader (),
116- ".tiff" : PillowImageReader (),
189+ ".tif" : TiffImageReader (),
190+ ".tiff" : TiffImageReader (),
117191 ".jpg" : PillowImageReader (),
118192 ".jpeg" : PillowImageReader (),
119193 ".png" : PillowImageReader (),
120194 ".dm3" : Dm3ImageReader (),
121195 ".dm4" : Dm4ImageReader (),
122196 }
123197
124- def read_array (self , file_name : str , dtype : np .dtype ) -> np .ndarray :
198+ def read_array (self , file_name : str , dtype : np .dtype , z_slice : int ) -> np .ndarray :
125199 _ , ext = path .splitext (file_name )
126200
127201 # Image shape will be (x, y, channel_count, z=1) or (x, y, z=1)
128- image = self .readers [ext ].read_array (file_name , dtype )
202+ image = self .readers [ext ].read_array (file_name , dtype , z_slice )
129203 # Standardize the image shape to (x, y, channel_count, z=1)
130204 if image .ndim == 3 :
131205 image = image .reshape (image .shape + (1 ,))
@@ -140,5 +214,9 @@ def read_channel_count(self, file_name: str) -> int:
140214 _ , ext = path .splitext (file_name )
141215 return self .readers [ext ].read_channel_count (file_name )
142216
217+ def read_z_slices_per_file (self , file_name : str ) -> int :
218+ _ , ext = path .splitext (file_name )
219+ return self .readers [ext ].read_z_slices_per_file (file_name )
220+
143221
144- image_reader = ImageReader ()
222+ image_reader = ImageReaderManager ()
0 commit comments