orik-ss commited on
Commit
1319df4
·
1 Parent(s): 100dbc1

Added threshold slider for siglip models

Browse files
Files changed (3) hide show
  1. app.py +47 -7
  2. siglip2_onnx_zeroshot.py +2 -10
  3. siglip_zeroshot.py +2 -10
app.py CHANGED
@@ -115,7 +115,9 @@ CLASSIFIER_MAP = {
115
  }
116
 
117
 
118
- def run_dfine_classify(image, refs_path, dfine_threshold, dfine_model_choice, min_display_conf=0.703, gap_threshold=0.005, classifier_choice="Jina-CLIP-v2 (few-shot)"):
 
 
119
  """Tab 2: D-FINE first, then classify crops.
120
  Returns (group_crop_gallery, known_crop_gallery, status_message).
121
  """
@@ -129,16 +131,27 @@ def run_dfine_classify(image, refs_path, dfine_threshold, dfine_model_choice, mi
129
 
130
  dfine_model = "large" if dfine_model_choice.strip().lower() == "large" else "medium"
131
  classifier = CLASSIFIER_MAP.get(classifier_choice, "jina")
 
 
 
 
 
 
 
 
 
 
 
132
  group_crops, known_crops, status = run_single_image(
133
  image,
134
  refs_dir=refs,
135
  dfine_model=dfine_model,
136
  det_threshold=float(dfine_threshold),
137
- conf_threshold=0.5,
138
- gap_threshold=float(gap_threshold),
139
  min_side=24,
140
  crop_dedup_iou=0.4,
141
- min_display_conf=float(min_display_conf),
142
  classifier=classifier,
143
  )
144
 
@@ -298,12 +311,13 @@ with gr.Blocks(title="Small Object Detection") as app:
298
 
299
  with gr.Column(scale=1):
300
 
 
301
  threshold_slider = gr.Slider(
302
  minimum=0.0,
303
  maximum=1.0,
304
  value=0.703,
305
  step=0.005,
306
- label="Threshold (min display confidence)",
307
  )
308
 
309
  gap_slider = gr.Slider(
@@ -311,7 +325,17 @@ with gr.Blocks(title="Small Object Detection") as app:
311
  maximum=0.02,
312
  value=0.005,
313
  step=0.001,
314
- label="Gap: how much the top guess must beat the runner-up (higher = stricter, fewer accepted)",
 
 
 
 
 
 
 
 
 
 
315
  )
316
 
317
  out_gallery_dfine = gr.Gallery(
@@ -334,9 +358,25 @@ with gr.Blocks(title="Small Object Detection") as app:
334
  interactive=False,
335
  )
336
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
  btn_dfine.click(
338
  fn=run_dfine_classify,
339
- inputs=[inp_dfine, refs_path, dfine_threshold_slider, dfine_model_radio, threshold_slider, gap_slider, classifier_radio],
 
340
  outputs=[out_gallery_dfine, out_gallery_known, out_status_dfine],
341
  concurrency_limit=1,
342
  )
 
115
  }
116
 
117
 
118
+ def run_dfine_classify(image, refs_path, dfine_threshold, dfine_model_choice,
119
+ min_display_conf, gap_threshold, siglip_threshold,
120
+ classifier_choice="Jina-CLIP-v2 (few-shot)"):
121
  """Tab 2: D-FINE first, then classify crops.
122
  Returns (group_crop_gallery, known_crop_gallery, status_message).
123
  """
 
131
 
132
  dfine_model = "large" if dfine_model_choice.strip().lower() == "large" else "medium"
133
  classifier = CLASSIFIER_MAP.get(classifier_choice, "jina")
134
+
135
+ # SigLIP models: use their own threshold, no gap check
136
+ if classifier in ("siglip", "siglip2_onnx"):
137
+ conf_thresh = float(siglip_threshold)
138
+ gap_thresh = 0.0
139
+ display_conf = float(siglip_threshold)
140
+ else:
141
+ conf_thresh = 0.5
142
+ gap_thresh = float(gap_threshold)
143
+ display_conf = float(min_display_conf)
144
+
145
  group_crops, known_crops, status = run_single_image(
146
  image,
147
  refs_dir=refs,
148
  dfine_model=dfine_model,
149
  det_threshold=float(dfine_threshold),
150
+ conf_threshold=conf_thresh,
151
+ gap_threshold=gap_thresh,
152
  min_side=24,
153
  crop_dedup_iou=0.4,
154
+ min_display_conf=display_conf,
155
  classifier=classifier,
156
  )
157
 
 
311
 
312
  with gr.Column(scale=1):
313
 
