From 37abe12cd1aca6e584d3e17c668baaa62accee13 Mon Sep 17 00:00:00 2001 From: Giuseppe Ambrosio Date: Wed, 4 Dec 2024 16:48:15 +0100 Subject: [PATCH 01/13] fix(readme): pip install add quotes for optional deps syntax + readme formatting --- README.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 543053c..c583ed2 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,14 @@ | focoos_isaid_nano | Semantic Segmentation | - | Satellite Imagery, 15 classes | | focoos_isaid_medium | Semantic Segmentation | - | Satellite Imagery, 15 classes | - # Focoos SDK - ## Requirements + For **local inference**, ensure that you have CUDA 12 and cuDNN 9 installed, as they are required for onnxruntime version 1.20.1. To install cuDNN 9: + ```bash apt-get -y install cudnn9-cuda-12 ``` @@ -31,22 +31,23 @@ To perform inference using TensorRT, ensure you have TensorRT version 10.5 insta # Install Nvidia GPU: + ```bash -pip install .[gpu] +pip install '.[gpu]' ``` Nvidia GPU,TensorRT: + ```bash -pip install .[gpu,tensorrt] +pip install '.[gpu,tensorrt]' ``` CPU,COREML: + ```bash -pip install .[cpu] +pip install '.[cpu]' ``` - - ## 🤖 Cloud Inference ```python @@ -58,12 +59,13 @@ model = focoos.get_remote_model("focoos_object365") model.deploy() detections = model.infer("./image.jpg", threshold=0.4) ``` + ## 🤖 Cloud Inference with Gradio setup FOCOOS_API_KEY_GRADIO environment variable with your Focoos API key ```bash -pip install .[gradio] +pip install '.[gradio]' ``` ```bash @@ -71,6 +73,7 @@ python gradio/app.py ``` ## Local Inference + ```python from focoos import Focoos From a4f41e06b3edb8b32cbec819ffb27135392a9f9a Mon Sep 17 00:00:00 2001 From: Giuseppe Ambrosio Date: Thu, 5 Dec 2024 10:01:51 +0100 Subject: [PATCH 02/13] feat(logging): focoos_log_level env var (default info) --- focoos/config.py | 7 +++++++ focoos/focoos.py | 9 ++++----- focoos/local_model.py | 7 +++---- focoos/remote_model.py | 12 ------------ focoos/utils/logger.py | 10 ++++++---- focoos/utils/system.py | 11 ++++------- 6 files changed, 24 insertions(+), 32 deletions(-) diff --git a/focoos/config.py b/focoos/config.py index ef84e0b..b4665d4 100644 --- a/focoos/config.py +++ b/focoos/config.py @@ -1,12 +1,19 @@ +import typing from typing import Optional from pydantic_settings import BaseSettings from focoos.ports import PROD_API_URL, RuntimeTypes +LogLevel = typing.Literal["DEBUG", "INFO", "WARNING", "ERROR", "FATAL", "CRITICAL"] + class FocoosConfig(BaseSettings): focoos_api_key: Optional[str] = None + focoos_log_level: LogLevel = "INFO" default_host_url: str = PROD_API_URL runtime_type: RuntimeTypes = RuntimeTypes.ONNX_CUDA32 warmup_iter: int = 2 + + +FOCOOS_CONFIG = FocoosConfig() diff --git a/focoos/focoos.py b/focoos/focoos.py index d10c6e5..c06a61f 100644 --- a/focoos/focoos.py +++ b/focoos/focoos.py @@ -3,22 +3,21 @@ from tqdm import tqdm -from focoos.config import FocoosConfig +from focoos.config import FOCOOS_CONFIG from focoos.local_model import LocalModel -from focoos.ports import DatasetMetadata, ModelMetadata, ModelPreview, ModelStatus +from focoos.ports import DatasetMetadata, ModelMetadata, ModelPreview from focoos.remote_model import RemoteModel from focoos.utils.logger import setup_logging from focoos.utils.system import HttpClient logger = setup_logging() -config = FocoosConfig() class Focoos: def __init__( self, - api_key: str = config.focoos_api_key, # type: ignore - host_url: str = config.default_host_url, + api_key: str = FOCOOS_CONFIG.focoos_api_key, # type: ignore + host_url: str = FOCOOS_CONFIG.default_host_url, ): self.api_key = api_key if not self.api_key: diff --git a/focoos/local_model.py b/focoos/local_model.py index bfd3b69..4a106a9 100644 --- a/focoos/local_model.py +++ b/focoos/local_model.py @@ -7,7 +7,7 @@ from PIL import Image from supervision import BoxAnnotator, Detections, LabelAnnotator, MaskAnnotator -from focoos.config import FocoosConfig +from focoos.config import FOCOOS_CONFIG from focoos.ports import ( FocoosDetections, FocoosTask, @@ -24,14 +24,13 @@ ) logger = get_logger(__name__) -config = FocoosConfig() class LocalModel: def __init__( self, model_dir: Union[str, Path], - runtime_type: RuntimeTypes = config.runtime_type, + runtime_type: RuntimeTypes = FOCOOS_CONFIG.runtime_type, ): logger.debug(f"Runtime type: {runtime_type}, Loading model from {model_dir},") if not os.path.exists(model_dir): @@ -46,7 +45,7 @@ def __init__( runtime_type, str(os.path.join(model_dir, "model.onnx")), self.metadata, - config.warmup_iter, + FOCOOS_CONFIG.warmup_iter, ) def _read_metadata(self) -> ModelMetadata: diff --git a/focoos/remote_model.py b/focoos/remote_model.py index 45270ae..81fd04f 100644 --- a/focoos/remote_model.py +++ b/focoos/remote_model.py @@ -5,36 +5,24 @@ from typing import Optional, Tuple, Union import numpy as np -from PIL import Image from supervision import BoxAnnotator, Detections, LabelAnnotator, MaskAnnotator -from tqdm import tqdm -from focoos.config import FocoosConfig from focoos.ports import ( - DeploymentMode, FocoosDet, FocoosDetections, FocoosTask, Hyperparameters, - LatencyMetrics, ModelMetadata, ModelStatus, - OnnxEngineOpts, TrainInstance, ) -from focoos.runtime import ONNXRuntime from focoos.utils.logger import get_logger from focoos.utils.system import HttpClient from focoos.utils.vision import ( focoos_detections_to_supervision, image_loader, - image_preprocess, - scale_detections, - sv_to_focoos_detections, ) -config = FocoosConfig() - logger = get_logger() diff --git a/focoos/utils/logger.py b/focoos/utils/logger.py index 40eb6af..b177b8a 100644 --- a/focoos/utils/logger.py +++ b/focoos/utils/logger.py @@ -2,6 +2,8 @@ import logging.config from functools import cache +from focoos.config import FOCOOS_CONFIG, LogLevel + class ColoredFormatter(logging.Formatter): log_format = "[%(asctime)s][%(levelname)s][%(name)s]: %(message)s" @@ -43,7 +45,7 @@ def format(self, record): "default": { "class": "logging.StreamHandler", "formatter": "color", - "level": "DEBUG", + "level": FOCOOS_CONFIG.focoos_log_level, }, }, "root": { # Configura il logger di default (root) @@ -53,7 +55,7 @@ def format(self, record): "loggers": { "focoos": { "handlers": ["default"], - "level": "DEBUG", + "level": FOCOOS_CONFIG.focoos_log_level, "propagate": False, }, "matplotlib": {"level": "WARNING"}, @@ -63,10 +65,10 @@ def format(self, record): @cache -def get_logger(name="focoos", level=logging.DEBUG): +def get_logger(name="focoos", level: LogLevel = FOCOOS_CONFIG.focoos_log_level): logger = logging.getLogger(name) logger.setLevel(level) - return logging.getLogger(name) + return logger def setup_logging(): diff --git a/focoos/utils/system.py b/focoos/utils/system.py index b1182c7..f2cee98 100644 --- a/focoos/utils/system.py +++ b/focoos/utils/system.py @@ -2,18 +2,15 @@ import requests -from focoos.config import FocoosConfig - -config = FocoosConfig() +from focoos.config import FOCOOS_CONFIG class HttpClient: def __init__( - self, api_key: Optional[str] = None, host_url: str = config.default_host_url + self, api_key: str = FOCOOS_CONFIG.focoos_api_key, # type: ignore + host_url: str = FOCOOS_CONFIG.default_host_url ): - if not api_key and not config.focoos_api_key: - raise ValueError("API key is required") - self.api_key = api_key or config.focoos_api_key + self.api_key = api_key self.host_url = host_url self.default_headers = { From 61e31c7ce8e39424d755b45447d5ef04b732c130 Mon Sep 17 00:00:00 2001 From: Giuseppe Ambrosio Date: Thu, 5 Dec 2024 10:02:53 +0100 Subject: [PATCH 03/13] feat(.gitignore): .envrc --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4e0b893..97d7466 100644 --- a/.gitignore +++ b/.gitignore @@ -83,6 +83,7 @@ profile_default/ ipython_config.py .env +.envrc .focoos .data notebooks/.data From e3c0d7c86a12c27a33111b99dda97916c6a75cc7 Mon Sep 17 00:00:00 2001 From: Giuseppe Ambrosio Date: Thu, 5 Dec 2024 10:33:21 +0100 Subject: [PATCH 04/13] feat(linting): .envrc --- focoos/remote_model.py | 5 +---- focoos/utils/system.py | 5 +++-- notebooks/playground.ipynb | 6 ++---- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/focoos/remote_model.py b/focoos/remote_model.py index 81fd04f..8c0f17a 100644 --- a/focoos/remote_model.py +++ b/focoos/remote_model.py @@ -18,10 +18,7 @@ ) from focoos.utils.logger import get_logger from focoos.utils.system import HttpClient -from focoos.utils.vision import ( - focoos_detections_to_supervision, - image_loader, -) +from focoos.utils.vision import focoos_detections_to_supervision, image_loader logger = get_logger() diff --git a/focoos/utils/system.py b/focoos/utils/system.py index f2cee98..027e765 100644 --- a/focoos/utils/system.py +++ b/focoos/utils/system.py @@ -7,8 +7,9 @@ class HttpClient: def __init__( - self, api_key: str = FOCOOS_CONFIG.focoos_api_key, # type: ignore - host_url: str = FOCOOS_CONFIG.default_host_url + self, + api_key: str = FOCOOS_CONFIG.focoos_api_key, # type: ignore + host_url: str = FOCOOS_CONFIG.default_host_url, ): self.api_key = api_key self.host_url = host_url diff --git a/notebooks/playground.ipynb b/notebooks/playground.ipynb index 5a2a41e..9e3150d 100644 --- a/notebooks/playground.ipynb +++ b/notebooks/playground.ipynb @@ -89,14 +89,12 @@ "\n", "# os.environ[\"RUNTIME_TYPE\"] = \"onnx_trt16\"\n", "from focoos import Focoos, DEV_API_URL\n", - "from focoos.config import FocoosConfig\n", + "from focoos.config import FOCOOS_CONFIG\n", "import os\n", "from pprint import pprint\n", "from supervision import plot_image\n", "\n", - "\n", - "config = FocoosConfig()\n", - "print(config)\n", + "print(FOCOOS_CONFIG)\n", "focoos = Focoos(\n", " api_key=os.getenv(\"FOCOOS_API_KEY\"),\n", " host_url=DEV_API_URL,\n", From e14e4988568c4d6d3281cd4deb881fca966c039a Mon Sep 17 00:00:00 2001 From: Giuseppe Ambrosio Date: Thu, 5 Dec 2024 11:03:29 +0100 Subject: [PATCH 05/13] fix(http-client): api_key None default --- focoos/utils/system.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/focoos/utils/system.py b/focoos/utils/system.py index 027e765..bcd7871 100644 --- a/focoos/utils/system.py +++ b/focoos/utils/system.py @@ -8,10 +8,12 @@ class HttpClient: def __init__( self, - api_key: str = FOCOOS_CONFIG.focoos_api_key, # type: ignore + api_key: Optional[str] = None, host_url: str = FOCOOS_CONFIG.default_host_url, ): - self.api_key = api_key + if not api_key and not FOCOOS_CONFIG.focoos_api_key: + raise ValueError("API key is required") + self.api_key = api_key or FOCOOS_CONFIG.focoos_api_key self.host_url = host_url self.default_headers = { From 74e0a3a2d29659834ad45c90b0fcfb1f66682bbf Mon Sep 17 00:00:00 2001 From: Giuseppe Ambrosio Date: Thu, 5 Dec 2024 11:34:59 +0100 Subject: [PATCH 06/13] refactor(http-client): api_key, host_url required params --- focoos/utils/system.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/focoos/utils/system.py b/focoos/utils/system.py index bcd7871..7f2e35f 100644 --- a/focoos/utils/system.py +++ b/focoos/utils/system.py @@ -8,12 +8,10 @@ class HttpClient: def __init__( self, - api_key: Optional[str] = None, - host_url: str = FOCOOS_CONFIG.default_host_url, + api_key: str, + host_url: str, ): - if not api_key and not FOCOOS_CONFIG.focoos_api_key: - raise ValueError("API key is required") - self.api_key = api_key or FOCOOS_CONFIG.focoos_api_key + self.api_key = api_key self.host_url = host_url self.default_headers = { From c584cb46e30fbc76e9f8ec30f9774cbc79dbf11b Mon Sep 17 00:00:00 2001 From: Giuseppe Ambrosio Date: Thu, 5 Dec 2024 14:20:07 +0100 Subject: [PATCH 07/13] feat(logging): default log level DEBUG --- focoos/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/focoos/config.py b/focoos/config.py index b4665d4..7549626 100644 --- a/focoos/config.py +++ b/focoos/config.py @@ -10,7 +10,7 @@ class FocoosConfig(BaseSettings): focoos_api_key: Optional[str] = None - focoos_log_level: LogLevel = "INFO" + focoos_log_level: LogLevel = "DEBUG" default_host_url: str = PROD_API_URL runtime_type: RuntimeTypes = RuntimeTypes.ONNX_CUDA32 warmup_iter: int = 2 From 78092537203d42cb6a45c78d445020d556a8e9d2 Mon Sep 17 00:00:00 2001 From: Giuseppe Ambrosio Date: Thu, 5 Dec 2024 14:23:02 +0100 Subject: [PATCH 08/13] feat(local-model): runtime_type default arg Focoos --- focoos/focoos.py | 9 ++++----- focoos/ports.py | 2 +- focoos/runtime.py | 15 +++++++++++++-- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/focoos/focoos.py b/focoos/focoos.py index c06a61f..d63df2b 100644 --- a/focoos/focoos.py +++ b/focoos/focoos.py @@ -5,7 +5,7 @@ from focoos.config import FOCOOS_CONFIG from focoos.local_model import LocalModel -from focoos.ports import DatasetMetadata, ModelMetadata, ModelPreview +from focoos.ports import DatasetMetadata, ModelMetadata, ModelPreview, RuntimeTypes from focoos.remote_model import RemoteModel from focoos.utils.logger import setup_logging from focoos.utils.system import HttpClient @@ -68,13 +68,12 @@ def list_focoos_models(self) -> list[ModelPreview]: def get_local_model( self, model_ref: str, + runtime_type: RuntimeTypes = FOCOOS_CONFIG.runtime_type, ) -> LocalModel: model_dir = os.path.join(self.cache_dir, model_ref) - if os.path.exists(os.path.join(model_dir, "model.onnx")): - return LocalModel(model_dir) - else: + if not os.path.exists(os.path.join(model_dir, "model.onnx")): self._download_model(model_ref) - return LocalModel(model_dir) + return LocalModel(model_dir, runtime_type) def get_remote_model(self, model_ref: str) -> RemoteModel: return RemoteModel(model_ref, self.http_client) diff --git a/focoos/ports.py b/focoos/ports.py index 9c05ad8..0d877e9 100644 --- a/focoos/ports.py +++ b/focoos/ports.py @@ -237,4 +237,4 @@ class RuntimeTypes(str, Enum): ONNX_TRT32 = "onnx_trt32" ONNX_TRT16 = "onnx_trt16" ONNX_CPU = "onnx_cpu" - COREML = "coreml" + ONNX_COREML = "onnx_coreml" diff --git a/focoos/runtime.py b/focoos/runtime.py index 27d3983..8b03cbf 100644 --- a/focoos/runtime.py +++ b/focoos/runtime.py @@ -156,11 +156,22 @@ def __init__( binding = None binding = None # TODO: remove this + if opts.cuda and "CUDAExecutionProvider" not in providers: + self.logger.warning("CUDA ExecutionProvider not found.") + if opts.trt and "TensorrtExecutionProvider" not in providers: + self.logger.warning("Tensorrt ExecutionProvider not found.") + if opts.vino and "OpenVINOExecutionProvider" not in providers: + self.logger.warning("OpenVINO ExecutionProvider not found.") + if opts.coreml and "CoreMLExecutionProvider" not in providers: + self.logger.warning("CoreML ExecutionProvider not found.") + providers.append("CPUExecutionProvider") self.dtype = dtype self.binding = binding self.ort_sess = ort.InferenceSession(model_path, options, providers=providers) - self.logger.info(f"[onnxruntime] Providers:{self.ort_sess.get_providers()}") + self.logger.info( + f"[onnxruntime] Active providers:{self.ort_sess.get_providers()}" + ) if self.ort_sess.get_inputs()[0].type == "tensor(uint8)": self.dtype = np.uint8 else: @@ -297,7 +308,7 @@ def get_runtime( ) elif runtime_type == RuntimeTypes.ONNX_CPU: opts = OnnxEngineOpts(cuda=False, verbose=False, warmup_iter=warmup_iter) - elif runtime_type == RuntimeTypes.COREML: + elif runtime_type == RuntimeTypes.ONNX_COREML: opts = OnnxEngineOpts( cuda=False, verbose=False, coreml=True, warmup_iter=warmup_iter ) From 18e4729de66db2a38fbc67c5d66061ad906a3385 Mon Sep 17 00:00:00 2001 From: Giuseppe Ambrosio Date: Thu, 5 Dec 2024 14:25:16 +0100 Subject: [PATCH 09/13] refactor(playground): unused import --- notebooks/playground.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebooks/playground.ipynb b/notebooks/playground.ipynb index 9e3150d..0d54d04 100644 --- a/notebooks/playground.ipynb +++ b/notebooks/playground.ipynb @@ -269,7 +269,7 @@ "metadata": {}, "outputs": [], "source": [ - "from focoos import Focoos, DEV_API_URL, DeploymentMode\n", + "from focoos import Focoos, DEV_API_URL\n", "import os\n", "from pprint import pprint\n", "from supervision import plot_image\n", From 6348473cb32c28e3f70f3c1bf36641f1974270b3 Mon Sep 17 00:00:00 2001 From: Giuseppe Ambrosio Date: Thu, 5 Dec 2024 14:47:57 +0100 Subject: [PATCH 10/13] feat(gh-actions): test actions uv --- .github/workflows/test.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a05427c..2e84ef9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: env: DOCKER_BUILDKIT: 1 - AWS_REGION: eu-west-1 + UV_SYSTEM_PYTHON: 1 jobs: Run-test: runs-on: ubuntu-22.04 @@ -22,9 +22,12 @@ jobs: uses: actions/setup-python@v4 with: python-version: "3.12" - cache: "pip" + - name: Install the latest version of uv and set the python version to 3.12 + uses: astral-sh/setup-uv@v4 + with: + python-version: "3.12" - name: Install dependencies - run: pip install .[cpu,dev] + run: uv pip install .[cpu,dev] - name: Run test run: make test - name: Pytest coverage comment From 50d922e43c59ba1221701dade7cb072e00b5ef42 Mon Sep 17 00:00:00 2001 From: Giuseppe Ambrosio Date: Thu, 5 Dec 2024 15:24:10 +0100 Subject: [PATCH 11/13] feat(gh-actions): test action with python version matrix --- .github/workflows/test.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2e84ef9..f43f687 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,6 +13,9 @@ jobs: contents: read issues: write pull-requests: write + strategy: + matrix: + python-version: ["3.10", "3.11","3.12"] steps: - name: Checkout Repository uses: actions/checkout@v4 @@ -21,11 +24,11 @@ jobs: - name: Setup Python uses: actions/setup-python@v4 with: - python-version: "3.12" + python-version: ${{ matrix.python-version }} - name: Install the latest version of uv and set the python version to 3.12 uses: astral-sh/setup-uv@v4 with: - python-version: "3.12" + python-version: ${{ matrix.python-version }} - name: Install dependencies run: uv pip install .[cpu,dev] - name: Run test From 64e8389958436bf6dadf36d04456b2de4421f700 Mon Sep 17 00:00:00 2001 From: Giuseppe Ambrosio Date: Thu, 5 Dec 2024 15:37:19 +0100 Subject: [PATCH 12/13] release: 0.2.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 72d6d70..638090b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ include = ["focoos**"] [project] name = "focoos" -version = "0.1.3" +version = "0.2.0" description = "Focoos SDK" readme = "README.md" requires-python = ">=3.10" From f4c276d970defc4142b8dcc188e36661867a48a3 Mon Sep 17 00:00:00 2001 From: Giuseppe Ambrosio Date: Thu, 5 Dec 2024 15:54:53 +0100 Subject: [PATCH 13/13] fix(logging): runtime_type with onnx available providers --- focoos/runtime.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/focoos/runtime.py b/focoos/runtime.py index 8b03cbf..7f9ed4f 100644 --- a/focoos/runtime.py +++ b/focoos/runtime.py @@ -100,6 +100,14 @@ def __init__( options.enable_profiling = opts.verbose # options.intra_op_num_threads = 1 available_providers = ort.get_available_providers() + if opts.cuda and "CUDAExecutionProvider" not in available_providers: + self.logger.warning("CUDA ExecutionProvider not found.") + if opts.trt and "TensorrtExecutionProvider" not in available_providers: + self.logger.warning("Tensorrt ExecutionProvider not found.") + if opts.vino and "OpenVINOExecutionProvider" not in available_providers: + self.logger.warning("OpenVINO ExecutionProvider not found.") + if opts.coreml and "CoreMLExecutionProvider" not in available_providers: + self.logger.warning("CoreML ExecutionProvider not found.") # Set providers providers = [] dtype = np.float32 @@ -156,15 +164,6 @@ def __init__( binding = None binding = None # TODO: remove this - if opts.cuda and "CUDAExecutionProvider" not in providers: - self.logger.warning("CUDA ExecutionProvider not found.") - if opts.trt and "TensorrtExecutionProvider" not in providers: - self.logger.warning("Tensorrt ExecutionProvider not found.") - if opts.vino and "OpenVINOExecutionProvider" not in providers: - self.logger.warning("OpenVINO ExecutionProvider not found.") - if opts.coreml and "CoreMLExecutionProvider" not in providers: - self.logger.warning("CoreML ExecutionProvider not found.") - providers.append("CPUExecutionProvider") self.dtype = dtype self.binding = binding