Calamar49 commited on
Commit
358074b
·
1 Parent(s): c5f9aa2

final APÍ REST

Browse files
Files changed (4) hide show
  1. Dockerfile +0 -1
  2. README.md +5 -8
  3. app.py +17 -32
  4. requirements.txt +2 -4
Dockerfile CHANGED
@@ -5,5 +5,4 @@ COPY ./requirements.txt /code/requirements.txt
5
  RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
6
  COPY . /code
7
  EXPOSE 7860
8
- # ¡ESTA LÍNEA ES CRUCIAL! Debe usar uvicorn.
9
  CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
 
5
  RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
6
  COPY . /code
7
  EXPOSE 7860
 
8
  CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,10 +1,7 @@
1
  ---
2
- title: Clasificador de Orquídeas
3
- emoji: 🌸
4
- colorFrom: green
5
- colorTo: purple
6
- sdk: docker # <-- ¡MUY IMPORTANTE! Debe ser 'docker'.
7
- app_file: app.py # <-- Le dice a Docker qué archivo es el principal.
8
- app_port: 7860 # <-- Le dice a Docker en qué puerto buscar.
9
- license: mit
10
  ---
 
1
  ---
2
+ title: Orchid Classifier API
3
+ emoji: 🤖
4
+ sdk: docker
5
+ app_file: app.py
6
+ app_port: 7860
 
 
 
7
  ---
app.py CHANGED
@@ -1,11 +1,10 @@
1
- # app.py (versión final con Gradio para web y FastAPI para móvil)
2
 
3
  import torch
4
  import torchvision.transforms as transforms
5
  from PIL import Image
6
  import json
7
  import timm
8
- import gradio as gr
9
  from fastapi import FastAPI, UploadFile, File
10
  from fastapi.responses import JSONResponse
11
  import io
@@ -16,17 +15,17 @@ from VisionEnsembleModel import VisionEnsembleModel
16
  # --- 2. Carga del Modelo y Componentes ---
17
  device = torch.device("cpu")
18
  MODEL_PATH = "model/best_vision_ensemble_model.pth"
19
- LABELS_PATH = "model/species_labels_map.json"
20
  NUM_CLASSES = 156
21
 
22
- with open(LABELS_PATH) as f:
23
- labels_map = json.load(f)
 
24
 
25
  model = VisionEnsembleModel(num_classes=NUM_CLASSES)
26
  model.load_state_dict(torch.load(MODEL_PATH, map_location=device))
27
  model.to(device)
28
  model.eval()
29
- print("Modelo Ensamblado Híbrido cargado.")
30
 
