Skip to content

Detection tuning params (sensitivity / nms_thresh / nms_aware) from IA.json are dropped on both integration paths #38

Description

@JasonWildMe

Summary

Per-species detection tuning from each Wildbook's IA.json (sensitivity, nms_thresh, nms_aware) is not applied by ml-service on either integration path. Detection instead runs at hardcoded/global defaults. This already affects the three migrated Wildbooks, and will silently discard tuned values for every Wildbook migrated from here on (IoT sea turtles is next).

Found while investigating doubled bounding-box reports from Internet of Turtles users.

Path A — /pipeline/ (used by the migrated Wildbooks today)

PipelineRequest exposes predict_model_params for exactly this purpose (app/routers/pipeline_router.py:31-34), and it is correctly merged over the model config before inference (pipeline_router.py:164-166).

But Wildbook never populates it. MlServiceClient.buildPipelinePayload() sends only image_uri, predict_model_id, classify_model_id, extract_model_id, orientation_model_id — no predict_model_params, and no bbox_score_threshold.

Net effect on the pipeline path:

  • sensitivity → replaced by bbox_score_threshold, which falls back to its default of 0.5 (pipeline_router.py:32)
  • nms_thresh / nms_aware → dropped; NMS runs at whatever the server-side model_config.json sets (LightNet defaults to 0.4, lightnet_model.py:40,45)

Path B — /api/engine/detect/cnn/lightnet/ (WBIA-compat shim, for upcoming migrations)

WbiaDetectRequest declares both fields (app/routers/wbia_compat_router.py:140-141):

sensitivity: Optional[float] = None
nms_thresh: Optional[float] = None
nms_aware: Optional[str] = None

nms_thresh and nms_aware are then never read. _run_detection() takes only sensitivity (wbia_compat_router.py:199,234) and applies it as a post-hoc score filter. Accepting a parameter and silently ignoring it is worse than rejecting it — the caller gets no signal that its tuning was discarded.

Polarity warning for whoever fixes this

Do not pass IA.json's nms_thresh straight through. WBIA inverts it before use:

# wbia/other/ibsfuncs.py:9883
nms_thresh = 1.0 - nms_thresh
keep = detectcore.nms(coord_list, confs_list, nms_thresh)   # py_cpu_nms keeps IoU <= thresh

So in IA.json a higher value means more suppression — the reverse of the usual convention, and the reverse of lightnet's own NonMaxSupression (ious > nms_thresh). A correct port must map effective_iou = 1.0 - ia_json_nms_thresh.

Note also that WBIA disables lightnet's internal NMS entirely (lightnet.py:319, nms_thresh = 1.0) and does suppression itself at the depc layer, whereas ml-service relies on lightnet's internal pass. nms_aware: "ispart" (suppress within body-vs-part pools separately) is closely approximated by lightnet's class_nms=True for models whose part classes are distinct labels, but it is not identical for byclass configs.

Affected migrated models

Wildbook predict_model_id IA.json sensitivity → effective IA.json nms_thresh (→ WBIA effective IoU)
giraffespotter.org giraffe_v1 0.58 → 0.5 0.5 (→0.5), nms_aware unset
zebra.wildme.org ggr2 0.40 → 0.5 0.4 (→0.6), nms_aware unset
Sharkbook.ai fins_v1_dorsal 0.53 → 0.5 0.6 (→0.4), nms_aware: byclass
Sharkbook.ai whaleshark_v0 0.65 → 0.5 0.6 (→0.4)
Sharkbook.ai shark_v0 0.65 → 0.5 0.55 (→0.45)
Sharkbook.ai leopard_shark_v0 0.65 → 0.5 0.5 (→0.5)

The Sharkbook entries are the most consequential: three of them were tuned to sensitivity 0.65 and now run at 0.5, which should measurably increase low-confidence detections. fins_v1_dorsal additionally loses nms_aware: byclass.

(sharkfin-v1 and tigershark-v1 set neither value in IA.json, so they are unaffected.)

Suggested fix

  1. /pipeline/ — have Wildbook forward the tuned values in predict_model_params and bbox_score_threshold; the server side already supports this, so this is mostly a MlServiceClient.buildPipelinePayload() change plus an agreed key mapping.
  2. /api/engine/detect/cnn/lightnet/ — either honor nms_thresh / nms_aware, or return a 4xx/warning when they are supplied, so silent divergence is impossible.
  3. Apply the 1.0 - x polarity conversion at the boundary and document it in one place.
  4. Consider logging the resolved detection params per request; this class of drop is otherwise invisible in production.

Happy to send a PR for (2) if that's a useful starting point.

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions