Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add export template #6

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
secret_debug.env
src/debug
debug
.idea
venv
.venv
supervisely
__pycache__
.vscode
11 changes: 3 additions & 8 deletions config.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
{
"name": "Export to Supervisely format",
"type": "app",
"categories": [
"images",
"export"
],
"categories": ["images", "export"],
"description": "images and JSON annotations",
"docker_image": "supervisely/base-py-sdk:6.68.109",
"docker_image": "supervisely/base-py-sdk:6.69.19",
"instance_version": "6.5.1",
"main_script": "src/main.py",
"modal_template": "src/modal.html",
Expand All @@ -20,9 +17,7 @@
"icon": "https://i.imgur.com/1hqGMyg.png",
"icon_background": "#FFFFFF",
"context_menu": {
"target": [
"images_project"
],
"target": ["images_project", "images_dataset"],
"context_root": "Download as"
},
"poster": "https://user-images.githubusercontent.com/106374579/186665737-ec3da9cc-193f-43ee-85db-a6f802b2dfe4.png"
Expand Down
19 changes: 19 additions & 0 deletions create_venv.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash

# learn more in documentation
# Official python docs: https://docs.python.org/3/library/venv.html
# Superviely developer portal: https://developer.supervise.ly/getting-started/installation#venv

if [ -d ".venv" ]; then
echo "VENV already exists, will be removed"
rm -rf .venv
fi

echo "VENV will be created" && \
python3 -m venv .venv && \
source .venv/bin/activate && \

echo "Install requirements..." && \
pip3 install -r requirements.txt && \
echo "Requirements have been successfully installed" && \
deactivate
20 changes: 0 additions & 20 deletions debug.env

This file was deleted.

12 changes: 12 additions & 0 deletions local.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
PYTHONUNBUFFERED=1

TASK_ID = 24870
context.teamId=439
context.workspaceId=658
modal.state.slyProjectId=14957
modal.state.download=all
modal.state.fixExtension=true

DEBUG_APP_DIR="/home/alex/alex_work/app_data/"
DEBUG_CACHE_DIR="/home/alex/alex_work/app_cache"
LOG_LEVEL="debug"
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
supervisely==6.68.109
supervisely==6.69.19
145 changes: 64 additions & 81 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@
import supervisely as sly
from supervisely.api.module_api import ApiField
from supervisely.io.fs import get_file_ext
from supervisely.app.v1.app_service import AppService
from distutils import util

api: sly.Api = sly.Api.from_env()
my_app: AppService = AppService()
from dotenv import load_dotenv

TEAM_ID = int(os.environ['context.teamId'])
WORKSPACE_ID = int(os.environ['context.workspaceId'])
PROJECT_ID = int(os.environ['modal.state.slyProjectId'])
task_id = int(os.environ["TASK_ID"])
mode = os.environ['modal.state.download']
replace_method = bool(util.strtobool(os.environ['modal.state.fixExtension']))

if sly.is_development():
load_dotenv("local.env")
load_dotenv(os.path.expanduser("~/supervisely.env"))


mode = os.environ["modal.state.download"]
replace_method = bool(util.strtobool(os.environ["modal.state.fixExtension"]))
batch_size = 10
STORAGE_DIR = sly.app.get_data_dir()


def ours_convert_json_info(self, info: dict, skip_missing=True):
Expand All @@ -31,94 +32,64 @@ def ours_convert_json_info(self, info: dict, skip_missing=True):
val = info[field_name]
field_values.append(val)
if field_name == ApiField.MIME:
temp_ext = val.split('/')[1]
temp_ext = val.split("/")[1]
field_values.append(temp_ext)
for idx, field_name in enumerate(self.info_sequence()):
if field_name == ApiField.NAME:
cur_ext = get_file_ext(field_values[idx]).replace(".", "").lower()
if not cur_ext:
field_values[idx] = "{}.{}".format(field_values[idx], temp_ext)
break
if temp_ext == 'jpeg' and cur_ext in ['jpg', 'jpeg', 'mpo']:
if temp_ext == "jpeg" and cur_ext in ["jpg", "jpeg", "mpo"]:
break
if temp_ext != cur_ext and cur_ext is not None:
pass
break
return self.InfoType(*field_values)


def init(data, state):
state['download'] = mode
state['fixExtension'] = replace_method


if replace_method:
sly.logger.debug('change SDK method')
sly.logger.debug("change SDK method")
sly.api.image_api.ImageApi._convert_json_info = ours_convert_json_info


@my_app.callback("download_as_sly")
@sly.timeit
def download_as_sly(api: sly.Api, task_id, context, state, app_logger):
project = api.project.get_info_by_id(PROJECT_ID)
datasets = api.dataset.get_list(project.id)
dataset_ids = [dataset.id for dataset in datasets]
if mode == 'all':
download_json_plus_images(api, project, dataset_ids)
else:
download_only_json(api, project, dataset_ids)

download_dir = os.path.join(my_app.data_dir, f'{project.id}_{project.name}')
full_archive_name = str(project.id) + '_' + project.name + '.tar'
result_archive = os.path.join(my_app.data_dir, full_archive_name)
sly.fs.archive_directory(download_dir, result_archive)
app_logger.info("Result directory is archived")
upload_progress = []
remote_archive_path = os.path.join(
sly.team_files.RECOMMENDED_EXPORT_PATH, "export-to-supervisely-format/{}_{}".format(task_id, full_archive_name))

def _print_progress(monitor, upload_progress):
if len(upload_progress) == 0:
upload_progress.append(sly.Progress(message="Upload {!r}".format(full_archive_name),
total_cnt=monitor.len,
ext_logger=app_logger,
is_size=True))
upload_progress[0].set_current_value(monitor.bytes_read)

file_info = api.file.upload(TEAM_ID, result_archive, remote_archive_path,
lambda m: _print_progress(m, upload_progress))
app_logger.info("Uploaded to Team-Files: {!r}".format(file_info.storage_path))
api.task.set_output_archive(task_id, file_info.id, full_archive_name, file_url=file_info.storage_path)
my_app.stop()


def download_json_plus_images(api, project, dataset_ids):
sly.logger.info('DOWNLOAD_PROJECT', extra={'title': project.name})
download_dir = os.path.join(my_app.data_dir, f'{project.id}_{project.name}')
sly.download_project(api, project.id, download_dir, dataset_ids=dataset_ids,
log_progress=True, batch_size=batch_size)
sly.logger.info('Project {!r} has been successfully downloaded.'.format(project.name))
sly.logger.info("DOWNLOAD_PROJECT", extra={"title": project.name})
download_dir = os.path.join(STORAGE_DIR, f"{project.id}_{project.name}")
sly.download_project(
api,
project.id,
download_dir,
dataset_ids=dataset_ids,
log_progress=True,
batch_size=batch_size,
)
sly.logger.info("Project {!r} has been successfully downloaded.".format(project.name))


def download_only_json(api, project, dataset_ids):
sly.logger.info('DOWNLOAD_PROJECT', extra={'title': project.name})
download_dir = os.path.join(my_app.data_dir, f'{project.id}_{project.name}')
sly.logger.info("DOWNLOAD_PROJECT", extra={"title": project.name})
download_dir = os.path.join(STORAGE_DIR, f"{project.id}_{project.name}")
sly.fs.mkdir(download_dir)
meta_json = api.project.get_meta(project.id)
sly.io.json.dump_json_file(meta_json, os.path.join(download_dir, 'meta.json'))
sly.io.json.dump_json_file(meta_json, os.path.join(download_dir, "meta.json"))

total_images = 0
dataset_info = (
[api.dataset.get_info_by_id(ds_id) for ds_id in dataset_ids]
if (dataset_ids is not None) else api.dataset.get_list(project.id))
if (dataset_ids is not None)
else api.dataset.get_list(project.id)
)

