Spaces:
Running
Running
Commit Β·
93ac3cd
1
Parent(s): 46e04d2
Relaxed Image validation limits for mobile photos
Browse files
main.py
CHANGED
|
@@ -219,9 +219,9 @@ async def predict(file: UploadFile = File(...)) -> dict:
|
|
| 219 |
# ββ 8. Validate confidence threshold βββββββββββββββββββββββββββββββββββ
|
| 220 |
top_confidence = predictions[0]["confidence"]
|
| 221 |
|
| 222 |
-
# Hard reject only truly uncertain predictions (lowered from 25
|
| 223 |
-
# because genuine skin photos of mild or early conditions can score low)
|
| 224 |
-
if top_confidence <
|
| 225 |
raise HTTPException(
|
| 226 |
status_code=422,
|
| 227 |
detail={
|
|
|
|
| 219 |
# ββ 8. Validate confidence threshold βββββββββββββββββββββββββββββββββββ
|
| 220 |
top_confidence = predictions[0]["confidence"]
|
| 221 |
|
| 222 |
+
# Hard reject only truly uncertain predictions (lowered from 25 -> 15 -> 5 %
|
| 223 |
+
# because genuine skin photos of mild or early conditions can score low, especially on smartphones)
|
| 224 |
+
if top_confidence < 5.0:
|
| 225 |
raise HTTPException(
|
| 226 |
status_code=422,
|
| 227 |
detail={
|
model.py
CHANGED
|
@@ -288,7 +288,7 @@ def is_skin_image(image_array: np.ndarray) -> dict:
|
|
| 288 |
skin_pixels = int(cv2.countNonZero(skin_mask))
|
| 289 |
skin_pct = (skin_pixels / total_pixels) * 100
|
| 290 |
|
| 291 |
-
MIN_SKIN_PCT =
|
| 292 |
|
| 293 |
if skin_pct < MIN_SKIN_PCT:
|
| 294 |
return {
|
|
@@ -329,7 +329,7 @@ def is_skin_image(image_array: np.ndarray) -> dict:
|
|
| 329 |
|
| 330 |
# The largest blob must cover β₯ 8 % of the total image
|
| 331 |
largest_blob_pct = (max(large_blobs) / total_pixels) * 100
|
| 332 |
-
if largest_blob_pct <
|
| 333 |
return {
|
| 334 |
"is_skin": False,
|
| 335 |
"skin_percentage": round(skin_pct, 1),
|
|
@@ -348,8 +348,8 @@ def is_skin_image(image_array: np.ndarray) -> dict:
|
|
| 348 |
|
| 349 |
if sat_skin.size > 0:
|
| 350 |
sat_std = float(np.std(sat_skin))
|
| 351 |
-
# Paint / metal usually has sat_std < 18;
|
| 352 |
-
if sat_std <
|
| 353 |
return {
|
| 354 |
"is_skin": False,
|
| 355 |
"skin_percentage": round(skin_pct, 1),
|
|
@@ -368,8 +368,8 @@ def is_skin_image(image_array: np.ndarray) -> dict:
|
|
| 368 |
|
| 369 |
if skin_pixels > 0:
|
| 370 |
edge_skin_ratio = edge_count / skin_pixels
|
| 371 |
-
#
|
| 372 |
-
if edge_skin_ratio >
|
| 373 |
return {
|
| 374 |
"is_skin": False,
|
| 375 |
"skin_percentage": round(skin_pct, 1),
|
|
@@ -409,10 +409,9 @@ def check_image_quality(image_array: np.ndarray) -> dict:
|
|
| 409 |
gray = cv2.cvtColor(image_array, cv2.COLOR_RGB2GRAY)
|
| 410 |
lap_var = float(cv2.Laplacian(gray, cv2.CV_64F).var())
|
| 411 |
|
| 412 |
-
# Threshold
|
| 413 |
-
#
|
| 414 |
-
|
| 415 |
-
BLUR_THRESHOLD = 20.0
|
| 416 |
|
| 417 |
if lap_var < BLUR_THRESHOLD:
|
| 418 |
return {
|
|
|
|
| 288 |
skin_pixels = int(cv2.countNonZero(skin_mask))
|
| 289 |
skin_pct = (skin_pixels / total_pixels) * 100
|
| 290 |
|
| 291 |
+
MIN_SKIN_PCT = 3.0 # dramatically lowered to allow partial/small skin patches or phone crops
|
| 292 |
|
| 293 |
if skin_pct < MIN_SKIN_PCT:
|
| 294 |
return {
|
|
|
|
| 329 |
|
| 330 |
# The largest blob must cover β₯ 8 % of the total image
|
| 331 |
largest_blob_pct = (max(large_blobs) / total_pixels) * 100
|
| 332 |
+
if largest_blob_pct < 3.0:
|
| 333 |
return {
|
| 334 |
"is_skin": False,
|
| 335 |
"skin_percentage": round(skin_pct, 1),
|
|
|
|
| 348 |
|
| 349 |
if sat_skin.size > 0:
|
| 350 |
sat_std = float(np.std(sat_skin))
|
| 351 |
+
# Paint / metal usually has sat_std < 18; phone images can be heavily compressed and lose variance
|
| 352 |
+
if sat_std < 5.0:
|
| 353 |
return {
|
| 354 |
"is_skin": False,
|
| 355 |
"skin_percentage": round(skin_pct, 1),
|
|
|
|
| 368 |
|
| 369 |
if skin_pixels > 0:
|
| 370 |
edge_skin_ratio = edge_count / skin_pixels
|
| 371 |
+
# Relaxed edge density check to accommodate textured or noisy images
|
| 372 |
+
if edge_skin_ratio > 1.5:
|
| 373 |
return {
|
| 374 |
"is_skin": False,
|
| 375 |
"skin_percentage": round(skin_pct, 1),
|
|
|
|
| 409 |
gray = cv2.cvtColor(image_array, cv2.COLOR_RGB2GRAY)
|
| 410 |
lap_var = float(cv2.Laplacian(gray, cv2.CV_64F).var())
|
| 411 |
|
| 412 |
+
# Threshold lowered drastically because smartphone images often exhibit close-up focus blur
|
| 413 |
+
# or compression artifacts which lower variance significantly.
|
| 414 |
+
BLUR_THRESHOLD = 5.0
|
|
|
|
| 415 |
|
| 416 |
if lap_var < BLUR_THRESHOLD:
|
| 417 |
return {
|