@@ -116,29 +116,198 @@ def create_tile_data_dicts_from_json(
116116 label_json : Optional [str ] = None ,
117117 mask_json : Optional [str ] = None ,
118118 chunk_num : Tuple [int , int , int ] = (2 , 2 , 2 ),
119+ chunk_indices : Optional [List [Dict [str , Any ]]] = None ,
119120) -> List [Dict [str , Any ]]:
120121 """
121122 Create MONAI data dictionaries from tile JSON metadata files.
122123
124+ This function loads tile metadata from JSON files and creates data dictionaries
125+ for each chunk of the volume. It's useful for preparing data before creating
126+ a dataset, or for custom dataset implementations.
127+
128+ JSON Schema:
129+ The JSON file should contain volume metadata in the following format:
130+ {
131+ "depth": int, # Volume depth in pixels/voxels
132+ "height": int, # Volume height in pixels/voxels
133+ "width": int, # Volume width in pixels/voxels
134+ "tiles": [ # List of tile files (optional)
135+ {
136+ "file": str, # Path to tile file
137+ "z_start": int, # Starting z coordinate
138+ "z_end": int, # Ending z coordinate
139+ "y_start": int, # Starting y coordinate
140+ "y_end": int, # Ending y coordinate
141+ "x_start": int, # Starting x coordinate
142+ "x_end": int # Ending x coordinate
143+ },
144+ ...
145+ ],
146+ "tile_size": [int, int, int], # Optional: default tile size (z, y, x)
147+ "overlap": [int, int, int], # Optional: tile overlap (z, y, x)
148+ "format": str, # Optional: file format (e.g., "tif", "h5")
149+ "metadata": {...} # Optional: additional metadata
150+ }
151+
123152 Args:
124- volume_json: JSON metadata file for input image tiles
125- label_json: Optional JSON metadata file for label tiles
126- mask_json: Optional JSON metadata file for mask tiles
127- chunk_num: Volume splitting parameters (z, y, x)
153+ volume_json: Path to JSON metadata file for input image tiles
154+ label_json: Optional path to JSON metadata file for label tiles
155+ mask_json: Optional path to JSON metadata file for mask tiles
156+ chunk_num: Volume splitting parameters (z, y, x). Default: (2, 2, 2)
157+ chunk_indices: Optional predefined list of chunk information dicts.
158+ Each dict should have 'chunk_id' and 'coords' keys.
128159
129160 Returns:
130- List of MONAI-style data dictionaries for tile chunks
131-
161+ List of MONAI-style data dictionaries for tile chunks.
162+ Each dictionary contains nested dicts for 'image', 'label' (if provided),
163+ and 'mask' (if provided) with metadata and chunk coordinates.
164+
132165 Examples:
133- >>> data_dicts = create_tile_data_dicts_from_json('tiles.json')
166+ >>> # Create data dicts from JSON with automatic chunking
167+ >>> data_dicts = create_tile_data_dicts_from_json(
168+ ... volume_json='tiles/image.json',
169+ ... label_json='tiles/label.json',
170+ ... chunk_num=(2, 2, 2)
171+ ... )
172+ >>> len(data_dicts) # 2*2*2 = 8 chunks
173+ 8
174+
175+ >>> # Create with custom chunk indices
176+ >>> custom_chunks = [
177+ ... {'chunk_id': (0, 0, 0), 'coords': (0, 100, 0, 200, 0, 200)},
178+ ... {'chunk_id': (0, 0, 1), 'coords': (0, 100, 0, 200, 200, 400)},
179+ ... ]
180+ >>> data_dicts = create_tile_data_dicts_from_json(
181+ ... 'tiles/image.json',
182+ ... chunk_indices=custom_chunks
183+ ... )
184+
185+ Raises:
186+ FileNotFoundError: If JSON file doesn't exist
187+ ValueError: If JSON is malformed or missing required fields
188+ KeyError: If required keys are missing from JSON
134189 """
135- # This would use the same logic as in MonaiTileDataset._create_chunk_data_dicts
136- # but as a standalone function
137- # TODO: Implement if needed
138- raise NotImplementedError (
139- "create_tile_data_dicts_from_json is not yet implemented. "
140- "Use create_tile_dataset() directly instead."
141- )
190+ import json
191+ from pathlib import Path
192+
193+ # Load volume metadata
194+ volume_path = Path (volume_json )
195+ if not volume_path .exists ():
196+ raise FileNotFoundError (f"Volume JSON file not found: { volume_json } " )
197+
198+ with open (volume_path , 'r' ) as f :
199+ volume_metadata = json .load (f )
200+
201+ # Validate required fields
202+ required_fields = ['depth' , 'height' , 'width' ]
203+ missing_fields = [field for field in required_fields if field not in volume_metadata ]
204+ if missing_fields :
205+ raise KeyError (
206+ f"Volume JSON missing required fields: { missing_fields } . "
207+ f"Required fields: { required_fields } "
208+ )
209+
210+ # Load label metadata if provided
211+ label_metadata = None
212+ if label_json is not None :
213+ label_path = Path (label_json )
214+ if not label_path .exists ():
215+ raise FileNotFoundError (f"Label JSON file not found: { label_json } " )
216+ with open (label_path , 'r' ) as f :
217+ label_metadata = json .load (f )
218+
219+ # Load mask metadata if provided
220+ mask_metadata = None
221+ if mask_json is not None :
222+ mask_path = Path (mask_json )
223+ if not mask_path .exists ():
224+ raise FileNotFoundError (f"Mask JSON file not found: { mask_json } " )
225+ with open (mask_path , 'r' ) as f :
226+ mask_metadata = json .load (f )
227+
228+ # Calculate chunk indices if not provided
229+ if chunk_indices is None :
230+ chunk_indices = _calculate_chunk_indices (volume_metadata , chunk_num )
231+
232+ # Create data dictionaries for each chunk
233+ data_dicts = []
234+ for chunk_info in chunk_indices :
235+ chunk_id = chunk_info ['chunk_id' ]
236+ coords = chunk_info ['coords' ]
237+
238+ data_dict = {
239+ 'image' : {
240+ 'metadata' : volume_metadata ,
241+ 'chunk_coords' : coords ,
242+ 'chunk_id' : chunk_id ,
243+ },
244+ }
245+
246+ if label_metadata is not None :
247+ data_dict ['label' ] = {
248+ 'metadata' : label_metadata ,
249+ 'chunk_coords' : coords ,
250+ 'chunk_id' : chunk_id ,
251+ }
252+
253+ if mask_metadata is not None :
254+ data_dict ['mask' ] = {
255+ 'metadata' : mask_metadata ,
256+ 'chunk_coords' : coords ,
257+ 'chunk_id' : chunk_id ,
258+ }
259+
260+ data_dicts .append (data_dict )
261+
262+ return data_dicts
263+
264+
265+ def _calculate_chunk_indices (
266+ volume_metadata : Dict [str , Any ],
267+ chunk_num : Tuple [int , int , int ],
268+ ) -> List [Dict [str , Any ]]:
269+ """
270+ Calculate chunk indices based on chunk_num and volume dimensions.
271+
272+ This is a helper function used by create_tile_data_dicts_from_json.
273+
274+ Args:
275+ volume_metadata: Dictionary containing 'depth', 'height', 'width' keys
276+ chunk_num: Number of chunks in each dimension (z, y, x)
277+
278+ Returns:
279+ List of chunk information dictionaries, each containing:
280+ - 'chunk_id': Tuple of (z, y, x) chunk indices
281+ - 'coords': Tuple of (z_start, z_end, y_start, y_end, x_start, x_end)
282+ """
283+ # Get volume dimensions
284+ depth = volume_metadata ['depth' ]
285+ height = volume_metadata ['height' ]
286+ width = volume_metadata ['width' ]
287+
288+ # Calculate chunk sizes
289+ chunk_z = depth // chunk_num [0 ]
290+ chunk_y = height // chunk_num [1 ]
291+ chunk_x = width // chunk_num [2 ]
292+
293+ chunk_indices = []
294+ for z in range (chunk_num [0 ]):
295+ for y in range (chunk_num [1 ]):
296+ for x in range (chunk_num [2 ]):
297+ # Calculate chunk boundaries
298+ z_start = z * chunk_z
299+ z_end = min ((z + 1 ) * chunk_z , depth )
300+ y_start = y * chunk_y
301+ y_end = min ((y + 1 ) * chunk_y , height )
302+ x_start = x * chunk_x
303+ x_end = min ((x + 1 ) * chunk_x , width )
304+
305+ chunk_indices .append ({
306+ 'chunk_id' : (z , y , x ),
307+ 'coords' : (z_start , z_end , y_start , y_end , x_start , x_end ),
308+ })
309+
310+ return chunk_indices
142311
143312
144313# ============================================================================
0 commit comments