@@ -36,9 +36,9 @@ class ImageDirectoryLoader(Operator):
36
36
image: Image object loaded from file
37
37
filename: Name of the loaded file (without extension)
38
38
"""
39
-
40
- SUPPORTED_EXTENSIONS = [' .jpg' , ' .jpeg' , ' .png' , ' .bmp' , ' .tiff' , ' .tif' ]
41
-
39
+
40
+ SUPPORTED_EXTENSIONS = [" .jpg" , " .jpeg" , " .png" , " .bmp" , " .tiff" , " .tif" ]
41
+
42
42
def __init__ (
43
43
self ,
44
44
fragment : Fragment ,
@@ -57,82 +57,84 @@ def __init__(
57
57
self ._logger = logging .getLogger ("{}.{}" .format (__name__ , type (self ).__name__ ))
58
58
self ._input_folder = Path (input_folder )
59
59
self ._channel_first = bool (channel_first )
60
-
60
+
61
61
super ().__init__ (fragment , * args , ** kwargs )
62
-
62
+
63
63
def _find_image_files (self ) -> List [Path ]:
64
64
"""Find all supported image files in the input directory."""
65
65
image_files = []
66
66
for ext in self .SUPPORTED_EXTENSIONS :
67
67
image_files .extend (self ._input_folder .rglob (f"*{ ext } " ))
68
68
image_files .extend (self ._input_folder .rglob (f"*{ ext .upper ()} " ))
69
-
69
+
70
70
# Sort files for consistent ordering
71
71
image_files .sort ()
72
72
return image_files
73
-
73
+
74
74
def setup (self , spec : OperatorSpec ):
75
75
"""Define the operator outputs."""
76
76
spec .output ("image" )
77
77
spec .output ("filename" )
78
-
78
+
79
79
# Pre-initialize the image files list
80
80
self ._image_files = self ._find_image_files ()
81
81
self ._current_index = 0
82
-
82
+
83
83
if not self ._image_files :
84
84
self ._logger .warning (f"No image files found in { self ._input_folder } " )
85
85
else :
86
86
self ._logger .info (f"Found { len (self ._image_files )} image files to process" )
87
-
87
+
88
88
def compute (self , op_input , op_output , context ):
89
89
"""Load one image and emit it."""
90
-
90
+
91
91
# Check if we have more images to process
92
92
if self ._current_index >= len (self ._image_files ):
93
93
# No more images to process
94
94
self ._logger .info ("All images have been processed" )
95
95
self .fragment .stop_execution ()
96
96
return
97
-
97
+
98
98
# Get the current image path
99
99
image_path = self ._image_files [self ._current_index ]
100
-
100
+
101
101
try :
102
102
# Load image using PIL
103
103
pil_image = PILImage .open (image_path )
104
-
104
+
105
105
# Convert to RGB if necessary
106
- if pil_image .mode != ' RGB' :
107
- pil_image = pil_image .convert (' RGB' )
108
-
106
+ if pil_image .mode != " RGB" :
107
+ pil_image = pil_image .convert (" RGB" )
108
+
109
109
# Convert to numpy array (HWC float32). Intensity scaling (to [0,1]) is typically handled by bundle.
110
110
image_array = np .array (pil_image ).astype (np .float32 )
111
111
112
112
# Convert to channel-first when requested
113
113
if self ._channel_first :
114
114
# PIL loads HWC; convert to CHW
115
115
image_array = np .transpose (image_array , (2 , 0 , 1 ))
116
-
116
+
117
117
# Create metadata
118
118
metadata = {
119
119
"filename" : str (image_path ),
120
120
"original_shape" : image_array .shape ,
121
121
"source_format" : image_path .suffix .lower (),
122
122
}
123
-
123
+
124
124
# Create Image object
125
125
image_obj = Image (image_array , metadata = metadata )
126
-
126
+
127
127
# Emit the image and filename
128
128
op_output .emit (image_obj , "image" )
129
129
op_output .emit (image_path .stem , "filename" )
130
-
131
- self ._logger .info (f"Loaded and emitted image: { image_path .name } ({ self ._current_index + 1 } /{ len (self ._image_files )} )" )
132
-
130
+
131
+ self ._logger .info (
132
+ f"Loaded and emitted image: { image_path .name } ({ self ._current_index + 1 } /{ len (self ._image_files )} )"
133
+ )
134
+
133
135
except Exception as e :
134
136
self ._logger .error (f"Failed to load image { image_path } : { e } " )
135
-
137
+
136
138
# Move to the next image
137
139
self ._current_index += 1
138
140
@@ -141,41 +143,42 @@ def test():
141
143
"""Test the ImageDirectoryLoader operator."""
142
144
import tempfile
143
145
from PIL import Image as PILImageCreate
144
-
146
+
145
147
# Create a temporary directory with test images
146
148
with tempfile .TemporaryDirectory () as temp_dir :
147
149
temp_path = Path (temp_dir )
148
-
150
+
149
151
# Create test images
150
152
for i in range (3 ):
151
- img = PILImageCreate .new (' RGB' , (100 , 100 ), color = (i * 50 , i * 50 , i * 50 ))
153
+ img = PILImageCreate .new (" RGB" , (100 , 100 ), color = (i * 50 , i * 50 , i * 50 ))
152
154
img .save (temp_path / f"test_{ i } .jpg" )
153
-
155
+
154
156
# Test the operator
155
157
fragment = Fragment ()
156
158
loader = ImageDirectoryLoader (fragment , input_folder = temp_path )
157
-
159
+
158
160
# Simulate setup
159
161
from monai .deploy .core import OperatorSpec
162
+
160
163
spec = OperatorSpec ()
161
164
loader .setup (spec )
162
-
165
+
163
166
print (f"Found { len (loader ._image_files )} test images" )
164
-
167
+
165
168
# Simulate compute calls
166
169
class MockOutput :
167
170
def emit (self , data , name ):
168
171
if name == "filename" :
169
172
print (f"Emitted filename: { data } " )
170
173
elif name == "image" :
171
174
print (f"Emitted image with shape: { data .asnumpy ().shape } " )
172
-
175
+
173
176
mock_output = MockOutput ()
174
-
177
+
175
178
# Process all images
176
179
while loader ._current_index < len (loader ._image_files ):
177
180
loader .compute (None , mock_output , None )
178
181
179
182
180
183
if __name__ == "__main__" :
181
- test ()
184
+ test ()
0 commit comments