From 3cf6c75f6df75a6328ee4407fda4832ecd63f8eb Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Thu, 6 Mar 2025 15:48:19 -0300 Subject: [PATCH 1/8] adds support for yolov12 model upload --- roboflow/util/model_processor.py | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/roboflow/util/model_processor.py b/roboflow/util/model_processor.py index 2e2a6173..f2673d42 100644 --- a/roboflow/util/model_processor.py +++ b/roboflow/util/model_processor.py @@ -10,25 +10,29 @@ def process(model_type: str, model_path: str, filename: str) -> str: + if model_type.startswith("yolo11"): + model_type = model_type.replace("yolo11", "yolov11") + + if model_type.startswith("yolo12"): + model_type = model_type.replace("yolo12", "yolov12") + processor = _get_processor_function(model_type) return processor(model_type, model_path, filename) def _get_processor_function(model_type: str) -> Callable: - if model_type.startswith("yolo11"): - model_type = model_type.replace("yolo11", "yolov11") - supported_models = [ "yolov5", "yolov7-seg", "yolov8", "yolov9", + "yolov10", + "yolov11", + "yolov12", "yolonas", "paligemma", "paligemma2", - "yolov10", "florence-2", - "yolov11", ] if not any(supported_model in model_type for supported_model in supported_models): @@ -109,6 +113,18 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: print_warn_for_wrong_dependencies_versions([("ultralytics", ">=", "8.3.0")], ask_to_continue=True) + elif "yolov12" in model_type: + try: + import torch + import ultralytics + except ImportError: + raise RuntimeError( + "The ultralytics python package is required to deploy yolov12" + " models. Please install it with `pip install ultralytics`" + ) + + print_warn_for_wrong_dependencies_versions([("ultralytics", ">=", "8.3.78")], ask_to_continue=True) + model = torch.load(os.path.join(model_path, filename)) if isinstance(model["model"].names, list): @@ -120,9 +136,9 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: class_names.sort(key=lambda x: x[0]) class_names = [x[1] for x in class_names] - if "yolov8" in model_type or "yolov10" in model_type or "yolov11" in model_type: + if "yolov8" in model_type or "yolov10" in model_type or "yolov11" in model_type or "yolov12" in model_type: # try except for backwards compatibility with older versions of ultralytics - if "-cls" in model_type or model_type.startswith("yolov10") or model_type.startswith("yolov11"): + if "-cls" in model_type or model_type.startswith("yolov10") or model_type.startswith("yolov11") or model_type.startswith("yolov12"): nc = model["model"].yaml["nc"] args = model["train_args"] else: From 7adc7d79e9648b3590d9cbc6e45f4011f6e10c4e Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Thu, 6 Mar 2025 15:48:32 -0300 Subject: [PATCH 2/8] bump version --- roboflow/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roboflow/__init__.py b/roboflow/__init__.py index 4b6d7476..851a5406 100644 --- a/roboflow/__init__.py +++ b/roboflow/__init__.py @@ -15,7 +15,7 @@ from roboflow.models import CLIPModel, GazeModel # noqa: F401 from roboflow.util.general import write_line -__version__ = "1.1.54" +__version__ = "1.1.55" def check_key(api_key, model, notebook, num_retries=0): From 1c9c7759a889b91b7f526a1181d4e1f6ab03c4a9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 6 Mar 2025 18:50:05 +0000 Subject: [PATCH 3/8] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto=20?= =?UTF-8?q?format=20pre-commit=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- roboflow/util/model_processor.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/roboflow/util/model_processor.py b/roboflow/util/model_processor.py index f2673d42..a99488a1 100644 --- a/roboflow/util/model_processor.py +++ b/roboflow/util/model_processor.py @@ -138,7 +138,12 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: if "yolov8" in model_type or "yolov10" in model_type or "yolov11" in model_type or "yolov12" in model_type: # try except for backwards compatibility with older versions of ultralytics - if "-cls" in model_type or model_type.startswith("yolov10") or model_type.startswith("yolov11") or model_type.startswith("yolov12"): + if ( + "-cls" in model_type + or model_type.startswith("yolov10") + or model_type.startswith("yolov11") + or model_type.startswith("yolov12") + ): nc = model["model"].yaml["nc"] args = model["train_args"] else: From 5c08cb78db7090d06ed20880d7debf4c67edf89d Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Thu, 6 Mar 2025 19:50:40 -0300 Subject: [PATCH 4/8] normalize yolo model name --- roboflow/core/version.py | 3 ++- roboflow/core/workspace.py | 3 ++- roboflow/util/model_processor.py | 6 ------ roboflow/util/versions.py | 5 +++++ 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/roboflow/core/version.py b/roboflow/core/version.py index 93a6dd98..f6ca0fc7 100644 --- a/roboflow/core/version.py +++ b/roboflow/core/version.py @@ -33,7 +33,7 @@ from roboflow.util.annotations import amend_data_yaml from roboflow.util.general import write_line from roboflow.util.model_processor import process -from roboflow.util.versions import get_wrong_dependencies_versions +from roboflow.util.versions import get_wrong_dependencies_versions, normalize_yolo_model_type if TYPE_CHECKING: import numpy as np @@ -477,6 +477,7 @@ def deploy(self, model_type: str, model_path: str, filename: str = "weights/best model_path (str): File path to the model weights to be uploaded. filename (str, optional): The name of the weights file. Defaults to "weights/best.pt". """ + model_type = normalize_yolo_model_type(model_type) zip_file_name = process(model_type, model_path, filename) if zip_file_name is None: diff --git a/roboflow/core/workspace.py b/roboflow/core/workspace.py index 815c494f..6eed604b 100644 --- a/roboflow/core/workspace.py +++ b/roboflow/core/workspace.py @@ -19,7 +19,7 @@ from roboflow.util.image_utils import load_labelmap from roboflow.util.model_processor import process from roboflow.util.two_stage_utils import ocr_infer - +from roboflow.util.versions import normalize_yolo_model_type class Workspace: """ @@ -594,6 +594,7 @@ def deploy_model( if project_id not in user_projects: raise ValueError(f"Project {project_id} is not accessible in this workspace") + model_type = normalize_yolo_model_type(model_type) zip_file_name = process(model_type, model_path, filename) if zip_file_name is None: diff --git a/roboflow/util/model_processor.py b/roboflow/util/model_processor.py index a99488a1..79c74a52 100644 --- a/roboflow/util/model_processor.py +++ b/roboflow/util/model_processor.py @@ -10,12 +10,6 @@ def process(model_type: str, model_path: str, filename: str) -> str: - if model_type.startswith("yolo11"): - model_type = model_type.replace("yolo11", "yolov11") - - if model_type.startswith("yolo12"): - model_type = model_type.replace("yolo12", "yolov12") - processor = _get_processor_function(model_type) return processor(model_type, model_path, filename) diff --git a/roboflow/util/versions.py b/roboflow/util/versions.py index 1f75a6d4..12e7549f 100644 --- a/roboflow/util/versions.py +++ b/roboflow/util/versions.py @@ -89,3 +89,8 @@ def _wrapper(*args, **kwargs): return _wrapper return _inner + +def normalize_yolo_model_type(model_type: str) -> str: + model_type = model_type.replace("yolo11", "yolov11") + model_type = model_type.replace("yolo12", "yolov12") + return model_type \ No newline at end of file From 8a9dc9adc5b11b30b27d1f43020121ae40874ff9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 6 Mar 2025 22:50:54 +0000 Subject: [PATCH 5/8] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto=20?= =?UTF-8?q?format=20pre-commit=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- roboflow/core/workspace.py | 1 + roboflow/util/versions.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/roboflow/core/workspace.py b/roboflow/core/workspace.py index 6eed604b..279bfb70 100644 --- a/roboflow/core/workspace.py +++ b/roboflow/core/workspace.py @@ -21,6 +21,7 @@ from roboflow.util.two_stage_utils import ocr_infer from roboflow.util.versions import normalize_yolo_model_type + class Workspace: """ Manage a Roboflow workspace. diff --git a/roboflow/util/versions.py b/roboflow/util/versions.py index 12e7549f..b775ff03 100644 --- a/roboflow/util/versions.py +++ b/roboflow/util/versions.py @@ -90,7 +90,8 @@ def _wrapper(*args, **kwargs): return _inner + def normalize_yolo_model_type(model_type: str) -> str: model_type = model_type.replace("yolo11", "yolov11") model_type = model_type.replace("yolo12", "yolov12") - return model_type \ No newline at end of file + return model_type From a5f612c987bc69c17af989f32c1a4c2cf50a8e8c Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Fri, 7 Mar 2025 19:18:32 -0300 Subject: [PATCH 6/8] different approach for yolov12 --- roboflow/util/model_processor.py | 75 ++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/roboflow/util/model_processor.py b/roboflow/util/model_processor.py index 79c74a52..ce85b11d 100644 --- a/roboflow/util/model_processor.py +++ b/roboflow/util/model_processor.py @@ -56,6 +56,9 @@ def _get_processor_function(model_type: str) -> Callable: if "yolonas" in model_type: return _process_yolonas + + if "yolov12" in model_type: + return _process_yolov12 return _process_yolo @@ -107,18 +110,6 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: print_warn_for_wrong_dependencies_versions([("ultralytics", ">=", "8.3.0")], ask_to_continue=True) - elif "yolov12" in model_type: - try: - import torch - import ultralytics - except ImportError: - raise RuntimeError( - "The ultralytics python package is required to deploy yolov12" - " models. Please install it with `pip install ultralytics`" - ) - - print_warn_for_wrong_dependencies_versions([("ultralytics", ">=", "8.3.78")], ask_to_continue=True) - model = torch.load(os.path.join(model_path, filename)) if isinstance(model["model"].names, list): @@ -130,13 +121,12 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: class_names.sort(key=lambda x: x[0]) class_names = [x[1] for x in class_names] - if "yolov8" in model_type or "yolov10" in model_type or "yolov11" in model_type or "yolov12" in model_type: + if "yolov8" in model_type or "yolov10" in model_type or "yolov11" in model_type: # try except for backwards compatibility with older versions of ultralytics if ( "-cls" in model_type or model_type.startswith("yolov10") or model_type.startswith("yolov11") - or model_type.startswith("yolov12") ): nc = model["model"].yaml["nc"] args = model["train_args"] @@ -210,6 +200,63 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: return zip_file_name +def _process_yolov12(model_type: str, model_path: str, filename: str) -> str: + # For YOLOv12, since it uses a special Ultralytics version, + # state dict extraction and model artifacts are handled during model conversion + + print( + "Note: Model must be trained using ultralytics from https://github.com/sunsmarterjie/yolov12 " + "or through the Roboflow platform" + ) + + # Check if model_path exists + if not os.path.exists(model_path): + raise FileNotFoundError(f"Model path {model_path} does not exist.") + + # Find any .pt file in model path + model_files = os.listdir(model_path) + pt_file = next((f for f in model_files if f.endswith('.pt')), None) + + if pt_file is None: + raise RuntimeError("No .pt model file found in the provided path") + + # Copy the .pt file to weights.pt if not already named weights.pt + if pt_file != "weights.pt": + shutil.copy( + os.path.join(model_path, pt_file), + os.path.join(model_path, "weights.pt") + ) + + required_files = [ + "weights.pt" + ] + + optional_files = [ + "results.csv", + "results.png", + "model_artifacts.json" + ] + + zip_file_name = "roboflow_deploy.zip" + with zipfile.ZipFile(os.path.join(model_path, zip_file_name), "w") as zipMe: + for file in required_files: + zipMe.write( + os.path.join(model_path, file), + arcname=file, + compress_type=zipfile.ZIP_DEFLATED + ) + + for file in optional_files: + if os.path.exists(os.path.join(model_path, file)): + zipMe.write( + os.path.join(model_path, file), + arcname=file, + compress_type=zipfile.ZIP_DEFLATED + ) + + return zip_file_name + + def _process_huggingface( model_type: str, model_path: str, filename: str = "fine-tuned-paligemma-3b-pt-224.f16.npz" ) -> str: From 93da5fb5b341d1132e9b677899901eb204623044 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 7 Mar 2025 22:19:26 +0000 Subject: [PATCH 7/8] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto=20?= =?UTF-8?q?format=20pre-commit=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- roboflow/util/model_processor.py | 41 ++++++++------------------------ 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/roboflow/util/model_processor.py b/roboflow/util/model_processor.py index ce85b11d..b7c6a16e 100644 --- a/roboflow/util/model_processor.py +++ b/roboflow/util/model_processor.py @@ -56,7 +56,7 @@ def _get_processor_function(model_type: str) -> Callable: if "yolonas" in model_type: return _process_yolonas - + if "yolov12" in model_type: return _process_yolov12 @@ -123,11 +123,7 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: if "yolov8" in model_type or "yolov10" in model_type or "yolov11" in model_type: # try except for backwards compatibility with older versions of ultralytics - if ( - "-cls" in model_type - or model_type.startswith("yolov10") - or model_type.startswith("yolov11") - ): + if "-cls" in model_type or model_type.startswith("yolov10") or model_type.startswith("yolov11"): nc = model["model"].yaml["nc"] args = model["train_args"] else: @@ -215,44 +211,27 @@ def _process_yolov12(model_type: str, model_path: str, filename: str) -> str: # Find any .pt file in model path model_files = os.listdir(model_path) - pt_file = next((f for f in model_files if f.endswith('.pt')), None) - + pt_file = next((f for f in model_files if f.endswith(".pt")), None) + if pt_file is None: raise RuntimeError("No .pt model file found in the provided path") # Copy the .pt file to weights.pt if not already named weights.pt if pt_file != "weights.pt": - shutil.copy( - os.path.join(model_path, pt_file), - os.path.join(model_path, "weights.pt") - ) + shutil.copy(os.path.join(model_path, pt_file), os.path.join(model_path, "weights.pt")) - required_files = [ - "weights.pt" - ] + required_files = ["weights.pt"] - optional_files = [ - "results.csv", - "results.png", - "model_artifacts.json" - ] + optional_files = ["results.csv", "results.png", "model_artifacts.json"] zip_file_name = "roboflow_deploy.zip" with zipfile.ZipFile(os.path.join(model_path, zip_file_name), "w") as zipMe: for file in required_files: - zipMe.write( - os.path.join(model_path, file), - arcname=file, - compress_type=zipfile.ZIP_DEFLATED - ) - + zipMe.write(os.path.join(model_path, file), arcname=file, compress_type=zipfile.ZIP_DEFLATED) + for file in optional_files: if os.path.exists(os.path.join(model_path, file)): - zipMe.write( - os.path.join(model_path, file), - arcname=file, - compress_type=zipfile.ZIP_DEFLATED - ) + zipMe.write(os.path.join(model_path, file), arcname=file, compress_type=zipfile.ZIP_DEFLATED) return zip_file_name From 266a12884d9be686140589ca7a2f558e55036896 Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Wed, 12 Mar 2025 14:24:58 -0300 Subject: [PATCH 8/8] bump version --- roboflow/__init__.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/roboflow/__init__.py b/roboflow/__init__.py index a5aa01c4..c64a62c1 100644 --- a/roboflow/__init__.py +++ b/roboflow/__init__.py @@ -15,11 +15,7 @@ from roboflow.models import CLIPModel, GazeModel # noqa: F401 from roboflow.util.general import write_line -<<<<<<< HEAD -__version__ = "1.1.55" -======= -__version__ = "1.1.56" ->>>>>>> main +__version__ = "1.1.57" def check_key(api_key, model, notebook, num_retries=0):