for dataset in dataset_info:
ann_dir = os.path.join(download_dir, dataset.name, 'ann')
ann_dir = os.path.join(download_dir, dataset.name, "ann")
sly.fs.mkdir(ann_dir)

images = api.image.get_list(dataset.id)
ds_progress = sly.Progress(
'Downloading annotations for: {!r}/{!r}'.format(project.name, dataset.name), total_cnt=len(images))
"Downloading annotations for: {!r}/{!r}".format(project.name, dataset.name),
total_cnt=len(images),
)
for batch in sly.batched(images, batch_size=10):
image_ids = [image_info.id for image_info in batch]
image_names = [image_info.name for image_info in batch]
Expand All @@ -127,29 +98,41 @@ def download_only_json(api, project, dataset_ids):
ann_infos = api.annotation.download_batch(dataset.id, image_ids)

for image_name, ann_info in zip(image_names, ann_infos):
sly.io.json.dump_json_file(ann_info.annotation, os.path.join(ann_dir, image_name + '.json'))
sly.io.json.dump_json_file(
ann_info.annotation, os.path.join(ann_dir, image_name + ".json")
)
ds_progress.iters_done_report(len(batch))
total_images += len(batch)

sly.logger.info('Project {!r} has been successfully downloaded'.format(project.name))
sly.logger.info('Total number of images: {!r}'.format(total_images))
sly.logger.info("Project {!r} has been successfully downloaded".format(project.name))
sly.logger.info("Total number of images: {!r}".format(total_images))


def main():
sly.logger.info(
"Script arguments",
extra={
"TEAM_ID": TEAM_ID,
"WORKSPACE_ID": WORKSPACE_ID,
"PROJECT_ID": PROJECT_ID
}
)
class MyExport(sly.app.Export):
def process(self, context: sly.app.Export.Context):

api = sly.Api.from_env()

project = api.project.get_info_by_id(id=context.project_id)
datasets = api.dataset.get_list(project.id)
if context.dataset_id is not None:
dataset_ids = [context.dataset_id]
else:
dataset_ids = [dataset.id for dataset in datasets]

if mode == "all":
download_json_plus_images(api, project, dataset_ids)
else:
download_only_json(api, project, dataset_ids)

download_dir = os.path.join(STORAGE_DIR, f"{project.id}_{project.name}")
full_archive_name = str(project.id) + "_" + project.name + ".tar"
result_archive = os.path.join(STORAGE_DIR, full_archive_name)
sly.fs.archive_directory(download_dir, result_archive)
sly.logger.info("Result directory is archived")

data = {}
state = {}
init(data, state)
my_app.run(initial_events=[{"command": "download_as_sly"}])
return result_archive


if __name__ == "__main__":
sly.main_wrapper("main", main)
app = MyExport()
app.run()