The ImagePairSequence widget is a widget in Supervisely designed for displaying pairs of images and annotations. It is useful for comparing. For example, it can be used to compare ground truth and predictions in a grid format. It allows users to navigate through multiple pages of predictions and provides zooming functionality, making it convenient for visualizing annotated image results.
All images will be saved in the Team Files in the offline-sessions directory: /offline-sessions/{task_id}/app-template/sly/css/app/widgets/image_pair_sequence/{image_name}
By using the ImagePairSequence widget, you can set the images, annotations and titles to display on the left and right sides of the widget. You can also set a batch of images, annotations and titles to display on the left and right sides of the widget. The widget also provides methods for clearing the widget.
All the images will be saved in the Team Files in the offline-sessions directory:
Create Button widgets for controlling ImagePairSequence
left_btn =Button(text="add 1 prediction to left")right_btn =Button(text="add 1 prediction to right")left_three_btn =Button(text="add 3 predictions to left")right_three_btn =Button(text="add 3 predictions to right")pair_btn =Button(text="add 1 pair")pairs_batch_btn =Button(text="add 3 pairs")clean_btn =Button(text="clean up")btn_container =Flexbox( [left_btn, right_btn, left_three_btn, right_three_btn, pair_btn, pairs_batch_btn, clean_btn])
Prepare images and annotations for ImagePairSequence widget
# get all image infos in datasetused_names =set()images_infos = api.image.get_list(dataset_id=dataset_id)images_infos =sorted(images_infos, key=lambdaimage_info: image_info.name)image_ids = [image_info.id for image_info in images_infos]urls = [img.full_storage_url for img in images_infos]paths = [ os.path.join(static_dir, sly.generate_free_name(used_names, image_info.name, with_ext=True))for image_info in images_infos]static_paths = [os.path.join("static", os.path.basename(path))for path in paths]api.image.download_paths(dataset_id, image_ids, paths)# get annotations for all imagesanns_json = api.annotation.download_json_batch(dataset_id=dataset_id, image_ids=image_ids)anns = [sly.Annotation.from_json(ann_json, project_meta)for ann_json in anns_json]
Initialize ImagePairSequence widget we will use in UI
image_pair_sequence =ImagePairSequence()
Create app layout
Prepare a layout for app using Card widget with the content parameter and place widget that we've just created in the Container widget.
text =Text()card =Card( title="Image Pair Sequence", content=Container([image_pair_sequence]),)layout =Container(widgets=[btn_container, card, text])
Create app using layout
Create an app object with layout parameter. We also pass the static_dir parameter to the app object so that the app can serve static files (optional).
left_urls_generator = (url for url in static_paths)right_urls_generator = (url for url in static_paths)left_anns_generator = (ann for ann in anns)right_anns_generator = (ann for ann in anns)left_num =0right_num =0defget_next_prediction(side):global left_num, right_num path, ann, title =None,None,None path_gen = left_urls_generator if side =="left"else right_urls_generator ann_gen = left_anns_generator if side =="left"else right_anns_generatortry: path =next(path_gen) ann =next(ann_gen)if side =="left": title =f"Predictions {left_num}" left_num +=1else: title =f"Predictions {right_num}" right_num +=1exceptStopIteration: sly.logger.info("No more predictions.") text.set(text="No more predictions.", status="info")finally:return path, ann, title@pair_btn.clickdefpair_btn_click_handler(): left =get_next_prediction("left") right =get_next_prediction("right")if left[0]isnotNoneand right[0]isnotNone: image_pair_sequence.append_pair(left=left, right=right)@pairs_batch_btn.clickdefpairs_batch_btn_click_handler(): lefts = [] rights = []for _ inrange(3): left =get_next_prediction("left") right =get_next_prediction("right")if left[0]isnotNoneand right[0]isnotNone: lefts.append(left) rights.append(right)iflen(lefts)>0andlen(rights)>0: image_pair_sequence.set_pairs_batch(lefts, rights)@left_btn.clickdefleft_btn_click_handler(): path, ann, title =get_next_prediction("left")if path isnotNone: image_pair_sequence.append_left(path, ann, title)@right_btn.clickdefright_btn_click_handler(): path, ann, title =get_next_prediction("right")if path isnotNone: image_pair_sequence.append_right(path, ann, title)@left_three_btn.clickdefleft_three_btn_click_handler(): data = []for _ inrange(3): left =get_next_prediction("left")if left[0]isnotNone: data.append(left)iflen(data)>0: paths, anns, titles =zip(*data) image_pair_sequence.extend_left(paths=paths, anns=anns, titles=titles)@right_three_btn.clickdefright_three_btn_click_handler(): data = []for _ inrange(3): right =get_next_prediction("right")if right[0]isnotNone: data.append(right)iflen(data)>0: paths, anns, titles =zip(*data) image_pair_sequence.extend_right(paths=paths, anns=anns, titles=titles)@clean_btn.clickdefclean_btn_click_handler(): image_pair_sequence.clean_up()