Modify annotations

This example show how to read Supervisely project, add new figure (rectangle with fixed position) to each annotation and then save results as a new Supervisely project.

Hint

Look for up-to-date sources on GitHub.

# coding: utf-8

from copy import deepcopy

from supervisely_lib import logger
import supervisely_lib as sly


# In this example we will read Supervisely project from disk, add rectangle of new class to each image annotation
# and store result as a new Supervisely project.


def main():
    logger.info('Hello world.')

    # It isn't necessary, but let's suppose that our data will be stored as for Supervisely task:
    # input in '/sly_task_data/data` and results in '/sly_task_data/results'.
    # So TaskPaths provides the paths.
    task_paths = sly.TaskPaths()

    in_pr_dir = task_paths.project_dir  # the paths includes project name

    in_pr_meta = sly.ProjectMeta.from_dir(in_pr_dir)
    # Now we've read meta of input project.
    logger.info('Input project meta: {} class(es).'.format(len(in_pr_meta.classes)))

    in_pr_fs = sly.ProjectFS.from_disk(*sly.ProjectFS.split_dir_project(in_pr_dir))
    # Now we've read project structure.
    logger.info('Input project: "{}" contains {} dataset(s) and {} image(s).'.format(
        in_pr_fs.pr_structure.name,
        len(in_pr_fs.pr_structure.datasets),
        in_pr_fs.image_cnt
    ))

    # It's convenient to create output project structure and store source file paths in ia_data.
    out_pr_structure = sly.ProjectStructure('my_new_project')  # rename project... just for fun
    for item_descr in in_pr_fs:  # iterate over input project
        new_ia_data = {
            'src_ann_path': item_descr.ann_path,
            'src_img_path': item_descr.img_path,
            **item_descr.ia_data  # contains 'image_ext' which is required to write images
        }
        out_pr_structure.add_item(item_descr.ds_name, item_descr.image_name, new_ia_data)
    # ProjectFS will provide out file paths
    out_pr_fs = sly.ProjectFS(task_paths.results_dir, out_pr_structure)

    # We will add the rectangle to each annotation.
    new_class_title = 'new-region'
    rect_to_add = sly.Rect(left=20, top=20, right=50, bottom=100)

    # Ok, start processing.
    out_pr_fs.make_dirs()  # create all directories required for writing
    for item_descr in out_pr_fs:  # iterate over output project
        logger.info('Processing sample',
                    extra={'dataset': item_descr.ds_name, 'image_name': item_descr.image_name})

        # Copy image unchanged.
        sly.copy_file(item_descr.ia_data['src_img_path'], item_descr.img_path)

        # Read annotation.
        ann_packed = sly.json_load(item_descr.ia_data['src_ann_path'])
        ann = sly.Annotation.from_packed(ann_packed, in_pr_meta)

        # Add new figure to the annotation.
        # Method to construct figures returns iterable of new figures.
        # (e.g., line cropped with image bounds may produce some lines), but here we'll get not more than one figure
        # ...or no figures if image is less than 20x20.
        new_figures = sly.FigureRectangle.from_rect(new_class_title, ann.image_size_wh, rect_to_add)
        ann['objects'].extend(new_figures)

        # Save annotation.
        sly.json_dump(ann.pack(), item_descr.ann_path)

    # OK, and don't forget to create and save output project meta.
    # We'll save given data and add new class with shape "rectangle".
    out_pr_meta = deepcopy(in_pr_meta)
    out_pr_meta.classes.add({'title': new_class_title, 'shape': 'rectangle', 'color': '#FFFF00'})
    # Then store the meta.
    out_pr_meta.to_dir(out_pr_fs.project_path)

    logger.info('Done.')


if __name__ == '__main__':
    main()