aaronthomas123 commited on
Commit
eda868e
·
1 Parent(s): e335b6f

Reject non-leaf uploads in predictions

Browse files
Files changed (1) hide show
  1. api.py +40 -3
api.py CHANGED
@@ -134,8 +134,31 @@ def load_model():
134
  def preprocess_image(image_bytes: bytes) -> np.ndarray:
135
  image = Image.open(BytesIO(image_bytes)).convert("RGB")
136
  image = image.resize((224, 224))
137
- img_array = np.array(image, dtype=np.float32) / 255.0
138
- return np.expand_dims(img_array, axis=0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
 
141
  MODEL = load_model()
@@ -154,7 +177,7 @@ async def predict(file: UploadFile = File(...)) -> dict:
154
 
155
  try:
156
  image_bytes = await file.read()
157
- input_tensor = preprocess_image(image_bytes)
158
  preds = MODEL.predict(input_tensor)
159
  probs = np.asarray(preds[0], dtype=np.float32)
160
 
@@ -176,6 +199,19 @@ async def predict(file: UploadFile = File(...)) -> dict:
176
  "confidence": float(probs[idx]),
177
  })
178
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  return {
180
  "prediction": {
181
  "class_index": result_idx,
@@ -183,6 +219,7 @@ async def predict(file: UploadFile = File(...)) -> dict:
183
  "confidence": confidence,
184
  },
185
  "top3": top3,
 
186
  }
187
  except HTTPException:
188
  raise
 
134
  def preprocess_image(image_bytes: bytes) -> np.ndarray:
135
  image = Image.open(BytesIO(image_bytes)).convert("RGB")
136
  image = image.resize((224, 224))
137
+ rgb_uint8 = np.array(image, dtype=np.uint8)
138
+ img_array = rgb_uint8.astype(np.float32) / 255.0
139
+ return np.expand_dims(img_array, axis=0), rgb_uint8
140
+
141
+
142
+ def should_reject_as_no_leaf(rgb_uint8: np.ndarray, probs: np.ndarray) -> tuple[bool, str]:
143
+ confidence = float(np.max(probs))
144
+ sorted_probs = np.sort(probs)
145
+ second_best = float(sorted_probs[-2]) if sorted_probs.size > 1 else 0.0
146
+ margin = confidence - second_best
147
+
148
+ r = rgb_uint8[:, :, 0].astype(np.float32)
149
+ g = rgb_uint8[:, :, 1].astype(np.float32)
150
+ b = rgb_uint8[:, :, 2].astype(np.float32)
151
+
152
+ green_mask = (g > 40) & (g > 1.05 * r) & (g > 1.05 * b)
153
+ green_ratio = float(np.mean(green_mask))
154
+
155
+ if confidence < 0.55:
156
+ return True, "low_confidence"
157
+ if margin < 0.12 and confidence < 0.70:
158
+ return True, "ambiguous_prediction"
159
+ if green_ratio < 0.05 and confidence < 0.80:
160
+ return True, "non_leaf_visual_pattern"
161
+ return False, "accepted"
162
 
163
 
164
  MODEL = load_model()
 
177
 
178
  try:
179
  image_bytes = await file.read()
180
+ input_tensor, rgb_uint8 = preprocess_image(image_bytes)
181
  preds = MODEL.predict(input_tensor)
182
  probs = np.asarray(preds[0], dtype=np.float32)
183
 
 
199
  "confidence": float(probs[idx]),
200
  })
201
 
202
+ reject, reject_reason = should_reject_as_no_leaf(rgb_uint8, probs)
203
+ if reject:
204
+ return {
205
+ "prediction": {
206
+ "class_index": -1,
207
+ "class_name": "No leaf detected",
208
+ "confidence": float(1.0 - confidence),
209
+ },
210
+ "top3": top3,
211
+ "rejected": True,
212
+ "reject_reason": reject_reason,
213
+ }
214
+
215
  return {
216
  "prediction": {
217
  "class_index": result_idx,
 
219
  "confidence": confidence,
220
  },
221
  "top3": top3,
222
+ "rejected": False,
223
  }
224
  except HTTPException:
225
  raise