cyberai-1 commited on
Commit
fd654aa
Β·
1 Parent(s): 833f32e
Files changed (1) hide show
  1. app.py +42 -42
app.py CHANGED
@@ -15,53 +15,36 @@ from torchvision import transforms
15
 
16
  app = Flask(__name__)
17
 
18
- CLASSES = ["buildings", "forest", "glacier", "mountain", "sea", "street"]
19
  IMG_SIZE = 150
20
 
21
  _pytorch_model = None
22
- _tf_model = None
23
-
24
-
25
- # ── Loaders ────────────────────────────────────────────────────────────────────
26
- def load_pytorch():
27
- global _pytorch_model
28
- if _pytorch_model is not None:
29
- return _pytorch_model
30
 
31
 
32
  class CNN_Torch(nn.Module):
33
  """
34
- CNN PyTorch allΓ©gΓ© pour images RGB (3 canaux).
35
- EntrΓ©e : (B, 3, 150, 150)
36
- Sortie : (B, num_classes) β€” log-softmax
37
-
38
- Architecture :
39
- Block 1 : Conv2d(3β†’32) + BN + ReLU + MaxPool2d(2) β†’ 75Γ—75
40
- Block 2 : Conv2d(32β†’64) + BN + ReLU + MaxPool2d(2) + Drop2d β†’ 37Γ—37
41
- Block 3 : Conv2d(64β†’128)+ BN + ReLU + MaxPool2d(2) + Drop2d β†’ 18Γ—18
42
- GAP : AdaptiveAvgPool2d(1) β†’ (B,128)
43
- Head : Linear(128β†’256) + ReLU + Dropout + Linear(256β†’C)
44
-
45
- Paramètre `dropout` contrôlé depuis l'extérieur → utilisé dans le CV.
46
  """
47
  def __init__(self, num_classes: int = 6, dropout: float = 0.5):
48
  super().__init__()
49
 
50
  self.features = nn.Sequential(
51
- # Block 1 β€” 150 β†’ 75
52
  nn.Conv2d(3, 32, kernel_size=3, padding=1, bias=False),
53
  nn.BatchNorm2d(32),
54
  nn.ReLU(inplace=True),
55
  nn.MaxPool2d(2),
56
 
57
- # Block 2 β€” 75 β†’ 37
58
  nn.Conv2d(32, 64, kernel_size=3, padding=1, bias=False),
59
  nn.BatchNorm2d(64),
60
  nn.ReLU(inplace=True),
61
  nn.MaxPool2d(2),
62
  nn.Dropout2d(0.1),
63
 
64
- # Block 3 β€” 37 β†’ 18
65
  nn.Conv2d(64, 128, kernel_size=3, padding=1, bias=False),
66
  nn.BatchNorm2d(128),
67
  nn.ReLU(inplace=True),
@@ -69,7 +52,7 @@ class CNN_Torch(nn.Module):
69
  nn.Dropout2d(0.2),
70
  )
71
 
72
- self.gap = nn.AdaptiveAvgPool2d(1) # (B, 128, 18, 18) β†’ (B, 128, 1, 1)
73
 
