github-actions[bot] commited on
Commit
44a7f6e
Β·
1 Parent(s): fb930ad

deploy: sync code from GH commit d0a11cd

Browse files
src/agrianalyze/api/app.py CHANGED
@@ -196,6 +196,40 @@ def _is_plant_image(image_bgr: np.ndarray) -> dict:
196
  # - Farmer holding a leaf (hand + plant)
197
  # - Plant in an office/lab setting
198
  # - Field photo with people in background
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  if has_plant_region:
200
  is_plant = True
201
  reason = ""
 
196
  # - Farmer holding a leaf (hand + plant)
197
  # - Plant in an office/lab setting
198
  # - Field photo with people in background
199
+ # ══════════════════════════════════════════════════════════════
200
+ # PRIORITY 0 β€” HUMAN VETO. Always wins over any vegetation signal.
201
+ # Skin pixels overlap the "brown vegetation" hue range, so a selfie
202
+ # would otherwise pass D1b. We reject FIRST if a face is clearly
203
+ # present OR skin dominates without a real green leaf blob.
204
+ # ══════════════════════════════════════════════════════════════
205
+ if face_count > 0 and face_area_pct > 0.01 and not has_plant_region:
206
+ return {
207
+ "is_plant": False,
208
+ "reason": (
209
+ f"Human face detected ({face_count} face{'s' if face_count > 1 else ''}, "
210
+ f"covering {face_area_pct:.0%} of the image) without a visible crop leaf. "
211
+ "Please upload a close-up photo of a crop leaf, not a person."
212
+ ),
213
+ "face_count": face_count, "face_area_pct": face_area_pct,
214
+ "skin_ratio": skin_ratio, "green_ratio": green_ratio,
215
+ "brown_ratio": brown_ratio, "achromatic_ratio": achromatic_ratio,
216
+ "largest_green_blob_ratio": largest_green_blob_ratio,
217
+ "edge_density": edge_density,
218
+ }
219
+ if skin_ratio > 0.20 and largest_green_blob_ratio < 0.02:
220
+ return {
221
+ "is_plant": False,
222
+ "reason": (
223
+ f"Person/skin detected ({skin_ratio:.0%} skin pixels) without a crop leaf region. "
224
+ "Please upload a close-up photo of a crop leaf."
225
+ ),
226
+ "face_count": face_count, "face_area_pct": face_area_pct,
227
+ "skin_ratio": skin_ratio, "green_ratio": green_ratio,
228
+ "brown_ratio": brown_ratio, "achromatic_ratio": achromatic_ratio,
229
+ "largest_green_blob_ratio": largest_green_blob_ratio,
230
+ "edge_density": edge_density,
231
+ }
232
+
233
  if has_plant_region:
234
  is_plant = True
235
  reason = ""
src/agrianalyze/core/detector.py CHANGED
@@ -92,8 +92,18 @@ def predict_with_uncertainty(
92
  if not results or results[0].probs is None:
93
  continue
94
  probs = results[0].probs
95
- top_idx = probs.top1
96
- top_conf = float(probs.top1conf)
 
 
 
 
 
 
 
 
 
 
97
  class_key = names[top_idx]
98
  confidences.append(top_conf)
99
  predictions.append(class_key)
 
92
  if not results or results[0].probs is None:
93
  continue
94
  probs = results[0].probs
95
+ # In MC-Dropout (train mode) ultralytics may return un-normalized
96
+ # logits via .top1conf. Re-normalize via softmax over .data so
97
+ # confidences stay in [0, 1].
98
+ raw = probs.data
99
+ if hasattr(raw, "float"):
100
+ raw = raw.float()
101
+ # softmax across the class dimension
102
+ soft = torch.softmax(raw, dim=-1)
103
+ top_idx = int(torch.argmax(soft).item())
104
+ top_conf = float(soft[top_idx].item())
105
+ # Hard clamp as a final safety net
106
+ top_conf = max(0.0, min(1.0, top_conf))
107
  class_key = names[top_idx]
108
  confidences.append(top_conf)
109
  predictions.append(class_key)