diff --git a/roboflow/__init__.py b/roboflow/__init__.py index 3c03de8c..c64a62c1 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.56" +__version__ = "1.1.57" def check_key(api_key, model, notebook, num_retries=0): 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 79bffd7a..a73da13b 100644 --- a/roboflow/core/workspace.py +++ b/roboflow/core/workspace.py @@ -19,6 +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: @@ -596,6 +597,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/roboflowpy.py b/roboflow/roboflowpy.py index d68bdaa9..48d6c0d7 100755 --- a/roboflow/roboflowpy.py +++ b/roboflow/roboflowpy.py @@ -81,7 +81,8 @@ def upload_model(args): if args.version_number is not None: # Deploy to specific version - project = workspace.project(args.project) + project_id = args.project[0] if isinstance(args.project, list) else args.project + project = workspace.project(project_id) version = project.version(args.version_number) version.deploy(str(args.model_type), str(args.model_path), str(args.filename)) else: diff --git a/roboflow/util/model_processor.py b/roboflow/util/model_processor.py index 2e2a6173..b7c6a16e 100644 --- a/roboflow/util/model_processor.py +++ b/roboflow/util/model_processor.py @@ -15,20 +15,18 @@ def process(model_type: str, model_path: str, filename: str) -> str: 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): @@ -59,6 +57,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 @@ -195,6 +196,46 @@ 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: diff --git a/roboflow/util/versions.py b/roboflow/util/versions.py index 1f75a6d4..b775ff03 100644 --- a/roboflow/util/versions.py +++ b/roboflow/util/versions.py @@ -89,3 +89,9 @@ 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