| """ |
| Intel Scene Classifier β Flask App |
| """ |
|
|
| import io |
| import os |
| import numpy as np |
| from flask import Flask, jsonify, render_template, request |
| from PIL import Image |
|
|
| app = Flask(__name__) |
|
|
| CLASSES = ["buildings", "forest", "glacier", "mountain", "sea", "street"] |
| IMG_SIZE = 150 |
|
|
| _pytorch_model = None |
| _tf_model = None |
|
|
|
|
| |
| def load_pytorch(): |
| global _pytorch_model |
| if _pytorch_model is not None: |
| return _pytorch_model |
|
|
| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
| from torchvision import transforms |
|
|
| class CNN_Torch(nn.Module): |
| def __init__(self, num_classes=6): |
| super().__init__() |
| self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1) |
| self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1) |
| self.conv3 = nn.Conv2d(64, 128, kernel_size=3, padding=1) |
| self.conv3_drop = nn.Dropout2d(p=0.25) |
| self.pool = nn.MaxPool2d(2, 2) |
| self.fc1 = nn.Linear(128 * 18 * 18, 256) |
| self.fc2 = nn.Linear(256, num_classes) |
|
|
| def forward(self, x): |
| x = self.pool(F.relu(self.conv1(x))) |
| x = self.pool(F.relu(self.conv2(x))) |
| x = self.pool(F.relu(self.conv3_drop(self.conv3(x)))) |
| x = x.view(-1, 128 * 18 * 18) |
| x = F.relu(self.fc1(x)) |
| x = F.dropout(x, training=self.training) |
| x = self.fc2(x) |
| return F.log_softmax(x, dim=1) |
|
|
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
| model = CNN_Torch(6).to(device) |
| model.load_state_dict(torch.load("parfait_model.pth", map_location=device)) |
| model.eval() |
|
|
| tf = transforms.Compose([ |
| transforms.Resize((IMG_SIZE, IMG_SIZE)), |
| transforms.ToTensor(), |
| transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), |
| ]) |
| _pytorch_model = (model, device, tf) |
| return _pytorch_model |
|
|
|
|
| def load_tensorflow(): |
| global _tf_model |
| if _tf_model is None: |
| import tensorflow as tf |
| _tf_model = tf.keras.models.load_model("parfait_model.keras") |
| return _tf_model |
|
|
|
|
| |
| @app.route("/") |
| def index(): |
| return render_template("index.html") |
|
|
|
|
| @app.route("/predict", methods=["POST"]) |
| def predict(): |
| if "image" not in request.files: |
| return jsonify({"error": "Aucune image fournie"}), 400 |
|
|
| framework = request.form.get("model", "pytorch") |
|
|
| try: |
| pil_img = Image.open(io.BytesIO(request.files["image"].read())).convert("RGB") |
| except Exception: |
| return jsonify({"error": "Fichier image invalide"}), 400 |
|
|
| try: |
| if framework == "pytorch": |
| import torch |
| model, device, tf = load_pytorch() |
| tensor = tf(pil_img).unsqueeze(0).to(device) |
| with torch.no_grad(): |
| out = model(tensor) |
| probs = torch.exp(out).cpu().numpy()[0] |
| else: |
| model = load_tensorflow() |
| arr = np.array(pil_img.resize((IMG_SIZE, IMG_SIZE)), dtype=np.float32) |
| probs = model.predict(np.expand_dims(arr, 0), verbose=0)[0] |
|
|
| pred_idx = int(np.argmax(probs)) |
| return jsonify({ |
| "class": CLASSES[pred_idx], |
| "confidence": float(probs[pred_idx]), |
| "probabilities": {c: float(p) for c, p in zip(CLASSES, probs)}, |
| }) |
|
|
| except FileNotFoundError as e: |
| return jsonify({"error": f"Modèle introuvable : {e}. Placez les fichiers .pth et .keras à la racine."}), 500 |
| except Exception as e: |
| return jsonify({"error": str(e)}), 500 |
|
|
|
|
| if __name__ == "__main__": |
| port = int(os.environ.get("PORT", 5000)) |
| app.run(host="0.0.0.0", port=port, debug=False) |
|
|