74
  self.classifier = nn.Sequential(
75
  nn.Flatten(),
@@ -85,17 +68,29 @@ class CNN_Torch(nn.Module):
85
  x = self.classifier(x)
86
  return F.log_softmax(x, dim=1)
87
 
 
 
 
 
 
 
88
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
89
- model = CNN_Torch(6).to(device)
90
- model.load_state_dict(torch.load("parfait_model.pth", map_location=device))
 
 
91
  model.eval()
92
 
93
- tf = transforms.Compose([
94
  transforms.Resize((IMG_SIZE, IMG_SIZE)),
95
  transforms.ToTensor(),
96
- transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
 
 
 
97
  ])
98
- _pytorch_model = (model, device, tf)
 
99
  return _pytorch_model
100
 
101
 
@@ -107,7 +102,6 @@ def load_tensorflow():
107
  return _tf_model
108
 
109
 
110
- # ── Routes ─────────────────────────────────────────────────────────────────────
111
  @app.route("/")
112
  def index():
113
  return render_template("index.html")
@@ -127,30 +121,36 @@ def predict():
127
 
128
  try:
129
  if framework == "pytorch":
130
- import torch
131
- model, device, tf = load_pytorch()
132
- tensor = tf(pil_img).unsqueeze(0).to(device)
133
  with torch.no_grad():
134
- out = model(tensor)
135
  probs = torch.exp(out).cpu().numpy()[0]
 
136
  else:
137
  model = load_tensorflow()
138
- arr = np.array(pil_img.resize((IMG_SIZE, IMG_SIZE)), dtype=np.float32)
139
- probs = model.predict(np.expand_dims(arr, 0), verbose=0)[0]
 
140
 
141
  pred_idx = int(np.argmax(probs))
142
  return jsonify({
143
- "class": CLASSES[pred_idx],
144
- "confidence": float(probs[pred_idx]),
145
- "probabilities": {c: float(p) for c, p in zip(CLASSES, probs)},
 
 
146
  })
147
 
148
  except FileNotFoundError as e:
149
- return jsonify({"error": f"Modèle introuvable : {e}. Placez les fichiers .pth et .keras à la racine."}), 500
 
 
150
  except Exception as e:
151
  return jsonify({"error": str(e)}), 500
152
 
153
 
154
  if __name__ == "__main__":
155
  port = int(os.environ.get("PORT", 5000))
156
- app.run(host="0.0.0.0", port=port, debug=False)
 
15
 
16
  app = Flask(__name__)
17
 
18
+ CLASSES = ["buildings", "forest", "glacier", "mountain", "sea", "street"]
19
  IMG_SIZE = 150
20
 
21
  _pytorch_model = None
22
+ _tf_model = None
 
 
 
 
 
 
 
23
 
24
 
25
  class CNN_Torch(nn.Module):
26
  """
27
+ CNN PyTorch pour images RGB (3, 150, 150)
28
+ Retourne des log-probabilitΓ©s via log_softmax.
 
 
 
 
 
 
 
 
 
 
29
  """
30
  def __init__(self, num_classes: int = 6, dropout: float = 0.5):
31
  super().__init__()
32
 
33
  self.features = nn.Sequential(
34
+ # Block 1: 150 -> 75
35
  nn.Conv2d(3, 32, kernel_size=3, padding=1, bias=False),
36
  nn.BatchNorm2d(32),
37
  nn.ReLU(inplace=True),
38
  nn.MaxPool2d(2),
39
 
40
+ # Block 2: 75 -> 37
41
  nn.Conv2d(32, 64, kernel_size=3, padding=1, bias=False),
42
  nn.BatchNorm2d(64),
43
  nn.ReLU(inplace=True),
44
  nn.MaxPool2d(2),
45
  nn.Dropout2d(0.1),
46
 
47
+ # Block 3: 37 -> 18
48
  nn.Conv2d(64, 128, kernel_size=3, padding=1, bias=False),
49
  nn.BatchNorm2d(128),
50
  nn.ReLU(inplace=True),
 
52
  nn.Dropout2d(0.2),
53
  )
54
 
55
+ self.gap = nn.AdaptiveAvgPool2d(1)
56
 
57
  self.classifier = nn.Sequential(
58
  nn.Flatten(),
 
68
  x = self.classifier(x)
69
  return F.log_softmax(x, dim=1)
70
 
71
+
72
+ def load_pytorch():
73
+ global _pytorch_model
74
+ if _pytorch_model is not None:
75
+ return _pytorch_model
76
+
77
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
78
+ model = CNN_Torch(num_classes=6).to(device)
79
+
80
+ state_dict = torch.load("parfait_model.pth", map_location=device)
81
+ model.load_state_dict(state_dict)
82
  model.eval()
83
 
84
+ tf_transform = transforms.Compose([
85
  transforms.Resize((IMG_SIZE, IMG_SIZE)),
86
  transforms.ToTensor(),
87
+ transforms.Normalize(
88
+ [0.485, 0.456, 0.406],
89
+ [0.229, 0.224, 0.225]
90
+ ),
91
  ])
92
+
93
+ _pytorch_model = (model, device, tf_transform)
94
  return _pytorch_model
95
 
96
 
 
102
  return _tf_model
103
 
104
 
 
105
  @app.route("/")
106
  def index():
107
  return render_template("index.html")
 
121
 
122
  try:
123
  if framework == "pytorch":
124
+ model, device, tf_transform = load_pytorch()
125
+ tensor = tf_transform(pil_img).unsqueeze(0).to(device)
126
+
127
  with torch.no_grad():
128
+ out = model(tensor)
129
  probs = torch.exp(out).cpu().numpy()[0]
130
+
131
  else:
132
  model = load_tensorflow()
133
+ arr = np.array(pil_img.resize((IMG_SIZE, IMG_SIZE)), dtype=np.float32)
134
+ arr = np.expand_dims(arr, axis=0)
135
+ probs = model.predict(arr, verbose=0)[0]
136
 
137
  pred_idx = int(np.argmax(probs))
138
  return jsonify({
139
+ "class": CLASSES[pred_idx],
140
+ "confidence": float(probs[pred_idx]),
141
+ "probabilities": {
142
+ c: float(p) for c, p in zip(CLASSES, probs)
143
+ },
144
  })
145
 
146
  except FileNotFoundError as e:
147
+ return jsonify({
148
+ "error": f"Modèle introuvable : {e}. Placez les fichiers .pth et .keras à la racine."
149
+ }), 500
150
  except Exception as e:
151
  return jsonify({"error": str(e)}), 500
152
 
153
 
154
  if __name__ == "__main__":
155
  port = int(os.environ.get("PORT", 5000))
156
+ app.run(host="0.0.0.0", port=port, debug=False)