diff --git a/.claude/skills/mblt-vision/SKILL.md b/.claude/skills/mblt-vision/SKILL.md index 3fafe11..13787d9 100644 --- a/.claude/skills/mblt-vision/SKILL.md +++ b/.claude/skills/mblt-vision/SKILL.md @@ -155,7 +155,8 @@ description: >- complete methodology are in the DOTA section of `mblt_vision/README.md`. - Run YOLOv7 models at `iou_thres: 0.65`, WongKinYiu/yolov7's own `test.py` default. -- ReID is utility-only until published wrappers and routing are added. Reuse +- Published CLIP-ReID wrappers use RN50/ViT processing bases and matching retrieval datasets; + keep their MXQ-only ONNX runtime guard. ReID compile, CLI/benchmark routing remain unavailable. Reuse `EmbeddingPost` for dense vectors, explicit float32 mean/std for source preprocessing, and prepared query/gallery loaders. Preserve ops stable distance ties, camera exclusions and dataset-specific junk handling; see the ReID API section in `mblt_vision/README.md`. diff --git a/AGENTS.md b/AGENTS.md index baaa824..0530a83 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -228,7 +228,10 @@ The current ownership boundary is deliberate: any future model needing more than one Hub artifact, rather than duplicating Hub-resolution logic. -- ReID utilities live in `mblt_vision.person_reid`; concrete published wrappers are separate. +- ReID utilities and twelve published CLIP-ReID wrappers live in `mblt_vision.person_reid`. + Model YAMLs inherit the RN50/ViT processing base and explicitly bind their dataset/dimension. + Published ReID artifacts are MXQ-only; preserve the trusted-local ONNX runtime guard. + ReID compilation/calibration is not supported by compile_vision_model in this stage. Use `EmbeddingPost` for validated dense vectors and `PersonReIDPost` for dataset binding. Retrieval protocols and prepared loaders are pinned to ops source in dataset YAMLs; keep stable distance ties, camera exclusions, junk rules, and preprocessing numeric parity tested. diff --git a/mblt_vision/README.md b/mblt_vision/README.md index 2d4b3bd..bebdada 100644 --- a/mblt_vision/README.md +++ b/mblt_vision/README.md @@ -1065,3 +1065,26 @@ Dataset YAMLs record exact source paths/revision. CFP-FP specifically refers to the prepared cat-claws mirror: ops warns it was not checked against the original `cfp_fp.bin` and records a training-log discrepancy. This package preserves that provenance and makes no new accuracy claim. + +### Published ReID models + +The twelve published `CLIP_ReID_*` wrappers are available from +`mblt_vision.person_reid`, lazy top-level imports, and `list_models("person_reid")`. +They cover RN50 / ViT-B-16, baseline / fine-tuned, each for Market-1501, DukeMTMC-reID +and Occluded-Duke. Every YAML inherits `bases/person_reid.yaml` and binds the +matching retrieval dataset. RN50 uses 3072 dimensions and torch mean/std; ViT uses +1280 dimensions and 0.5 mean/std. Both take 256x128 PIL bilinear crops and one +image input, confirmed from ops distribution contracts and recorded preprocessing. + +HF metadata confirms one canonical `aries-rb/.mxq` per repository, +with no published ONNX. These configurations retain `revision: main` by project +policy; the approval HF commit is explicitly **not recorded**, while the ops source +commit and exact source paths are included under `source`. Live metadata SHAs are +audit diagnostics, not approval pins. `onnx_local_artifact_only: true` preserves +automatic MXQ loading and requires trusted local ONNX for runtime. +`compile_vision_model` does not yet support `person_reid`; this stage does not +add a ReID compilation/calibration path. +The guard is inherited from the PR31 dependency, not reimplemented here. + +ArcFace/CosFace eight-model publication is pending. Those models remain +inventory-only: no guessed HF repository identity or downloadable YAML is shipped. diff --git a/mblt_vision/__init__.py b/mblt_vision/__init__.py index 177a478..89a596e 100644 --- a/mblt_vision/__init__.py +++ b/mblt_vision/__init__.py @@ -31,12 +31,15 @@ object_detection, obb, pose_estimation, + person_reid, semantic_segmentation, ) _LEGACY_MODEL_EXPORTS: dict[str, object] = {} for _task_module in _TASK_MODULES: - for _export_name in getattr(_task_module, "__all__", ()): + for _export_name in getattr( + _task_module, "MODEL_EXPORTS", getattr(_task_module, "__all__", ()) + ): if _export_name in _LEGACY_MODEL_EXPORTS: raise RuntimeError( f"Duplicate vision export detected for '{_export_name}'." diff --git a/mblt_vision/models/CLIP_ReID_RN50_Baseline_DukeMTMC.yaml b/mblt_vision/models/CLIP_ReID_RN50_Baseline_DukeMTMC.yaml new file mode 100644 index 0000000..93f46ed --- /dev/null +++ b/mblt_vision/models/CLIP_ReID_RN50_Baseline_DukeMTMC.yaml @@ -0,0 +1,17 @@ +# Approval HF commit: not recorded; main retained by explicit project policy. +DEFAULT: + extends: bases/person_reid.yaml#DEFAULT + file_cfg: + repo_id: mobilint/CLIP_ReID_RN50_Baseline_DukeMTMC + filename: clip_reid_rn50_baseline_dukemtmc.mxq + revision: main + onnx_local_artifact_only: true + post_cfg: + dataset: dukemtmc-reid + embedding_dim: 3072 + source: + repository: https://git.mobilint.com/algorithm-team/testing/mblt-model-ops + revision: ec537764e36ca10da963fcb7afab157ea60ca022 + distribution_path: models/person_reid/CLIP_ReID_RN50_Baseline_DukeMTMC/distribution.yaml + pre_cfg_path: models/person_reid/CLIP_ReID_RN50_Baseline_DukeMTMC/_source/predict_float.input.json + approved_hf_revision: not recorded diff --git a/mblt_vision/models/CLIP_ReID_RN50_Baseline_Market1501.yaml b/mblt_vision/models/CLIP_ReID_RN50_Baseline_Market1501.yaml new file mode 100644 index 0000000..762082b --- /dev/null +++ b/mblt_vision/models/CLIP_ReID_RN50_Baseline_Market1501.yaml @@ -0,0 +1,17 @@ +# Approval HF commit: not recorded; main retained by explicit project policy. +DEFAULT: + extends: bases/person_reid.yaml#DEFAULT + file_cfg: + repo_id: mobilint/CLIP_ReID_RN50_Baseline_Market1501 + filename: clip_reid_rn50_baseline_market1501.mxq + revision: main + onnx_local_artifact_only: true + post_cfg: + dataset: market1501 + embedding_dim: 3072 + source: + repository: https://git.mobilint.com/algorithm-team/testing/mblt-model-ops + revision: ec537764e36ca10da963fcb7afab157ea60ca022 + distribution_path: models/person_reid/CLIP_ReID_RN50_Baseline_Market1501/distribution.yaml + pre_cfg_path: models/person_reid/CLIP_ReID_RN50_Baseline_Market1501/_source/predict_float.input.json + approved_hf_revision: not recorded diff --git a/mblt_vision/models/CLIP_ReID_RN50_Baseline_OccDuke.yaml b/mblt_vision/models/CLIP_ReID_RN50_Baseline_OccDuke.yaml new file mode 100644 index 0000000..7df7104 --- /dev/null +++ b/mblt_vision/models/CLIP_ReID_RN50_Baseline_OccDuke.yaml @@ -0,0 +1,17 @@ +# Approval HF commit: not recorded; main retained by explicit project policy. +DEFAULT: + extends: bases/person_reid.yaml#DEFAULT + file_cfg: + repo_id: mobilint/CLIP_ReID_RN50_Baseline_OccDuke + filename: clip_reid_rn50_baseline_occduke.mxq + revision: main + onnx_local_artifact_only: true + post_cfg: + dataset: occluded-duke + embedding_dim: 3072 + source: + repository: https://git.mobilint.com/algorithm-team/testing/mblt-model-ops + revision: ec537764e36ca10da963fcb7afab157ea60ca022 + distribution_path: models/person_reid/CLIP_ReID_RN50_Baseline_OccDuke/distribution.yaml + pre_cfg_path: models/person_reid/CLIP_ReID_RN50_Baseline_OccDuke/_source/predict_float.input.json + approved_hf_revision: not recorded diff --git a/mblt_vision/models/CLIP_ReID_RN50_DukeMTMC.yaml b/mblt_vision/models/CLIP_ReID_RN50_DukeMTMC.yaml new file mode 100644 index 0000000..a0edfff --- /dev/null +++ b/mblt_vision/models/CLIP_ReID_RN50_DukeMTMC.yaml @@ -0,0 +1,17 @@ +# Approval HF commit: not recorded; main retained by explicit project policy. +DEFAULT: + extends: bases/person_reid.yaml#DEFAULT + file_cfg: + repo_id: mobilint/CLIP_ReID_RN50_DukeMTMC + filename: clip_reid_rn50_dukemtmc.mxq + revision: main + onnx_local_artifact_only: true + post_cfg: + dataset: dukemtmc-reid + embedding_dim: 3072 + source: + repository: https://git.mobilint.com/algorithm-team/testing/mblt-model-ops + revision: ec537764e36ca10da963fcb7afab157ea60ca022 + distribution_path: models/person_reid/CLIP_ReID_RN50_DukeMTMC/distribution.yaml + pre_cfg_path: models/person_reid/CLIP_ReID_RN50_DukeMTMC/_source/predict_float.input.json + approved_hf_revision: not recorded diff --git a/mblt_vision/models/CLIP_ReID_RN50_Market1501.yaml b/mblt_vision/models/CLIP_ReID_RN50_Market1501.yaml new file mode 100644 index 0000000..54cc350 --- /dev/null +++ b/mblt_vision/models/CLIP_ReID_RN50_Market1501.yaml @@ -0,0 +1,17 @@ +# Approval HF commit: not recorded; main retained by explicit project policy. +DEFAULT: + extends: bases/person_reid.yaml#DEFAULT + file_cfg: + repo_id: mobilint/CLIP_ReID_RN50_Market1501 + filename: clip_reid_rn50_market1501.mxq + revision: main + onnx_local_artifact_only: true + post_cfg: + dataset: market1501 + embedding_dim: 3072 + source: + repository: https://git.mobilint.com/algorithm-team/testing/mblt-model-ops + revision: ec537764e36ca10da963fcb7afab157ea60ca022 + distribution_path: models/person_reid/CLIP_ReID_RN50_Market1501/distribution.yaml + pre_cfg_path: models/person_reid/CLIP_ReID_RN50_Market1501/_source/predict_float.input.json + approved_hf_revision: not recorded diff --git a/mblt_vision/models/CLIP_ReID_RN50_OccDuke.yaml b/mblt_vision/models/CLIP_ReID_RN50_OccDuke.yaml new file mode 100644 index 0000000..8e5ff73 --- /dev/null +++ b/mblt_vision/models/CLIP_ReID_RN50_OccDuke.yaml @@ -0,0 +1,17 @@ +# Approval HF commit: not recorded; main retained by explicit project policy. +DEFAULT: + extends: bases/person_reid.yaml#DEFAULT + file_cfg: + repo_id: mobilint/CLIP_ReID_RN50_OccDuke + filename: clip_reid_rn50_occduke.mxq + revision: main + onnx_local_artifact_only: true + post_cfg: + dataset: occluded-duke + embedding_dim: 3072 + source: + repository: https://git.mobilint.com/algorithm-team/testing/mblt-model-ops + revision: ec537764e36ca10da963fcb7afab157ea60ca022 + distribution_path: models/person_reid/CLIP_ReID_RN50_OccDuke/distribution.yaml + pre_cfg_path: models/person_reid/CLIP_ReID_RN50_OccDuke/_source/predict_float.input.json + approved_hf_revision: not recorded diff --git a/mblt_vision/models/CLIP_ReID_ViT_B_16_Baseline_DukeMTMC.yaml b/mblt_vision/models/CLIP_ReID_ViT_B_16_Baseline_DukeMTMC.yaml new file mode 100644 index 0000000..478aa35 --- /dev/null +++ b/mblt_vision/models/CLIP_ReID_ViT_B_16_Baseline_DukeMTMC.yaml @@ -0,0 +1,17 @@ +# Approval HF commit: not recorded; main retained by explicit project policy. +DEFAULT: + extends: bases/person_reid.yaml#VIT + file_cfg: + repo_id: mobilint/CLIP_ReID_ViT_B_16_Baseline_DukeMTMC + filename: clip_reid_vit_b_16_baseline_dukemtmc.mxq + revision: main + onnx_local_artifact_only: true + post_cfg: + dataset: dukemtmc-reid + embedding_dim: 1280 + source: + repository: https://git.mobilint.com/algorithm-team/testing/mblt-model-ops + revision: ec537764e36ca10da963fcb7afab157ea60ca022 + distribution_path: models/person_reid/CLIP_ReID_ViT_B_16_Baseline_DukeMTMC/distribution.yaml + pre_cfg_path: models/person_reid/CLIP_ReID_ViT_B_16_Baseline_DukeMTMC/_source/predict_float.input.json + approved_hf_revision: not recorded diff --git a/mblt_vision/models/CLIP_ReID_ViT_B_16_Baseline_Market1501.yaml b/mblt_vision/models/CLIP_ReID_ViT_B_16_Baseline_Market1501.yaml new file mode 100644 index 0000000..7ea0b0b --- /dev/null +++ b/mblt_vision/models/CLIP_ReID_ViT_B_16_Baseline_Market1501.yaml @@ -0,0 +1,17 @@ +# Approval HF commit: not recorded; main retained by explicit project policy. +DEFAULT: + extends: bases/person_reid.yaml#VIT + file_cfg: + repo_id: mobilint/CLIP_ReID_ViT_B_16_Baseline_Market1501 + filename: clip_reid_vit_b_16_baseline_market1501.mxq + revision: main + onnx_local_artifact_only: true + post_cfg: + dataset: market1501 + embedding_dim: 1280 + source: + repository: https://git.mobilint.com/algorithm-team/testing/mblt-model-ops + revision: ec537764e36ca10da963fcb7afab157ea60ca022 + distribution_path: models/person_reid/CLIP_ReID_ViT_B_16_Baseline_Market1501/distribution.yaml + pre_cfg_path: models/person_reid/CLIP_ReID_ViT_B_16_Baseline_Market1501/_source/predict_float.input.json + approved_hf_revision: not recorded diff --git a/mblt_vision/models/CLIP_ReID_ViT_B_16_Baseline_OccDuke.yaml b/mblt_vision/models/CLIP_ReID_ViT_B_16_Baseline_OccDuke.yaml new file mode 100644 index 0000000..c655b3c --- /dev/null +++ b/mblt_vision/models/CLIP_ReID_ViT_B_16_Baseline_OccDuke.yaml @@ -0,0 +1,17 @@ +# Approval HF commit: not recorded; main retained by explicit project policy. +DEFAULT: + extends: bases/person_reid.yaml#VIT + file_cfg: + repo_id: mobilint/CLIP_ReID_ViT_B_16_Baseline_OccDuke + filename: clip_reid_vit_b_16_baseline_occduke.mxq + revision: main + onnx_local_artifact_only: true + post_cfg: + dataset: occluded-duke + embedding_dim: 1280 + source: + repository: https://git.mobilint.com/algorithm-team/testing/mblt-model-ops + revision: ec537764e36ca10da963fcb7afab157ea60ca022 + distribution_path: models/person_reid/CLIP_ReID_ViT_B_16_Baseline_OccDuke/distribution.yaml + pre_cfg_path: models/person_reid/CLIP_ReID_ViT_B_16_Baseline_OccDuke/_source/predict_float.input.json + approved_hf_revision: not recorded diff --git a/mblt_vision/models/CLIP_ReID_ViT_B_16_DukeMTMC.yaml b/mblt_vision/models/CLIP_ReID_ViT_B_16_DukeMTMC.yaml new file mode 100644 index 0000000..b6462e4 --- /dev/null +++ b/mblt_vision/models/CLIP_ReID_ViT_B_16_DukeMTMC.yaml @@ -0,0 +1,17 @@ +# Approval HF commit: not recorded; main retained by explicit project policy. +DEFAULT: + extends: bases/person_reid.yaml#VIT + file_cfg: + repo_id: mobilint/CLIP_ReID_ViT_B_16_DukeMTMC + filename: clip_reid_vit_b_16_dukemtmc.mxq + revision: main + onnx_local_artifact_only: true + post_cfg: + dataset: dukemtmc-reid + embedding_dim: 1280 + source: + repository: https://git.mobilint.com/algorithm-team/testing/mblt-model-ops + revision: ec537764e36ca10da963fcb7afab157ea60ca022 + distribution_path: models/person_reid/CLIP_ReID_ViT_B_16_DukeMTMC/distribution.yaml + pre_cfg_path: models/person_reid/CLIP_ReID_ViT_B_16_DukeMTMC/_source/predict_float.input.json + approved_hf_revision: not recorded diff --git a/mblt_vision/models/CLIP_ReID_ViT_B_16_Market1501.yaml b/mblt_vision/models/CLIP_ReID_ViT_B_16_Market1501.yaml new file mode 100644 index 0000000..f9b601c --- /dev/null +++ b/mblt_vision/models/CLIP_ReID_ViT_B_16_Market1501.yaml @@ -0,0 +1,17 @@ +# Approval HF commit: not recorded; main retained by explicit project policy. +DEFAULT: + extends: bases/person_reid.yaml#VIT + file_cfg: + repo_id: mobilint/CLIP_ReID_ViT_B_16_Market1501 + filename: clip_reid_vit_b_16_market1501.mxq + revision: main + onnx_local_artifact_only: true + post_cfg: + dataset: market1501 + embedding_dim: 1280 + source: + repository: https://git.mobilint.com/algorithm-team/testing/mblt-model-ops + revision: ec537764e36ca10da963fcb7afab157ea60ca022 + distribution_path: models/person_reid/CLIP_ReID_ViT_B_16_Market1501/distribution.yaml + pre_cfg_path: models/person_reid/CLIP_ReID_ViT_B_16_Market1501/_source/predict_float.input.json + approved_hf_revision: not recorded diff --git a/mblt_vision/models/CLIP_ReID_ViT_B_16_OccDuke.yaml b/mblt_vision/models/CLIP_ReID_ViT_B_16_OccDuke.yaml new file mode 100644 index 0000000..a7fabfb --- /dev/null +++ b/mblt_vision/models/CLIP_ReID_ViT_B_16_OccDuke.yaml @@ -0,0 +1,17 @@ +# Approval HF commit: not recorded; main retained by explicit project policy. +DEFAULT: + extends: bases/person_reid.yaml#VIT + file_cfg: + repo_id: mobilint/CLIP_ReID_ViT_B_16_OccDuke + filename: clip_reid_vit_b_16_occduke.mxq + revision: main + onnx_local_artifact_only: true + post_cfg: + dataset: occluded-duke + embedding_dim: 1280 + source: + repository: https://git.mobilint.com/algorithm-team/testing/mblt-model-ops + revision: ec537764e36ca10da963fcb7afab157ea60ca022 + distribution_path: models/person_reid/CLIP_ReID_ViT_B_16_OccDuke/distribution.yaml + pre_cfg_path: models/person_reid/CLIP_ReID_ViT_B_16_OccDuke/_source/predict_float.input.json + approved_hf_revision: not recorded diff --git a/mblt_vision/models/bases/person_reid.yaml b/mblt_vision/models/bases/person_reid.yaml index cafeaf3..1d75d79 100644 --- a/mblt_vision/models/bases/person_reid.yaml +++ b/mblt_vision/models/bases/person_reid.yaml @@ -24,3 +24,9 @@ OCCLUDED_DUKE: extends: person_reid.yaml#DEFAULT post_cfg: dataset: occluded-duke +VIT: + extends: person_reid.yaml#DEFAULT + pre_cfg: + Normalize: + mean: [0.5, 0.5, 0.5] + std: [0.5, 0.5, 0.5] diff --git a/mblt_vision/person_reid/__init__.py b/mblt_vision/person_reid/__init__.py index e5e6574..0abfca2 100644 --- a/mblt_vision/person_reid/__init__.py +++ b/mblt_vision/person_reid/__init__.py @@ -1,5 +1,6 @@ """Person retrieval utilities; published model wrappers are added separately.""" +from .._compat import create_model_class from ..utils.datasets.reid import ( DukeMTMCReIDDataset, Market1501Dataset, @@ -32,3 +33,50 @@ "RetrievalResult", "extract_embeddings", ] + +# Concrete published single-input models; utilities above remain explicit exports. +CLIP_ReID_RN50_Baseline_DukeMTMC = create_model_class( + "CLIP_ReID_RN50_Baseline_DukeMTMC", __name__ +) +CLIP_ReID_RN50_Baseline_Market1501 = create_model_class( + "CLIP_ReID_RN50_Baseline_Market1501", __name__ +) +CLIP_ReID_RN50_Baseline_OccDuke = create_model_class( + "CLIP_ReID_RN50_Baseline_OccDuke", __name__ +) +CLIP_ReID_RN50_DukeMTMC = create_model_class("CLIP_ReID_RN50_DukeMTMC", __name__) +CLIP_ReID_RN50_Market1501 = create_model_class("CLIP_ReID_RN50_Market1501", __name__) +CLIP_ReID_RN50_OccDuke = create_model_class("CLIP_ReID_RN50_OccDuke", __name__) +CLIP_ReID_ViT_B_16_Baseline_DukeMTMC = create_model_class( + "CLIP_ReID_ViT_B_16_Baseline_DukeMTMC", __name__ +) +CLIP_ReID_ViT_B_16_Baseline_Market1501 = create_model_class( + "CLIP_ReID_ViT_B_16_Baseline_Market1501", __name__ +) +CLIP_ReID_ViT_B_16_Baseline_OccDuke = create_model_class( + "CLIP_ReID_ViT_B_16_Baseline_OccDuke", __name__ +) +CLIP_ReID_ViT_B_16_DukeMTMC = create_model_class( + "CLIP_ReID_ViT_B_16_DukeMTMC", __name__ +) +CLIP_ReID_ViT_B_16_Market1501 = create_model_class( + "CLIP_ReID_ViT_B_16_Market1501", __name__ +) +CLIP_ReID_ViT_B_16_OccDuke = create_model_class("CLIP_ReID_ViT_B_16_OccDuke", __name__) + +MODEL_EXPORTS = [ + "CLIP_ReID_RN50_Baseline_DukeMTMC", + "CLIP_ReID_RN50_Baseline_Market1501", + "CLIP_ReID_RN50_Baseline_OccDuke", + "CLIP_ReID_RN50_DukeMTMC", + "CLIP_ReID_RN50_Market1501", + "CLIP_ReID_RN50_OccDuke", + "CLIP_ReID_ViT_B_16_Baseline_DukeMTMC", + "CLIP_ReID_ViT_B_16_Baseline_Market1501", + "CLIP_ReID_ViT_B_16_Baseline_OccDuke", + "CLIP_ReID_ViT_B_16_DukeMTMC", + "CLIP_ReID_ViT_B_16_Market1501", + "CLIP_ReID_ViT_B_16_OccDuke", +] + +__all__ += MODEL_EXPORTS diff --git a/tests/test_model_metadata.py b/tests/test_model_metadata.py index de3fb68..d45bcc4 100644 --- a/tests/test_model_metadata.py +++ b/tests/test_model_metadata.py @@ -19,6 +19,7 @@ "object_detection": {"coco"}, "obb": {"dotav1"}, "pose_estimation": {"coco"}, + "person_reid": {"market1501", "dukemtmc-reid", "occluded-duke"}, "semantic_segmentation": {"ade20k", "cityscapes"}, } diff --git a/tests/test_published_reid.py b/tests/test_published_reid.py new file mode 100644 index 0000000..bb5dfa7 --- /dev/null +++ b/tests/test_published_reid.py @@ -0,0 +1,102 @@ +"""Published ReID metadata, offline processing and source parity (no weights).""" + +from pathlib import Path + +import mblt_vision +import numpy as np +import pytest +import torch +from mblt_vision.utils.postprocess import PersonReIDPost, build_postprocess +from mblt_vision.utils.preprocess import build_preprocess +from mblt_vision.wrapper import resolve_model_config +from PIL import Image + +MODELS = [ + f"CLIP_ReID_{backbone}{baseline}_{dataset}" + for backbone in ("RN50", "ViT_B_16") + for baseline in ("", "_Baseline") + for dataset in ("Market1501", "DukeMTMC", "OccDuke") +] +DATASETS = { + "Market1501": "market1501", + "DukeMTMC": "dukemtmc-reid", + "OccDuke": "occluded-duke", +} + + +@pytest.mark.parametrize("model", MODELS) +def test_published_reid_config_and_discovery(model): + cfg = resolve_model_config(model) + dim = 1280 if "ViT" in model else 3072 + assert cfg["file_cfg"] == { + "repo_id": "mobilint/" + model, + "filename": model.lower() + ".mxq", + "revision": "main", + "onnx_local_artifact_only": True, + "onnx_filename": model.lower() + ".onnx", + } + assert cfg["post_cfg"] == { + "task": "person_reid", + "dataset": DATASETS[model.split("_")[-1]], + "normalize": True, + "embedding_dim": dim, + } + assert cfg["source"]["approved_hf_revision"] == "not recorded" + assert cfg["source"]["revision"] == "ec537764e36ca10da963fcb7afab157ea60ca022" + assert model in mblt_vision.list_models("person_reid")["person_reid"] + assert getattr(mblt_vision, model) is getattr(mblt_vision.person_reid, model) + post = build_postprocess(cfg["pre_cfg"], cfg["post_cfg"]) + assert isinstance(post, PersonReIDPost) + raw = np.ones((1, dim), dtype=np.float32) + out = post([raw]) + assert tuple(out.shape) == (1, dim) + torch.testing.assert_close(torch.linalg.vector_norm(out, dim=1), torch.ones(1)) + + +@pytest.mark.parametrize("model", MODELS) +def test_published_reid_preprocess_source_numeric_parity(tmp_path, model): + cfg = resolve_model_config(model) + rng = np.random.default_rng(0) + pixels = rng.integers(0, 256, (53, 31, 3), dtype=np.uint8) + path = tmp_path / "0001_c1_synthetic.png" + Image.fromarray(pixels).save(path) + actual = build_preprocess(cfg["pre_cfg"])(path) + if "ViT" in model: + mean, std = [0.5] * 3, [0.5] * 3 + else: + mean, std = [0.485, 0.456, 0.406], [0.229, 0.224, 0.225] + # ops models/person_reid/_shared/preprocess.py at ec537764, canonical order. + with Image.open(path) as image: + resized = image.convert("RGB").resize((128, 256), Image.Resampling.BILINEAR) + expected = ( + np.asarray(resized, dtype=np.float32) / 255.0 + - np.asarray(mean, dtype=np.float32) + ) / np.asarray(std, dtype=np.float32) + np.testing.assert_array_equal(actual, expected) # atol=0 for every concrete config + + +def test_face_publish_pending_models_remain_inventory_only(): + root = Path(__file__).parents[1] / "mblt_vision/models" + assert not list(root.glob("ArcFace_IResNet_*.yaml")) + assert not list(root.glob("CosFace_IResNet_*.yaml")) + + +def test_top_level_reid_exports_are_model_only(): + assert set(mblt_vision.person_reid.MODEL_EXPORTS) == set(MODELS) + for name in ( + "EmbeddingPost", + "ReIDSample", + "Market1501Dataset", + "RetrievalEvaluator", + "extract_embeddings", + ): + assert name not in mblt_vision.__all__ + assert name not in mblt_vision._LEGACY_MODEL_EXPORTS + assert hasattr(mblt_vision.person_reid, name) + + +def test_reid_compile_is_explicitly_unsupported(): + from mblt_vision.compile.vision import _normalize_task + + with pytest.raises(ValueError, match="Unsupported Vision task"): + _normalize_task("person_reid")