31
  transforms_val = transforms.Compose([
32
  transforms.Resize((224, 224)),
@@ -34,29 +33,14 @@ transforms_val = transforms.Compose([
34
  transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
35
  ])
36
 
37
- # --- 3. Función de Predicción Interna ---
38
- def make_prediction(image_pil):
39
- input_tensor = transforms_val(image_pil).unsqueeze(0).to(device)
40
- with torch.no_grad():
41
- output = model(input_tensor)
42
- probabilities = torch.nn.functional.softmax(output[0], dim=0)
43
- return probabilities
44
-
45
- # --- 4. Función para la Interfaz de Gradio (devuelve nombres) ---
46
- def predict_for_gradio(image_numpy):
47
- pil_image = Image.fromarray(image_numpy.astype('uint8'), 'RGB')
48
- probabilities = make_prediction(pil_image)
49
- top5_prob, top5_catid = torch.topk(probabilities, 5)
50
- confidences = {labels_map.get(str(cat_id.item()), "Desconocido"): prob.item() for prob, cat_id in zip(top5_prob, top5_catid)}
51
- return confidences
52
-
53
- # --- 5. Crear la Interfaz de Gradio ---
54
- gradio_interface = gr.Interface(fn=predict_for_gradio, inputs=gr.Image(type="numpy"), outputs=gr.Label(num_top_classes=5), title="Clasificador de Orquídeas")
55
 
56
- # --- 6. Crear la App FastAPI ---
57
- app = FastAPI()
 
58
 
59
- # --- 7. Endpoint de API para la App Móvil (devuelve IDs) ---
60
  @app.post("/predict_for_mobile")
61
  async def predict_for_mobile(file: UploadFile = File(...)):
62
  image_bytes = await file.read()
@@ -65,11 +49,12 @@ async def predict_for_mobile(file: UploadFile = File(...)):
65
  except Exception:
66
  return JSONResponse(status_code=400, content={"error": "Archivo de imagen inválido."})
67
 
68
- probabilities = make_prediction(pil_image)
 
 
 
 
69
  top5_prob, top_catid = torch.topk(probabilities, 5)
70
 
71
  results = [{"species_id": cat_id.item(), "confidence": prob.item()} for prob, cat_id in zip(top5_prob, top_catid)]
72
- return JSONResponse(content={"predictions": results})
73
-
74
- # --- 8. Montar Gradio en la App FastAPI ---
75
- app = gr.mount_gradio_app(app, gradio_interface, path="/")
 
1
+ # app.py (Versión final - Solo API FastAPI)
2
 
3
  import torch
4
  import torchvision.transforms as transforms
5
  from PIL import Image
6
  import json
7
  import timm
 
8
  from fastapi import FastAPI, UploadFile, File
9
  from fastapi.responses import JSONResponse
10
  import io
 
15
  # --- 2. Carga del Modelo y Componentes ---
16
  device = torch.device("cpu")
17
  MODEL_PATH = "model/best_vision_ensemble_model.pth"
 
18
  NUM_CLASSES = 156
19
 
20
+ # No necesitamos el mapa de etiquetas en el servidor, ya que la app lo manejará
21
+ # with open(LABELS_PATH) as f:
22
+ # labels_map = json.load(f)
23
 
24
  model = VisionEnsembleModel(num_classes=NUM_CLASSES)
25
  model.load_state_dict(torch.load(MODEL_PATH, map_location=device))
26
  model.to(device)
27
  model.eval()
28
+ print("Modelo Ensamblado Híbrido cargado y listo para servir la API.")
29
 
30
  transforms_val = transforms.Compose([
31
  transforms.Resize((224, 224)),
 
33
  transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
34
  ])
35
 
36
+ # --- 3. Crear la App FastAPI ---
37
+ app = FastAPI(title="API de Clasificación de Orquídeas")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
+ @app.get("/")
40
+ def read_root():
41
+ return {"status": "ok", "message": "API de Orquídeas funcionando. Usa el endpoint /predict_for_mobile"}
42
 
43
+ # --- 4. Endpoint de API para la App Móvil (devuelve IDs) ---
44
  @app.post("/predict_for_mobile")
45
  async def predict_for_mobile(file: UploadFile = File(...)):
46
  image_bytes = await file.read()
 
49
  except Exception:
50
  return JSONResponse(status_code=400, content={"error": "Archivo de imagen inválido."})
51
 
52
+ input_tensor = transforms_val(pil_image).unsqueeze(0).to(device)
53
+ with torch.no_grad():
54
+ output = model(input_tensor)
55
+ probabilities = torch.nn.functional.softmax(output[0], dim=0)
56
+
57
  top5_prob, top_catid = torch.topk(probabilities, 5)
58
 
59
  results = [{"species_id": cat_id.item(), "confidence": prob.item()} for prob, cat_id in zip(top5_prob, top_catid)]
60
+ return JSONResponse(content={"predictions": results})
 
 
 
requirements.txt CHANGED
@@ -1,10 +1,8 @@
1
- # Contenido de: requirements.txt
2
  fastapi
3
  uvicorn[standard]
4
  python-multipart
5
  torch
6
  torchvision
7
  timm
8
- Pillow
9
- scikit-learn
10
- gradio
 
1
+ # requirements.txt
2
  fastapi
3
  uvicorn[standard]
4
  python-multipart
5
  torch
6
  torchvision
7
  timm
8
+ Pillow