diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e502787 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Current File", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "justMyCode": true, + "env": { + "PYTHONPATH": "${workspaceFolder}:${PYTHONPATH}" + } + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9e31563 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,27 @@ +{ + "python.defaultInterpreterPath": ".venv/bin/python", + "files.exclude": { + "**/__pycache__": true, + ".venv": true + }, + "editor.formatOnSave": true, + "editor.formatOnPaste": true, + "editor.formatOnType": true, + "black-formatter.args": ["--line-length", "100"], + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter", + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + } + }, + "isort.args": ["--profile", "black"], + "debug.inlineValues": "off", + "python.analysis.typeCheckingMode": "off", + "python.analysis.autoImportCompletions": false +} diff --git a/debug.env b/debug.env index 1b1ba47..bc809e9 100644 --- a/debug.env +++ b/debug.env @@ -2,19 +2,13 @@ PYTHONUNBUFFERED=1 TASK_ID=16200 -context.teamId=314 -context.workspaceId=459 -modal.state.slyProjectId=11162 -modal.state.slyProjectId=11237 -modal.state.slyProjectId=11106 +context.teamId=567 +context.workspaceId=1072 +modal.state.slyProjectId=44098 -modal.state.targetFps=4 +modal.state.targetFps=25 modal.state.resultProjectName="" DEBUG_APP_DIR="debug_data/sly-app" DEBUG_TEMPORARY_APP_DIR="debug_data/sly-app" LOG_LEVEL="debug" - -SERVER_ADDRESS="put your value here" -API_TOKEN="put your value here" -AGENT_TOKEN="put your value here" diff --git a/src/debug_utils.py b/src/debug_utils.py index 6844416..c2f8cb5 100644 --- a/src/debug_utils.py +++ b/src/debug_utils.py @@ -5,8 +5,8 @@ def load_envs_from_files(): app_dir = os.getcwd() - debug_env_path = os.path.join(app_dir, 'debug.env') - secret_debug_env_path = os.path.join(app_dir, 'secret_debug.env') + debug_env_path = os.path.join(app_dir, "debug.env") + secret_debug_env_path = "~/supervisely.env" load_dotenv(debug_env_path) load_dotenv(secret_debug_env_path, override=True) diff --git a/src/globals.py b/src/globals.py index a6c720f..eeaf7b2 100644 --- a/src/globals.py +++ b/src/globals.py @@ -7,32 +7,40 @@ from supervisely.app.fastapi import create from supervisely.app.content import get_data_dir from distutils.util import strtobool +from dotenv import load_dotenv +if sly.is_development(): + load_dotenv("debug.env") + load_dotenv("~supervisely.env") app_root_directory = os.getcwd() -sly.logger.info(f'App root directory: {app_root_directory!r}') +sly.logger.info(f"App root directory: {app_root_directory!r}") sys.path.append(app_root_directory) data_directory = get_data_dir() -temp_data_directory = os.getenv('DEBUG_TEMPORARY_APP_DIR', '/tmp/sly-app') # to be removed after task execution -sly.logger.info(f'App data directory: {data_directory!r} Temporary data directory: {temp_data_directory!r}') +temp_data_directory = os.getenv( + "DEBUG_TEMPORARY_APP_DIR", "/tmp/sly-app" +) # to be removed after task execution +sly.logger.info( + f"App data directory: {data_directory!r} Temporary data directory: {temp_data_directory!r}" +) app = FastAPI() sly_app = create() -app.mount('/sly', sly_app) +app.mount("/sly", sly_app) api = sly.Api.from_env() -TASK_ID = int(os.environ['TASK_ID']) -TEAM_ID = int(os.environ['context.teamId']) -WORKSPACE_ID = int(os.environ['context.workspaceId']) -PROJECT_ID = int(os.environ['modal.state.slyProjectId']) -DATASET_ID = os.environ.get('modal.state.slyDatasetId', None) +TASK_ID = int(os.environ["TASK_ID"]) +TEAM_ID = int(os.environ["context.teamId"]) +WORKSPACE_ID = int(os.environ["context.workspaceId"]) +PROJECT_ID = int(os.environ["modal.state.slyProjectId"]) +DATASET_ID = os.environ.get("modal.state.slyDatasetId", None) if DATASET_ID is not None: DATASET_ID = int(DATASET_ID) -CHANGE_RESOLUTION = bool(strtobool(os.environ.get('modal.state.changeResolution', 'false'))) -TARGET_HEIGHT = int(os.environ.get('modal.state.targetResolutionHeight', 1280)) -TARGET_WIDTH = int(os.environ.get('modal.state.targetResolutionWidth', 720)) +CHANGE_RESOLUTION = bool(strtobool(os.environ.get("modal.state.changeResolution", "false"))) +TARGET_HEIGHT = int(os.environ.get("modal.state.targetResolutionHeight", 1280)) +TARGET_WIDTH = int(os.environ.get("modal.state.targetResolutionWidth", 720)) target_resolution = (TARGET_WIDTH, TARGET_HEIGHT) -TARGET_FPS = float(os.environ['modal.state.targetFps']) -RES_PROJECT_NAME = os.getenv('modal.state.resultProjectName', None) +TARGET_FPS = float(os.environ["modal.state.targetFps"]) +RES_PROJECT_NAME = os.getenv("modal.state.resultProjectName", None) diff --git a/src/main.py b/src/main.py index e1f56ee..e4d0923 100644 --- a/src/main.py +++ b/src/main.py @@ -1,12 +1,15 @@ import os import sys -if '--DebugImportEnvsFromFiles' in sys.argv: +if "--DebugImportEnvsFromFiles" in sys.argv: import debug_utils + debug_utils.load_envs_from_files() import supervisely as sly from supervisely.video_annotation.key_id_map import KeyIdMap +from supervisely.api.module_api import ApiField +from supervisely.api.video.video_api import VideoInfo import globals as g from functions import FpsVideoInfo, convert_video @@ -14,51 +17,75 @@ @sly.timeit def change_framerate(api: sly.Api, target_fps, result_project_name): - + if g.DATASET_ID: datasets = [api.dataset.get_info_by_id(g.DATASET_ID)] - sly.logger.info(f'Processing single dataset ID {g.DATASET_ID} in project ID {g.PROJECT_ID}') + sly.logger.info(f"Processing single dataset ID {g.DATASET_ID} in project ID {g.PROJECT_ID}") else: datasets = api.dataset.get_list(g.PROJECT_ID) - sly.logger.info(f'Processing all datasets in project ID {g.PROJECT_ID}') + sly.logger.info(f"Processing all datasets in project ID {g.PROJECT_ID}") if not result_project_name: project_info = api.project.get_info_by_id(g.PROJECT_ID) - result_project_name = f'{project_info.name} {target_fps:.6g} FPS' + result_project_name = f"{project_info.name} {target_fps:.6g} FPS" - src_dir, res_dir = (os.path.join(g.temp_data_directory, n) for n in ('source', 'result')) + src_dir, res_dir = (os.path.join(g.temp_data_directory, n) for n in ("source", "result")) for d in (src_dir, res_dir): sly.fs.mkdir(d, remove_content_if_exists=True) meta_json = api.project.get_meta(g.PROJECT_ID) meta = sly.ProjectMeta.from_json(meta_json) - res_project = api.project.create(g.WORKSPACE_ID, result_project_name, - type=sly.ProjectType.VIDEOS, change_name_if_conflict=True) + res_project = api.project.create( + g.WORKSPACE_ID, + result_project_name, + type=sly.ProjectType.VIDEOS, + change_name_if_conflict=True, + ) api.project.update_meta(res_project.id, meta_json) dummy_map = KeyIdMap() - progress = sly.Progress('Video recoding', sum(ds.items_count for ds in datasets)) + progress = sly.Progress("Video recoding", sum(ds.items_count for ds in datasets)) for dataset in datasets: res_dataset = api.dataset.create(res_project.id, dataset.name, change_name_if_conflict=True) videos = api.video.get_list(dataset.id) for video_info in videos: - + if video_info.frames_to_timecodes is None and video_info.link: + video_info = api.video.get_list( + dataset.id, + filters=[ + { + ApiField.FIELD: ApiField.ID, + ApiField.OPERATOR: "=", + ApiField.VALUE: video_info.id, + } + ], + force_metadata_for_links=True, + )[0] + if video_info.frames_to_timecodes is None: + sly.logger.warning( + f"Video ID {video_info.id}; NAME '{video_info.name}' has no frames to timecodes info. Skipping." + ) + continue ann_info = api.video.annotation.download(video_info.id) ann = sly.VideoAnnotation.from_json(ann_info, meta, dummy_map) if ann.frames.figures or ann.objects: - sly.logger.warn(f'Annotation data from video {video_info.name!r} will be discarded.') + sly.logger.warning( + f"Annotation data from video {video_info.name!r} will be discarded." + ) fps_info = FpsVideoInfo(video_info.frames_to_timecodes) expected_frame_cnt = fps_info.expect_frames_cnt(target_fps) - sly.logger.info(f'Dataset {dataset.name!r} Video {video_info.name!r} Source: {fps_info.fps:.5} FPS, ' - f'{fps_info.frames_cnt} frames. Expect: {expected_frame_cnt} frames') + sly.logger.info( + f"Dataset {dataset.name!r} Video {video_info.name!r} Source: {fps_info.fps:.5} FPS, " + f"{fps_info.frames_cnt} frames. Expect: {expected_frame_cnt} frames" + ) if expected_frame_cnt < 2: - raise ValueError('Low FPS value', {'dataset': dataset.id, 'video': video_info.id}) + raise ValueError("Low FPS value", {"dataset": dataset.id, "video": video_info.id}) if fps_info.fps_equals(target_fps): - sly.logger.debug(f'Preserving existing frame rate for video {video_info.name!r}') + sly.logger.debug(f"Preserving existing frame rate for video {video_info.name!r}") api.video.upload_id(res_dataset.id, video_info.name, video_info.id) # api.video.upload_hash(res_dataset.id, video_info.name, video_info.hash) else: @@ -68,25 +95,25 @@ def change_framerate(api: sly.Api, target_fps, result_project_name): in_fpath, out_fpath = [os.path.join(d, video_info.name) for d in curr_dirs] api.video.download_path(video_info.id, in_fpath) - sly.logger.debug(f'Downloaded video to {in_fpath!r}') + sly.logger.debug(f"Downloaded video to {in_fpath!r}") convert_video(target_fps, in_fpath, out_fpath, g.target_resolution) - sly.logger.debug(f'Converted video to {out_fpath!r}') + sly.logger.debug(f"Converted video to {out_fpath!r}") api.video.upload_paths(res_dataset.id, (video_info.name,), (out_fpath,)) - sly.logger.debug('Uploaded video') + sly.logger.debug("Uploaded video") progress.iter_done_report() - sly.logger.debug('Finished change_framerate') + sly.logger.debug("Finished change_framerate") -if __name__ == '__main__': +if __name__ == "__main__": sly.logger.info( - 'Script arguments', + "Script arguments", extra={ - 'context.teamId': g.TEAM_ID, - 'context.workspaceId': g.WORKSPACE_ID, - 'modal.state.slyProjectId': g.PROJECT_ID, - 'modal.state.targetFps': g.TARGET_FPS, - 'modal.state.resultProjectName': g.RES_PROJECT_NAME + "context.teamId": g.TEAM_ID, + "context.workspaceId": g.WORKSPACE_ID, + "modal.state.slyProjectId": g.PROJECT_ID, + "modal.state.targetFps": g.TARGET_FPS, + "modal.state.resultProjectName": g.RES_PROJECT_NAME, }, ) @@ -95,4 +122,4 @@ def change_framerate(api: sly.Api, target_fps, result_project_name): try: sly.app.fastapi.shutdown() except KeyboardInterrupt: - sly.logger.info('Application shutdown successfully') + sly.logger.info("Application shutdown successfully")