crtal commited on
Commit
a6e1cbc
·
1 Parent(s): 1d971c4

Robustness Update: Added confidence thresholding, file type validation, and error handling

Browse files
Files changed (2) hide show
  1. app.py +54 -27
  2. deploy/app.py +54 -27
app.py CHANGED
@@ -1,4 +1,4 @@
1
- from fastapi import FastAPI, File, UploadFile
2
  import torch
3
  import torch.nn as nn
4
  from torchvision import models, transforms
@@ -16,9 +16,12 @@ num_ftrs = model.head.in_features
16
  model.head = nn.Linear(num_ftrs, 2)
17
 
18
  # Load Swin weights
19
- model.load_state_dict(torch.load("models/spoilage_model_swin_finetuned.pth", map_location=device))
20
- model = model.to(device)
21
- model.eval()
 
 
 
22
 
23
  # Preprocessing transforms
24
  preprocess = transforms.Compose([
@@ -29,35 +32,59 @@ preprocess = transforms.Compose([
29
  ])
30
 
31
  CLASS_NAMES = ["fresh", "spoiled"]
 
32
 
33
  @app.post("/predict")
34
  async def predict(file: UploadFile = File(...)):
35
- # Read image
36
- contents = await file.read()
37
- image = Image.open(io.BytesIO(contents)).convert("RGB")
38
-
39
- # Preprocess
40
- input_tensor = preprocess(image)
41
- input_batch = input_tensor.unsqueeze(0).to(device)
42
-
43
- # Inference
44
- with torch.no_grad():
45
- outputs = model(input_batch)
46
- probabilities = torch.nn.functional.softmax(outputs[0], dim=0)
47
- confidence, index = torch.max(probabilities, 0)
48
-
49
- result = {
50
- "prediction": CLASS_NAMES[index.item()],
51
- "confidence": float(confidence.item()),
52
- "status": "success",
53
- "model": "swin_transformer_tiny"
54
- }
55
-
56
- return result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
  @app.get("/")
59
  def read_root():
60
- return {"message": "Food Spoilage Detection API (Swin) is running. Use /predict to send an image."}
 
 
 
 
61
 
62
  if __name__ == "__main__":
63
  import uvicorn
 
1
+ from fastapi import FastAPI, File, UploadFile, HTTPException
2
  import torch
3
  import torch.nn as nn
4
  from torchvision import models, transforms
 
16
  model.head = nn.Linear(num_ftrs, 2)
17
 
18
  # Load Swin weights
19
+ try:
20
+ model.load_state_dict(torch.load("models/spoilage_model_swin_finetuned.pth", map_location=device))
21
+ model = model.to(device)
22
+ model.eval()
23
+ except Exception as e:
24
+ print(f"Error loading model: {e}")
25
 
26
  # Preprocessing transforms
27
  preprocess = transforms.Compose([
 
32
  ])
33
 
34
  CLASS_NAMES = ["fresh", "spoiled"]
35
+ ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg"}
36
 
37
  @app.post("/predict")
38
  async def predict(file: UploadFile = File(...)):
39
+ # 1. Validate File Extension
40
+ extension = file.filename.split(".")[-1].lower()
41
+ if extension not in ALLOWED_EXTENSIONS:
42
+ raise HTTPException(status_code=400, detail=f"Invalid file type. Allowed: {ALLOWED_EXTENSIONS}")
43
+
44
+ try:
45
+ # 2. Read and Validate Image
46
+ contents = await file.read()
47
+ image = Image.open(io.BytesIO(contents)).convert("RGB")
48
+
49
+ # 3. Preprocess
50
+ input_tensor = preprocess(image)
51
+ input_batch = input_tensor.unsqueeze(0).to(device)
52
+
53
+ # 4. Inference
54
+ with torch.no_grad():
55
+ outputs = model(input_batch)
56
+ probabilities = torch.nn.functional.softmax(outputs[0], dim=0)
57
+ confidence, index = torch.max(probabilities, 0)
58
+
59
+ conf_score = float(confidence.item())
60
+
61
+ # 5. Robustness Check: Confidence Threshold
62
+ # If the model is not confident enough, it might not be a food image
63
+ if conf_score < 0.70:
64
+ return {
65
+ "status": "ambiguous",
66
+ "message": "Model confidence too low. The image might not be a recognized food item or is too low quality.",
67
+ "confidence": conf_score,
68
+ "suggestion": "Please provide a clearer image of a fruit or vegetable."
69
+ }
70
+
71
+ return {
72
+ "prediction": CLASS_NAMES[index.item()],
73
+ "confidence": conf_score,
74
+ "status": "success",
75
+ "model": "swin_transformer_tiny_finetuned"
76
+ }
77
+
78
+ except Exception as e:
79
+ raise HTTPException(status_code=500, detail=f"Error processing image: {str(e)}")
80
 
81
  @app.get("/")
82
  def read_root():
83
+ return {
84
+ "message": "Food Spoilage Detection API is running.",
85
+ "usage": "POST an image to /predict",
86
+ "supported_classes": CLASS_NAMES
87
+ }
88
 
89
  if __name__ == "__main__":
90
  import uvicorn
deploy/app.py CHANGED
@@ -1,4 +1,4 @@
1
- from fastapi import FastAPI, File, UploadFile
2
  import torch
3
  import torch.nn as nn
4
  from torchvision import models, transforms
@@ -16,9 +16,12 @@ num_ftrs = model.head.in_features
16
  model.head = nn.Linear(num_ftrs, 2)
17
 
18
  # Load Swin weights
19
- model.load_state_dict(torch.load("models/spoilage_model_swin_finetuned.pth", map_location=device))
20
- model = model.to(device)
21
- model.eval()
 
 
 
22
 
23
  # Preprocessing transforms
24
  preprocess = transforms.Compose([
@@ -29,35 +32,59 @@ preprocess = transforms.Compose([
29
  ])
30
 
31
  CLASS_NAMES = ["fresh", "spoiled"]
 
32
 
33
  @app.post("/predict")
34
  async def predict(file: UploadFile = File(...)):
35
- # Read image
36
- contents = await file.read()
37
- image = Image.open(io.BytesIO(contents)).convert("RGB")
38
-
39
- # Preprocess
40
- input_tensor = preprocess(image)
41
- input_batch = input_tensor.unsqueeze(0).to(device)
42
-
43
- # Inference
44
- with torch.no_grad():
45
- outputs = model(input_batch)
46
- probabilities = torch.nn.functional.softmax(outputs[0], dim=0)
47
- confidence, index = torch.max(probabilities, 0)
48
-
49
- result = {
50
- "prediction": CLASS_NAMES[index.item()],
51
- "confidence": float(confidence.item()),
52
- "status": "success",
53
- "model": "swin_transformer_tiny"
54
- }
55
-
56
- return result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
  @app.get("/")
59
  def read_root():
60
- return {"message": "Food Spoilage Detection API (Swin) is running. Use /predict to send an image."}
 
 
 
 
61
 
62
  if __name__ == "__main__":
63
  import uvicorn
 
1
+ from fastapi import FastAPI, File, UploadFile, HTTPException
2
  import torch
3
  import torch.nn as nn
4
  from torchvision import models, transforms
 
16
  model.head = nn.Linear(num_ftrs, 2)
17
 
18
  # Load Swin weights
19
+ try:
20
+ model.load_state_dict(torch.load("models/spoilage_model_swin_finetuned.pth", map_location=device))
21
+ model = model.to(device)
22
+ model.eval()
23
+ except Exception as e:
24
+ print(f"Error loading model: {e}")
25
 
26
  # Preprocessing transforms
27
  preprocess = transforms.Compose([
 
32
  ])
33
 
34
  CLASS_NAMES = ["fresh", "spoiled"]
35
+ ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg"}
36
 
37
  @app.post("/predict")
38
  async def predict(file: UploadFile = File(...)):
39
+ # 1. Validate File Extension
40
+ extension = file.filename.split(".")[-1].lower()
41
+ if extension not in ALLOWED_EXTENSIONS:
42
+ raise HTTPException(status_code=400, detail=f"Invalid file type. Allowed: {ALLOWED_EXTENSIONS}")
43
+
44
+ try:
45
+ # 2. Read and Validate Image
46
+ contents = await file.read()
47
+ image = Image.open(io.BytesIO(contents)).convert("RGB")
48
+
49
+ # 3. Preprocess
50
+ input_tensor = preprocess(image)
51
+ input_batch = input_tensor.unsqueeze(0).to(device)
52
+
53
+ # 4. Inference
54
+ with torch.no_grad():
55
+ outputs = model(input_batch)
56
+ probabilities = torch.nn.functional.softmax(outputs[0], dim=0)
57
+ confidence, index = torch.max(probabilities, 0)
58
+
59
+ conf_score = float(confidence.item())
60
+
61
+ # 5. Robustness Check: Confidence Threshold
62
+ # If the model is not confident enough, it might not be a food image
63
+ if conf_score < 0.70:
64
+ return {
65
+ "status": "ambiguous",
66
+ "message": "Model confidence too low. The image might not be a recognized food item or is too low quality.",
67
+ "confidence": conf_score,
68
+ "suggestion": "Please provide a clearer image of a fruit or vegetable."
69
+ }
70
+
71
+ return {
72
+ "prediction": CLASS_NAMES[index.item()],
73
+ "confidence": conf_score,
74
+ "status": "success",
75
+ "model": "swin_transformer_tiny_finetuned"
76
+ }
77
+
78
+ except Exception as e:
79
+ raise HTTPException(status_code=500, detail=f"Error processing image: {str(e)}")
80
 
81
  @app.get("/")
82
  def read_root():
83
+ return {
84
+ "message": "Food Spoilage Detection API is running.",
85
+ "usage": "POST an image to /predict",
86
+ "supported_classes": CLASS_NAMES
87
+ }
88
 
89
  if __name__ == "__main__":
90
  import uvicorn