Core ML
Core ML is Apple's on-device model format. LibreYOLO traces the detector behind a per-family preprocessing wrapper so the converted graph always takes a canonical RGB image input, then writes an ML Program .mlpackage with the model metadata attached.
- Flag
export(format="coreml")- Writes
- One .mlpackage bundle (a directory) in ML Program format
- Extra
pip install "libreyolo[coreml]"- Loads back
LibreYOLO("weights/LibreYOLO9t.mlpackage") on macOS- Shapes
- Fixed. The input is a hard-shaped ct.ImageType.
- Precision
- FP32, FP16 (half=True). No INT8.
- Families
- Detection only, for yolox, yolo9, rtdetr and rfdetr
Install
pip install "libreyolo[coreml]"Prediction needs macOS. LibreYOLO() refuses a .mlpackage on any other platform
with a message naming the current one, and the support matrix records these
combinations as available on the grounds that runtime parity needs a macOS runner.
Export
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9t.pt") # Writes the bundle weights/LibreYOLO9t.mlpackagepath = model.export(format="coreml")print(path)libreyolo export --model LibreYOLO9t.pt --format coremlmodel.export( format="coreml", imgsz=640, batch=1, half=False, # True converts with FLOAT16 compute precision compute_units="all", # all | cpu_and_gpu | cpu_and_ne | cpu_only output_path=None, # None writes weights/<stem>.mlpackage) # dynamic is accepted but the input is a fixed-shape ct.ImageType,# and the embedded metadata records dynamic=False either way.The bundle is written to weights/ under the checkpoint's stem, with _fp16
appended when half=True. A .mlpackage is a directory, so copy the whole tree.
Every family is traced behind a preprocessing wrapper, so the converted graph
takes one canonical input: RGB, scale=1/255, no bias, declared as
ct.ImageType. The wrapper absorbs the family's own convention, which is BGR in
the range 0 to 255 for YOLOX, ImageNet mean and standard deviation for RF-DETR,
and identity for YOLO9 and RT-DETR. That is why a Core ML consumer feeds an
ordinary image rather than a family-specific tensor.
Conversion targets ML Program with a minimum deployment target of iOS 15.
compute_units is stored on the converted model and can be overridden again when
the artifact is loaded.
Model metadata goes into user_defined_metadata as strings, which is where the
backend reads the family, task, class names, input size and pose schema.
Embedded NMS
from libreyolo import LibreYOLO # YOLOX and YOLO9 detection only, batch 1.LibreYOLO("LibreYOLO9t.pt").export( format="coreml", nms=True, conf=0.25, iou=0.45,)libreyolo export --model LibreYOLO9t.pt --format coreml --nms \ --conf 0.25 --iou 0.45nms=True wraps the model in a Core ML pipeline that ends in Apple's
NonMaximumSuppression layer. The result has two outputs: confidence, shaped
N by the class count, and coordinates, shaped N by 4 as normalized xywh.
It applies to YOLOX and YOLO9 detection only, and it requires batch 1. The
DETR-style families are refused by name, because set prediction takes a top-k over
queries and classes with no IoU step and cannot use that layer. max_det is not
exposed here either; when the detection cap matters, use
ONNX embedded NMS instead.
Run the artifact
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO( "weights/LibreYOLO9t.mlpackage", compute_units="all", # or cpu_and_ne to pin the Neural Engine)result = model.predict(SAMPLE_IMAGE)print(result.boxes.xyxy[:3])import coremltools as ctfrom PIL import Image mlmodel = ct.models.MLModel("weights/LibreYOLO9t.mlpackage")print(mlmodel.user_defined_metadata["model_family"])print(mlmodel.user_defined_metadata["names"]) # The input is an image named "image" at the fixed export size.image = Image.open(SAMPLE_IMAGE).convert("RGB").resize((640, 640))out = mlmodel.predict({"image": image})print({name: value.shape for name, value in out.items()}) # Letterboxing and postprocessing are yours on this path.LibreYOLO() recognizes a directory with the .mlpackage suffix and returns the
same Results object as the checkpoint. compute_units is the one argument the
factory passes through for this format, and it accepts all, cpu_and_gpu,
cpu_and_ne and cpu_only. The device argument is ignored, because Core ML
routes through compute units instead.
The second snippet is the bare-runtime path. Letterboxing, decoding, NMS and
coordinate rescaling become yours there, and the class names live in
user_defined_metadata.
Constraints
Four families, detection only: yolox, yolo9, rtdetr and rfdetr. Anything
else is refused in preflight, because the family-aware preprocessing wrapper is
what makes the fixed image input contract correct, and a family outside it would
convert with the wrong normalization. The error names ONNX and TorchScript as the
alternatives.
The input shape is hard-fixed by ct.ImageType, so dynamic=True changes nothing
and the metadata records dynamic=False. Export a second bundle for a second
resolution.
half=True converts with FP16 compute precision. There is no INT8 path from this
exporter.
For the full family and task grid, see the export matrix. For Apple's newer on-device format, see Core AI. For one combination:
libreyolo formats --family yolo9 --task detect