def from_json(cls, data, project_meta: ProjectMeta): ''' The function from_json convert Label from json format to Label class object. If there is no ObjClass from input json format in ProjectMeta, it generate RuntimeError error. :param data: input label in json format :param project_meta: ProjectMeta class object :return: Label class object ''' obj_class_name = data[LabelJsonFields.OBJ_CLASS_NAME] obj_class = project_meta.get_obj_class(obj_class_name) if obj_class is None: raise RuntimeError( f'Failed to deserialize a Label object from JSON: label class name {obj_class_name!r} ' f'was not found in the given project meta.') if obj_class.geometry_type is AnyGeometry: geometry_type_actual = GET_GEOMETRY_FROM_STR( data[GEOMETRY_TYPE] if GEOMETRY_TYPE in data else data[GEOMETRY_SHAPE]) geometry = geometry_type_actual.from_json(data) else: geometry = obj_class.geometry_type.from_json(data) return cls(geometry=geometry, obj_class=obj_class, tags=TagCollection.from_json(data[LabelJsonFields.TAGS], project_meta.tag_metas), description=data.get(LabelJsonFields.DESCRIPTION, ""))
def apply(augs, meta: ProjectMeta, img, ann: Annotation, segmentation_type='semantic'): # @TODO: save object tags # works for rectangles det_meta, det_mapping = meta.to_detection_task(convert_classes=False) det_ann = ann.to_detection_task(det_mapping) ia_boxes = det_ann.bboxes_to_imgaug() # works for polygons and bitmaps seg_meta, seg_mapping = meta.to_segmentation_task() seg_ann = ann.to_nonoverlapping_masks(seg_mapping) if segmentation_type == 'semantic': seg_ann = seg_ann.to_segmentation_task() class_to_index = { obj_class.name: idx for idx, obj_class in enumerate(seg_meta.obj_classes, start=1) } index_to_class = {v: k for k, v in class_to_index.items()} elif segmentation_type == 'instance': class_to_index = None index_to_class = { idx: label.obj_class.name for idx, label in enumerate(seg_ann.labels, start=1) } elif segmentation_type == 'panoptic': raise NotImplementedError ia_masks = seg_ann.masks_to_imgaug(class_to_index) res_meta = det_meta.merge(seg_meta) # TagMetas should be preserved res_img, res_ia_boxes, res_ia_masks = _apply(augs, img, ia_boxes, ia_masks) res_ann = Annotation.from_imgaug(res_img, ia_boxes=res_ia_boxes, ia_masks=res_ia_masks, index_to_class=index_to_class, meta=res_meta) # add image tags res_ann = res_ann.clone(img_tags=ann.img_tags) return res_meta, res_img, res_ann
def merge_metas(self, src_project_id, dst_project_id): ''' Add metadata from given progect to given destination project :param src_project_id: int :param dst_project_id: int :return: merged project metainformation ''' if src_project_id == dst_project_id: return self.get_meta(src_project_id) src_meta = ProjectMeta.from_json(self.get_meta(src_project_id)) dst_meta = ProjectMeta.from_json(self.get_meta(dst_project_id)) new_dst_meta = src_meta.merge(dst_meta) new_dst_meta_json = new_dst_meta.to_json() self.update_meta(dst_project_id, new_dst_meta.to_json()) return new_dst_meta_json
def to_json(self): return { "content": { "projectMeta": ProjectMeta().to_json(), "layout": [], "annotations": {} }, "options": {} }
def __init__(self, task_id, api: Api, v_model, project_meta: ProjectMeta): self._task_id = task_id self._api = api self._v_model = v_model self._project_meta = project_meta.clone() self._image_url = None self._ann = None self._options_initialized = False self.set_options()
def from_json(cls, data, project_meta: ProjectMeta, key_id_map: KeyIdMap = None): ''' The function from_json convert PointcloudObject from json format to PointcloudObject class object. Raise error if object class name is not found in the given project meta :param data: input PointcloudObject in json format :param project_meta: ProjectMeta class object :param key_id_map: KeyIdMap class object :return: PointcloudObject class object ''' obj_class_name = data[LabelJsonFields.OBJ_CLASS_NAME] obj_class = project_meta.get_obj_class(obj_class_name) if obj_class is None: raise RuntimeError( f'Failed to deserialize a object from JSON: class name {obj_class_name!r} ' f'was not found in the given project meta.') object_id = data.get(ID, None) existing_key = None if object_id is not None and key_id_map is not None: existing_key = key_id_map.get_object_key(object_id) json_key = uuid.UUID(data[KEY]) if KEY in data else None if (existing_key is not None) and (json_key is not None) and (existing_key != json_key): raise RuntimeError( "Object id = {!r}: existing_key {!r} != json_key {!r}".format( object_id, existing_key, json_key)) if existing_key is not None: key = existing_key elif json_key is not None: key = json_key else: key = uuid.uuid4() if key_id_map is not None and existing_key is None: key_id_map.add_object(key, object_id) class_id = data.get(CLASS_ID, None) labeler_login = data.get(LABELER_LOGIN, None) updated_at = data.get(UPDATED_AT, None) created_at = data.get(CREATED_AT, None) return cls(obj_class=obj_class, key=key, tags=VideoTagCollection.from_json( data[LabelJsonFields.TAGS], project_meta.tag_metas), class_id=class_id, labeler_login=labeler_login, updated_at=updated_at, created_at=created_at)
def from_imgaug(cls, img, ia_boxes=None, ia_masks=None, index_to_class=None, meta: ProjectMeta = None): if ((ia_boxes is not None) or (ia_masks is not None)) and meta is None: raise ValueError("Project meta has to be provided") labels = [] if ia_boxes is not None: for ia_box in ia_boxes: obj_class = meta.get_obj_class(ia_box.label) if obj_class is None: raise KeyError( "Class {!r} not found in project meta".format( ia_box.label)) lbl = Label( Rectangle(top=ia_box.y1, left=ia_box.x1, bottom=ia_box.y2, right=ia_box.x2), obj_class) labels.append(lbl) if ia_masks is not None: if index_to_class is None: raise ValueError( "mapping from index to class name is needed to transform masks to SLY format" ) class_mask = ia_masks.get_arr() # mask = white_mask == 255 (unique, counts) = np.unique(class_mask, return_counts=True) for index, count in zip(unique, counts): if index == 0: continue mask = class_mask == index bitmap = Bitmap(data=mask[:, :, 0]) restore_class = meta.get_obj_class(index_to_class[index]) labels.append(Label(geometry=bitmap, obj_class=restore_class)) return cls(img_size=img.shape[:2], labels=labels)
def from_json(cls, data, project_meta): ''' The function from_json convert annotation from json format to Annotation class object. If one of the labels of annotation in json format cannot be convert to Label class object it generate exception error. :param data: input annotation in json format :param project_meta: ProjectMeta class object :return: Annotation class object ''' img_size_dict = data[AnnotationJsonFields.IMG_SIZE] img_height = img_size_dict[AnnotationJsonFields.IMG_SIZE_HEIGHT] img_width = img_size_dict[AnnotationJsonFields.IMG_SIZE_WIDTH] img_size = (img_height, img_width) try: labels = [ Label.from_json(label_json, project_meta) for label_json in data[AnnotationJsonFields.LABELS] ] except Exception: logger.fatal( 'Failed to deserialize annotation from JSON format. One of the Label objects could not be ' 'deserialized') raise custom_data = data.get(AnnotationJsonFields.CUSTOM_DATA, {}) prob_labels = None if AnnotationJsonFields.PROBABILITY_LABELS in custom_data and \ AnnotationJsonFields.PROBABILITY_CLASSES in custom_data: prob_classes = ObjClassCollection.from_json( custom_data[AnnotationJsonFields.PROBABILITY_CLASSES]) # @TODO: tony, maybe link with project meta (add probability classes???) prob_project_meta = ProjectMeta(obj_classes=prob_classes) prob_labels = [ Label.from_json(label_json, prob_project_meta) for label_json in custom_data[AnnotationJsonFields.PROBABILITY_LABELS] ] custom_data.pop(AnnotationJsonFields.PROBABILITY_CLASSES) custom_data.pop(AnnotationJsonFields.PROBABILITY_LABELS) image_id = data.get(AnnotationJsonFields.IMAGE_ID, None) return cls(img_size=img_size, labels=labels, img_tags=TagCollection.from_json( data[AnnotationJsonFields.IMG_TAGS], project_meta.tag_metas), img_description=data.get( AnnotationJsonFields.IMG_DESCRIPTION, ""), pixelwise_scores_labels=prob_labels, custom_data=custom_data, image_id=image_id)
def _do_single_img_inference(self, img, in_msg): in_project_meta = self._in_project_meta_from_msg(in_msg) ann_json = in_msg.get('annotation') if ann_json is not None: if in_project_meta is None: raise ValueError('In order to perform inference with annotation you must specify the appropriate' ' project meta.') ann = Annotation.from_json(ann_json, in_project_meta) else: in_project_meta = in_project_meta or ProjectMeta() ann = Annotation(img.shape[:2]) inference_mode = self._make_inference_mode(in_msg.get(MODE, {}), in_project_meta) inference_result = inference_mode.infer_annotate(img, ann) return inference_result.to_json()
def inference(self, id, img, ann=None, meta=None, mode=None, ext=None): data = { "request_type": "inference", "meta": meta or ProjectMeta().to_json(), "annotation": ann or None, "mode": mode or {}, } img_data = sly_image.write_bytes(img, ext or '.jpg') encoder = MultipartEncoder({ 'id': str(id).encode('utf-8'), 'data': json.dumps(data), 'image': ("img", img_data, "") }) response = self._api.post('models.infer', MultipartEncoderMonitor(encoder)) return response.json()
def post(self): args = self._parser.parse_args() meta = args[META] mode = args[MODE] data = { "request_type": GET_OUTPUT_META, "meta": json.loads(meta) if meta is not None else ProjectMeta().to_json(), "mode": json.loads(mode) if mode is not None else {} } response_json = self._model._final_processing(data) if 'out_meta' in response_json: return response_json['out_meta'] if 'output_meta' in response_json: return response_json['output_meta'] return response_json
def get_output_meta(self, id, input_meta=None, inference_mode=None): data = { "request_type": "get_out_meta", "meta": input_meta or ProjectMeta().to_json(), "mode": inference_mode or {} } encoder = MultipartEncoder({ 'id': str(id).encode('utf-8'), 'data': json.dumps(data) }) response = self._api.post('models.infer', MultipartEncoderMonitor(encoder)) response_json = response.json() if 'out_meta' in response_json: return response_json['out_meta'] if 'output_meta' in response_json: return response_json['output_meta'] return response.json()
def setUp(self): self._obj_class_gt = ObjClass(name='a', geometry_type=Rectangle) self._obj_class_pred = ObjClass(name='b', geometry_type=Rectangle) self._confidence_tag_meta = TagMeta(name='confidence', value_type=TagValueType.ANY_NUMBER) self._meta = ProjectMeta( obj_classes=ObjClassCollection([self._obj_class_gt, self._obj_class_pred]), tag_metas=TagMetaCollection([self._confidence_tag_meta])) # Will match self._pred_obj_1 self._gt_obj_1 = Label(obj_class=self._obj_class_gt, geometry=Rectangle(0, 0, 10, 10)) # Will match self._pred_obj_3 self._gt_obj_2 = Label(obj_class=self._obj_class_gt, geometry=Rectangle(13, 13, 15, 15)) # Will be a false negative self._gt_obj_3 = Label(obj_class=self._obj_class_gt, geometry=Rectangle(43, 43, 45, 45)) # Will match self._gt_obj_1 self._pred_obj_1 = Label( obj_class=self._obj_class_pred, geometry=Rectangle(0, 0, 9, 9), tags=TagCollection([Tag(meta=self._confidence_tag_meta, value=0.7)])) # Will be a false positive (self._pred_obj_1 has higher IoU). self._pred_obj_2 = Label( obj_class=self._obj_class_pred, geometry=Rectangle(0, 0, 8, 8), tags=TagCollection([Tag(meta=self._confidence_tag_meta, value=0.6)])) # Will match self._gt_obj_2 self._pred_obj_3 = Label( obj_class=self._obj_class_pred, geometry=Rectangle(13, 13, 15, 15), tags=TagCollection([Tag(meta=self._confidence_tag_meta, value=0.1)])) # More false positives. self._pred_objs_fp = [ Label(obj_class=self._obj_class_pred, geometry=Rectangle(20, 20, 30, 30), tags=TagCollection([Tag(meta=self._confidence_tag_meta, value=v / 100)])) for v in range(15, 85, 10)] self._metric_calculator = MAPMetric(class_mapping={'a': 'b'}, iou_threshold=0.5)
def __init__(self, task_id, api: Api, v_model, project_meta: ProjectMeta, col_number: int, preview_info=False, enable_zoom=False, resize_on_zoom=False, sync_views=False, show_preview=True, selectable=False, opacity=0.5, show_opacity_header=True, fill_rectangle=False, border_width=3): self._task_id = task_id self._api = api self._v_model = v_model self._project_meta = project_meta.clone() self._data = {} self.col_number = col_number self.preview_info = preview_info self._need_zoom = False self._with_title_url = False if not isinstance(self.col_number, int): raise ValueError("Columns number must be integer, not {}".format( type(self.col_number).__name__)) self._options = { "enableZoom": enable_zoom, "syncViews": sync_views, "resizeOnZoom": resize_on_zoom, "showPreview": show_preview, "selectable": selectable, "opacity": opacity, "showOpacityInHeader": show_opacity_header, "fillRectangle": fill_rectangle, "borderWidth": border_width } self._options_initialized = False
def _load_train_config(self): self._load_raw_model_config_json() self.class_title_to_idx = self.train_config[ self.class_title_to_idx_key] logger.info('Read model internal class mapping', extra={'class_mapping': self.class_title_to_idx}) train_classes = ObjClassCollection.from_json( self.train_config[self.train_classes_key]) logger.info('Read model out classes', extra={'classes': train_classes.to_json()}) # TODO: Factor out meta constructing from _load_train_config method. self._model_out_meta = ProjectMeta(obj_classes=train_classes, tag_metas=self._model_out_tags()) # Make a separate [index] --> [class] map that excludes the 'special' classes that should not be in the` # final output. self.out_class_mapping = { idx: train_classes.get(title) for title, idx in self.class_title_to_idx.items() if train_classes.has_key(title) }
def __init__(self, task_id, api: Api, v_model: str, project_meta: ProjectMeta): self._task_id = task_id self._api = api self._v_model = v_model if self._v_model.startswith("data.") is False: new_v_model = v_model.replace('state.', 'data.', 1) raise KeyError( f"Data for this widget has to be stored in data field due to potential performance issues. " f"Please, change value of v_model argument " f"from {v_model} to {new_v_model} manually and also check html template" ) self._project_meta = project_meta.clone() self._options = { "enableZoom": True, "syncViews": True, "showPreview": True, "selectable": True, "opacity": 0.5, "showOpacityInHeader": True, #"viewHeight": 450, } self._num_cols = 3 self._gt_annotations = {} self._pred_annotations = {} self._order = [] self._infos = {} self._min_time_index = None self._max_time_index = None self._active_time = None self._items_to_upload = { "gt": [], #"pred": {} }
def __init__(self, task_id, api: Api, v_model, project_meta: ProjectMeta): self._task_id = task_id self._api = api self._v_model = v_model self._project_meta = project_meta.clone() self._left_title = None self._left_image_url = None self._left_ann = None self._right_title = None self._right_image_url = None self._right_ann = None self._options = { "enableZoom": True, "syncViews": True, "showPreview": False, "selectable": False, "opacity": 0.5, "showOpacityInHeader": True, # "viewHeight": 450, } self._options_initialized = False
def from_json(cls, data, project_meta: ProjectMeta, key_id_map: KeyIdMap = None): ''' The function from_json convert VideoObject from json format to VideoObject class object. Raise error if object class name is not found in the given project meta :param data: input VideoObject in json format :param project_meta: ProjectMeta class object :param key_id_map: KeyIdMap class object :return: VideoObject class object ''' obj_class_name = data[LabelJsonFields.OBJ_CLASS_NAME] obj_class = project_meta.get_obj_class(obj_class_name) if obj_class is None: raise RuntimeError( f'Failed to deserialize a object from JSON: class name {obj_class_name!r} ' f'was not found in the given project meta.') key = uuid.UUID(data[KEY]) if KEY in data else uuid.uuid4() if key_id_map is not None: key_id_map.add_object(key, data.get(ID, None)) class_id = data.get(CLASS_ID, None) labeler_login = data.get(LABELER_LOGIN, None) updated_at = data.get(UPDATED_AT, None) created_at = data.get(CREATED_AT, None) return cls(obj_class=obj_class, key=key, tags=VideoTagCollection.from_json( data[LabelJsonFields.TAGS], project_meta.tag_metas), class_id=class_id, labeler_login=labeler_login, updated_at=updated_at, created_at=created_at)
def _get_out_meta(self, in_msg): in_meta = self._in_project_meta_from_msg(in_msg) or ProjectMeta() inference_mode = self._make_inference_mode(in_msg.get(MODE, {}), in_meta) return inference_mode.out_meta
def _in_project_meta_from_msg(in_msg): pr_meta_json = in_msg.get('meta') return ProjectMeta.from_json(pr_meta_json) if pr_meta_json is not None else None
def download_pointcloud_project(api, project_id, dest_dir, dataset_ids=None, download_items=True, log_progress=False): LOG_BATCH_SIZE = 1 key_id_map = KeyIdMap() project_fs = PointcloudProject(dest_dir, OpenMode.CREATE) meta = ProjectMeta.from_json(api.project.get_meta(project_id)) project_fs.set_meta(meta) datasets_infos = [] if dataset_ids is not None: for ds_id in dataset_ids: datasets_infos.append(api.dataset.get_info_by_id(ds_id)) else: datasets_infos = api.dataset.get_list(project_id) for dataset in datasets_infos: dataset_fs = project_fs.create_dataset(dataset.name) pointclouds = api.pointcloud.get_list(dataset.id) ds_progress = None if log_progress: ds_progress = Progress('Downloading dataset: {!r}'.format( dataset.name), total_cnt=len(pointclouds)) for batch in batched(pointclouds, batch_size=LOG_BATCH_SIZE): pointcloud_ids = [pointcloud_info.id for pointcloud_info in batch] pointcloud_names = [ pointcloud_info.name for pointcloud_info in batch ] ann_jsons = api.pointcloud.annotation.download_bulk( dataset.id, pointcloud_ids) for pointcloud_id, pointcloud_name, ann_json in zip( pointcloud_ids, pointcloud_names, ann_jsons): if pointcloud_name != ann_json[ApiField.NAME]: raise RuntimeError( "Error in api.video.annotation.download_batch: broken order" ) pointcloud_file_path = dataset_fs.generate_item_path( pointcloud_name) if download_items is True: api.pointcloud.download_path(pointcloud_id, pointcloud_file_path) related_images_path = dataset_fs.get_related_images_path( pointcloud_name) related_images = api.pointcloud.get_list_related_images( pointcloud_id) for rimage_info in related_images: name = rimage_info[ApiField.NAME] if not has_valid_ext(name): new_name = get_file_name( name) # to fix cases like .png.json if has_valid_ext(new_name): name = new_name rimage_info[ApiField.NAME] = name else: raise RuntimeError( 'Something wrong with photo context filenames.\ Please, contact support') rimage_id = rimage_info[ApiField.ID] path_img = os.path.join(related_images_path, name) path_json = os.path.join(related_images_path, name + ".json") api.pointcloud.download_related_image( rimage_id, path_img) dump_json_file(rimage_info, path_json) else: touch(pointcloud_file_path) dataset_fs.add_item_file(pointcloud_name, pointcloud_file_path, ann=PointcloudAnnotation.from_json( ann_json, project_fs.meta, key_id_map), _validate_item=False) ds_progress.iters_done_report(len(batch)) project_fs.set_key_id_map(key_id_map)
def post(self): args = self._parser.parse_args() img_bytes = args[IMAGE].stream.read() img = sly_image.read_bytes(img_bytes) meta = args[META] ann = args[ANNOTATION] mode = args[MODE] data = { "request_type": INFERENCE, "meta": json.loads(meta.stream.read().decode("utf-8")) if meta is not None else ProjectMeta().to_json(), "annotation": json.loads(ann.stream.read().decode("utf-8")) if ann is not None else None, "mode": json.loads(mode.stream.read().decode("utf-8")) if mode is not None else {}, 'image_arr': img } return self._model._final_processing(data)
def download_video_project(api, project_id, dest_dir, dataset_ids=None, download_videos=True, log_progress=False): ''' Download project with given id in destination directory :param api: Api class object :param project_id: int :param dest_dir: str :param dataset_ids: list of integers :param download_videos: bool :param log_progress: bool ''' LOG_BATCH_SIZE = 1 key_id_map = KeyIdMap() project_fs = VideoProject(dest_dir, OpenMode.CREATE) meta = ProjectMeta.from_json(api.project.get_meta(project_id)) project_fs.set_meta(meta) datasets_infos = [] if dataset_ids is not None: for ds_id in dataset_ids: datasets_infos.append(api.dataset.get_info_by_id(ds_id)) else: datasets_infos = api.dataset.get_list(project_id) for dataset in datasets_infos: dataset_fs = project_fs.create_dataset(dataset.name) videos = api.video.get_list(dataset.id) ds_progress = None if log_progress: ds_progress = Progress('Downloading dataset: {!r}'.format( dataset.name), total_cnt=len(videos)) for batch in batched(videos, batch_size=LOG_BATCH_SIZE): video_ids = [video_info.id for video_info in batch] video_names = [video_info.name for video_info in batch] ann_jsons = api.video.annotation.download_bulk( dataset.id, video_ids) for video_id, video_name, ann_json in zip(video_ids, video_names, ann_jsons): if video_name != ann_json[ApiField.VIDEO_NAME]: raise RuntimeError( "Error in api.video.annotation.download_batch: broken order" ) video_file_path = dataset_fs.generate_item_path(video_name) if download_videos is True: api.video.download_path(video_id, video_file_path) else: touch(video_file_path) dataset_fs.add_item_file(video_name, video_file_path, ann=VideoAnnotation.from_json( ann_json, project_fs.meta, key_id_map), _validate_item=False) ds_progress.iters_done_report(len(batch)) project_fs.set_key_id_map(key_id_map)
def download_pointcloud_episode_project(api, project_id, dest_dir, dataset_ids=None, download_pcd=True, download_realated_images=True, download_annotations=True, log_progress=False, batch_size=10): key_id_map = KeyIdMap() project_fs = PointcloudEpisodeProject(dest_dir, OpenMode.CREATE) meta = ProjectMeta.from_json(api.project.get_meta(project_id)) project_fs.set_meta(meta) datasets_infos = [] if dataset_ids is not None: for ds_id in dataset_ids: datasets_infos.append(api.dataset.get_info_by_id(ds_id)) else: datasets_infos = api.dataset.get_list(project_id) for dataset in datasets_infos: dataset_fs = project_fs.create_dataset(dataset.name) pointclouds = api.pointcloud_episode.get_list(dataset.id) if download_annotations: # Download annotation to project_path/dataset_path/annotation.json ann_json = api.pointcloud_episode.annotation.download(dataset.id) annotation = dataset_fs.annotation_class.from_json( ann_json, meta, key_id_map) dataset_fs.set_ann(annotation) # frames --> pointcloud mapping to project_path/dataset_path/frame_pointcloud_map.json frame_name_map = api.pointcloud_episode.get_frame_name_map( dataset.id) frame_pointcloud_map_path = dataset_fs.get_frame_pointcloud_map_path( ) dump_json_file(frame_name_map, frame_pointcloud_map_path) # Download data if log_progress: ds_progress = Progress('Downloading dataset: {!r}'.format( dataset.name), total_cnt=len(pointclouds)) for batch in batched(pointclouds, batch_size=batch_size): pointcloud_ids = [pointcloud_info.id for pointcloud_info in batch] pointcloud_names = [ pointcloud_info.name for pointcloud_info in batch ] for pointcloud_id, pointcloud_name in zip(pointcloud_ids, pointcloud_names): pointcloud_file_path = dataset_fs.generate_item_path( pointcloud_name) if download_pcd is True: api.pointcloud_episode.download_path( pointcloud_id, pointcloud_file_path) else: touch(pointcloud_file_path) if download_realated_images: related_images_path = dataset_fs.get_related_images_path( pointcloud_name) related_images = api.pointcloud_episode.get_list_related_images( pointcloud_id) for rimage_info in related_images: name = rimage_info[ApiField.NAME] rimage_id = rimage_info[ApiField.ID] path_img = os.path.join(related_images_path, name) path_json = os.path.join(related_images_path, name + ".json") api.pointcloud_episode.download_related_image( rimage_id, path_img) dump_json_file(rimage_info, path_json) dataset_fs.add_item_file(pointcloud_name, pointcloud_file_path, _validate_item=False) if log_progress: ds_progress.iters_done_report(len(batch)) project_fs.set_key_id_map(key_id_map)
def update_project_meta(self, project_meta: ProjectMeta): self._project_meta = project_meta.clone()