@@ -102,9 +102,11 @@ def __init__(
102102 images_dir : str = "." ,
103103 labels_dir : str = "labels" ,
104104 datastore_config : str = "datastore_v2.json" ,
105- extensions = ("*.nii.gz" , "*.nii" ),
105+ extensions = ("*.nii.gz" , "*.nii" , "*.nrrd" ),
106106 auto_reload = False ,
107107 read_only = False ,
108+ multichannel : bool = False ,
109+ multi_file : bool = False ,
108110 ):
109111 """
110112 Creates a `LocalDataset` object
@@ -124,6 +126,8 @@ def __init__(
124126 self ._ignore_event_config = False
125127 self ._config_ts = 0
126128 self ._auto_reload = auto_reload
129+ self ._multichannel : bool = multichannel
130+ self ._multi_file : bool = multi_file
127131
128132 logging .getLogger ("filelock" ).setLevel (logging .ERROR )
129133
@@ -256,6 +260,18 @@ def datalist(self, full_path=True) -> List[Dict[str, Any]]:
256260 ds = json .loads (json .dumps (ds ).replace (f"{ self ._datastore_path .rstrip (os .pathsep )} { os .pathsep } " , "" ))
257261 return ds
258262
263+ def get_is_multichannel (self ) -> bool :
264+ """
265+ Returns whether the dataset is multichannel or not
266+ """
267+ return self ._multichannel
268+
269+ def get_is_multi_file (self ) -> bool :
270+ """
271+ Returns whether the dataset is multichannel or not
272+ """
273+ return self ._multi_file
274+
259275 def get_image (self , image_id : str , params = None ) -> Any :
260276 """
261277 Retrieve image object based on image id
@@ -431,6 +447,43 @@ def refresh(self):
431447 """
432448 self ._reconcile_datastore ()
433449
450+ def add_directory (self , directory_id : str , filename : str , info : Dict [str , Any ]) -> str :
451+ """
452+ Add a directory to the datastore
453+
454+ :param directory_id: the directory id
455+ :param filename: the filename
456+ :param info: additional info
457+
458+ :return: directory id
459+ """
460+ id = os .path .basename (os .path .normpath (filename ))
461+ if not directory_id :
462+ directory_id = id
463+
464+ logger .info (f"Adding Image: { directory_id } => { filename } " )
465+ name = directory_id
466+ dest = os .path .realpath (os .path .join (self ._datastore .image_path (), name ))
467+
468+ with FileLock (self ._lock_file ):
469+ logger .debug ("Acquired the lock!" )
470+ if os .path .isdir (filename ):
471+ if os .path .exists (dest ):
472+ shutil .rmtree (dest )
473+ shutil .copytree (filename , dest )
474+ else :
475+ shutil .copy2 (filename , dest )
476+
477+ info = info if info else {}
478+ info ["ts" ] = int (time .time ())
479+ info ["name" ] = name
480+
481+ # images = get_directory_contents(filename)
482+ self ._datastore .objects [directory_id ] = ImageLabelModel (image = DataModel (info = info , ext = "" ))
483+ self ._update_datastore_file (lock = False )
484+ logger .debug ("Released the lock!" )
485+ return directory_id
486+
434487 def add_image (self , image_id : str , image_filename : str , image_info : Dict [str , Any ]) -> str :
435488 id , image_ext = self ._to_id (os .path .basename (image_filename ))
436489 if not image_id :
@@ -552,10 +605,17 @@ def _list_files(self, path, patterns):
552605 files = os .listdir (path )
553606
554607 filtered = dict ()
555- for pattern in patterns :
556- matching = fnmatch .filter (files , pattern )
557- for file in matching :
558- filtered [os .path .basename (file )] = file
608+ if not self ._multi_file :
609+ for pattern in patterns :
610+ matching = fnmatch .filter (files , pattern )
611+ for file in matching :
612+ filtered [os .path .basename (file )] = file
613+ else :
614+ ignored = {"labels" , ".lock" , os .path .basename (self ._datastore_config_path ).lower ()}
615+ for file in files :
616+ abs_file = os .path .join (path , file )
617+ if os .path .isdir (abs_file ) and file .lower () not in ignored :
618+ filtered [os .path .basename (file )] = file
559619 return filtered
560620
561621 def _reconcile_datastore (self ):
@@ -585,24 +645,26 @@ def _add_non_existing_images(self) -> int:
585645 invalidate = 0
586646 self ._init_from_datastore_file ()
587647
588- local_images = self ._list_files (self ._datastore .image_path (), self ._extensions )
648+ local_files = self ._list_files (self ._datastore .image_path (), self ._extensions )
589649
590- image_ids = list (self ._datastore .objects .keys ())
591- for image_file in local_images :
592- image_id , image_ext = self ._to_id (image_file )
593- if image_id not in image_ids :
594- logger .info (f"Adding New Image: { image_id } => { image_file } " )
650+ ids = list (self ._datastore .objects .keys ())
651+ for file in local_files :
652+ if self ._multi_file :
653+ # Directories have no extension — use the name as-is
654+ file_id = file
655+ file_ext_str = ""
656+ else :
657+ file_id , file_ext_str = self ._to_id (file )
595658
596- name = self ._filename (image_id , image_ext )
597- image_info = {
659+ if file_id not in ids :
660+ logger .info (f"Adding New Image: { file_id } => { file } " )
661+ name = self ._filename (file_id , file_ext_str )
662+ file_info = {
598663 "ts" : int (time .time ()),
599- # "checksum": file_checksum(os.path.join(self._datastore.image_path(), name)),
600664 "name" : name ,
601665 }
602-
603666 invalidate += 1
604- self ._datastore .objects [image_id ] = ImageLabelModel (image = DataModel (info = image_info , ext = image_ext ))
605-
667+ self ._datastore .objects [file_id ] = ImageLabelModel (image = DataModel (info = file_info , ext = file_ext_str ))
606668 return invalidate
607669
608670 def _add_non_existing_labels (self , tag ) -> int :
0 commit comments