314
+ # --- Jina thresholds (visible when Jina selected) ---
315
  threshold_slider = gr.Slider(
316
  minimum=0.0,
317
  maximum=1.0,
318
  value=0.703,
319
  step=0.005,
320
+ label="Jina: min display confidence",
321
  )
322
 
323
  gap_slider = gr.Slider(
 
325
  maximum=0.02,
326
  value=0.005,
327
  step=0.001,
328
+ label="Jina: gap (top class must beat runner-up by this much)",
329
+ )
330
+
331
+ # --- SigLIP threshold (visible when SigLIP selected) ---
332
+ siglip_threshold_slider = gr.Slider(
333
+ minimum=0.0,
334
+ maximum=1.0,
335
+ value=0.05,
336
+ step=0.01,
337
+ label="SigLIP: min confidence threshold",
338
+ visible=False,
339
  )
340
 
341
  out_gallery_dfine = gr.Gallery(
 
358
  interactive=False,
359
  )
360
 
361
+ # Show/hide threshold sliders based on classifier choice
362
+ def update_threshold_visibility(choice):
363
+ is_jina = (choice == "Jina-CLIP-v2 (few-shot)")
364
+ return (
365
+ gr.update(visible=is_jina), # threshold_slider
366
+ gr.update(visible=is_jina), # gap_slider
367
+ gr.update(visible=not is_jina), # siglip_threshold_slider
368
+ )
369
+
370
+ classifier_radio.change(
371
+ fn=update_threshold_visibility,
372
+ inputs=[classifier_radio],
373
+ outputs=[threshold_slider, gap_slider, siglip_threshold_slider],
374
+ )
375
+
376
  btn_dfine.click(
377
  fn=run_dfine_classify,
378
+ inputs=[inp_dfine, refs_path, dfine_threshold_slider, dfine_model_radio,
379
+ threshold_slider, gap_slider, siglip_threshold_slider, classifier_radio],
380
  outputs=[out_gallery_dfine, out_gallery_known, out_status_dfine],
381
  concurrency_limit=1,
382
  )
siglip2_onnx_zeroshot.py CHANGED
@@ -163,20 +163,12 @@ class SigLIP2ONNXClassifier:
163
  conf = float(probs[best_idx])
164
  gap = float(probs[best_idx] - probs[second_idx])
165
 
166
- conf_ok = conf >= conf_threshold
167
- gap_ok = gap >= gap_threshold
168
-
169
- if conf_ok and gap_ok:
170
  prediction = self.labels[best_idx]
171
  status = "accepted"
172
  else:
173
  prediction = "unknown"
174
- reasons = []
175
- if not conf_ok:
176
- reasons.append(f"conf {conf:.4f} < {conf_threshold}")
177
- if not gap_ok:
178
- reasons.append(f"gap {gap:.4f} < {gap_threshold}")
179
- status = "rejected: " + ", ".join(reasons)
180
 
181
  return {
182
  "prediction": prediction,
 
163
  conf = float(probs[best_idx])
164
  gap = float(probs[best_idx] - probs[second_idx])
165
 
166
+ if conf >= conf_threshold:
 
 
 
167
  prediction = self.labels[best_idx]
168
  status = "accepted"
169
  else:
170
  prediction = "unknown"
171
+ status = f"rejected: conf {conf:.4f} < {conf_threshold}"
 
 
 
 
 
172
 
173
  return {
174
  "prediction": prediction,
siglip_zeroshot.py CHANGED
@@ -62,20 +62,12 @@ class SigLIPClassifier:
62
  conf = float(probs[best_idx])
63
  gap = float(probs[best_idx] - probs[second_idx])
64
 
65
- conf_ok = conf >= conf_threshold
66
- gap_ok = gap >= gap_threshold
67
-
68
- if conf_ok and gap_ok:
69
  prediction = self.labels[best_idx]
70
  status = "accepted"
71
  else:
72
  prediction = "unknown"
73
- reasons = []
74
- if not conf_ok:
75
- reasons.append(f"conf {conf:.4f} < {conf_threshold}")
76
- if not gap_ok:
77
- reasons.append(f"gap {gap:.4f} < {gap_threshold}")
78
- status = "rejected: " + ", ".join(reasons)
79
 
80
  return {
81
  "prediction": prediction,
 
62
  conf = float(probs[best_idx])
63
  gap = float(probs[best_idx] - probs[second_idx])
64
 
65
+ if conf >= conf_threshold:
 
 
 
66
  prediction = self.labels[best_idx]
67
  status = "accepted"
68
  else:
69
  prediction = "unknown"
70
+ status = f"rejected: conf {conf:.4f} < {conf_threshold}"
 
 
 
 
 
71
 
72
  return {
73
  "prediction": prediction,