cyberai-1 commited on
Commit
4d2ac45
·
1 Parent(s): b899fa4
Files changed (1) hide show
  1. app.py +60 -52
app.py CHANGED
@@ -4,59 +4,49 @@ Intel Scene Classifier — Flask App
4
 
5
  import io
6
  import os
 
7
  import numpy as np
8
  from flask import Flask, jsonify, render_template, request
9
  from PIL import Image
10
 
 
 
 
 
 
11
  app = Flask(__name__)
12
 
13
- CLASSES = ["buildings", "forest", "glacier", "mountain", "sea", "street"]
14
  IMG_SIZE = 150
15
 
16
  _pytorch_model = None
17
- _tf_model = None
18
-
19
-
20
- # ── Loaders ────────────────────────────────────────────────────────────────────
21
- def load_pytorch():
22
- global _pytorch_model
23
- if _pytorch_model is not None:
24
- return _pytorch_model
25
 
26
 
27
  class CNN_Torch(nn.Module):
28
  """
29
- CNN PyTorch allégé pour images RGB (3 canaux).
30
- Entrée : (B, 3, 150, 150)
31
- Sortie : (B, num_classes) log-softmax
32
-
33
- Architecture :
34
- Block 1 : Conv2d(3→32) + BN + ReLU + MaxPool2d(2) → 75×75
35
- Block 2 : Conv2d(32→64) + BN + ReLU + MaxPool2d(2) + Drop2d → 37×37
36
- Block 3 : Conv2d(64→128)+ BN + ReLU + MaxPool2d(2) + Drop2d → 18×18
37
- GAP : AdaptiveAvgPool2d(1) → (B,128)
38
- Head : Linear(128→256) + ReLU + Dropout + Linear(256→C)
39
-
40
- Paramètre `dropout` contrôlé depuis l'extérieur → utilisé dans le CV.
41
  """
42
  def __init__(self, num_classes: int = 6, dropout: float = 0.5):
43
  super().__init__()
44
 
45
  self.features = nn.Sequential(
46
- # Block 1 — 150 → 75
47
  nn.Conv2d(3, 32, kernel_size=3, padding=1, bias=False),
48
  nn.BatchNorm2d(32),
49
  nn.ReLU(inplace=True),
50
  nn.MaxPool2d(2),
51
 
52
- # Block 2 — 75 → 37
53
  nn.Conv2d(32, 64, kernel_size=3, padding=1, bias=False),
54
  nn.BatchNorm2d(64),
55
  nn.ReLU(inplace=True),
56
  nn.MaxPool2d(2),
57
  nn.Dropout2d(0.1),
58
 
59
- # Block 3 — 37 → 18
60
  nn.Conv2d(64, 128, kernel_size=3, padding=1, bias=False),
61
  nn.BatchNorm2d(128),
62
  nn.ReLU(inplace=True),
@@ -64,7 +54,7 @@ class CNN_Torch(nn.Module):
64
  nn.Dropout2d(0.2),
65
  )
66
 
67
- self.gap = nn.AdaptiveAvgPool2d(1) # (B, 128, 18, 18) → (B, 128, 1, 1)
68
 
69
  self.classifier = nn.Sequential(
70
  nn.Flatten(),
@@ -80,17 +70,29 @@ class CNN_Torch(nn.Module):
80
  x = self.classifier(x)
81
  return F.log_softmax(x, dim=1)
82
 
 
 
 
 
 
 
83
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
84
- model = CNN_Torch(6).to(device)
85
- model.load_state_dict(torch.load("parfait_model.pth", map_location=device))
 
 
86
  model.eval()
87
 
88
- tf = transforms.Compose([
89
  transforms.Resize((IMG_SIZE, IMG_SIZE)),
90
  transforms.ToTensor(),
91
- transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
 
 
 
92
  ])
93
- _pytorch_model = (model, device, tf)
 
94
  return _pytorch_model
95
 
96
 
@@ -102,32 +104,26 @@ def load_tensorflow():
102
  return _tf_model
103
 
104
 
105
-
106
  def read_input_image():
107
  if "image" in request.files and request.files["image"].filename:
108
  return Image.open(io.BytesIO(request.files["image"].read())).convert("RGB")
109
 
110
  image_url = request.form.get("image_url", "").strip()
111
  if image_url:
112
- with urllib.request.urlopen(image_url) as resp:
113
- return Image.open(io.BytesIO(resp.read())).convert("RGB")
114
 
115
  raise ValueError("No image provided")
116
 
117
- # ── Routes ─────────────────────────────────────────────────────────────────────
118
  @app.route("/")
119
  def index():
120
  return render_template("index.html")
121
 
122
 
123
-
124
-
125
  @app.route("/predict", methods=["POST"])
126
  def predict():
127
- # if "image" not in request.files:
128
- # return jsonify({"error": "Aucune image fournie"}), 400
129
-
130
- # framework = request.form.get("model", "pytorch")
131
 
132
  try:
133
  pil_img = read_input_image()
@@ -136,30 +132,42 @@ def predict():
136
 
137
  try:
138
  if framework == "pytorch":
139
- import torch
140
- model, device, tf = load_pytorch()
141
- tensor = tf(pil_img).unsqueeze(0).to(device)
142
  with torch.no_grad():
143
- out = model(tensor)
144
  probs = torch.exp(out).cpu().numpy()[0]
145
- else:
 
146
  model = load_tensorflow()
147
- arr = np.array(pil_img.resize((IMG_SIZE, IMG_SIZE)), dtype=np.float32)
148
- probs = model.predict(np.expand_dims(arr, 0), verbose=0)[0]
 
 
 
 
 
 
 
149
 
150
  pred_idx = int(np.argmax(probs))
151
  return jsonify({
152
- "class": CLASSES[pred_idx],
153
- "confidence": float(probs[pred_idx]),
154
- "probabilities": {c: float(p) for c, p in zip(CLASSES, probs)},
 
 
155
  })
156
 
157
  except FileNotFoundError as e:
158
- return jsonify({"error": f"Modèle introuvable : {e}. Placez les fichiers .pth et .keras à la racine."}), 500
 
 
159
  except Exception as e:
160
  return jsonify({"error": str(e)}), 500
161
 
162
 
163
  if __name__ == "__main__":
164
  port = int(os.environ.get("PORT", 5000))
165
- app.run(host="0.0.0.0", port=port, debug=False)
 
4
 
5
  import io
6
  import os
7
+ import urllib.request
8
  import numpy as np
9
  from flask import Flask, jsonify, render_template, request
10
  from PIL import Image
11
 
12
+ import torch
13
+ import torch.nn as nn
14
+ import torch.nn.functional as F
15
+ from torchvision import transforms
16
+
17
  app = Flask(__name__)
18
 
19
+ CLASSES = ["buildings", "forest", "glacier", "mountain", "sea", "street"]
20
  IMG_SIZE = 150
21
 
22
  _pytorch_model = None
23
+ _tf_model = None
 
 
 
 
 
 
 
24
 
25
 
26
  class CNN_Torch(nn.Module):
27
  """
28
+ CNN PyTorch léger pour images RGB.
29
+ Entrée : (B, 3, 150, 150)
30
+ Sortie : logits transformés en log_softmax
 
 
 
 
 
 
 
 
 
31
  """
32
  def __init__(self, num_classes: int = 6, dropout: float = 0.5):
33
  super().__init__()
34
 
35
  self.features = nn.Sequential(
36
+ # Block 1
37
  nn.Conv2d(3, 32, kernel_size=3, padding=1, bias=False),
38
  nn.BatchNorm2d(32),
39
  nn.ReLU(inplace=True),
40
  nn.MaxPool2d(2),
41
 
42
+ # Block 2
43
  nn.Conv2d(32, 64, kernel_size=3, padding=1, bias=False),
44
  nn.BatchNorm2d(64),
45
  nn.ReLU(inplace=True),
46
  nn.MaxPool2d(2),
47
  nn.Dropout2d(0.1),
48
 
49
+ # Block 3
50
  nn.Conv2d(64, 128, kernel_size=3, padding=1, bias=False),
51
  nn.BatchNorm2d(128),
52
  nn.ReLU(inplace=True),
 
54
  nn.Dropout2d(0.2),
55
  )
56
 
57
+ self.gap = nn.AdaptiveAvgPool2d(1)
58
 
59
  self.classifier = nn.Sequential(
60
  nn.Flatten(),
 
70
  x = self.classifier(x)
71
  return F.log_softmax(x, dim=1)
72
 
73
+
74
+ def load_pytorch():
75
+ global _pytorch_model
76
+ if _pytorch_model is not None:
77
+ return _pytorch_model
78
+
79
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
80
+ model = CNN_Torch(num_classes=6).to(device)
81
+
82
+ state_dict = torch.load("parfait_model.pth", map_location=device)
83
+ model.load_state_dict(state_dict)
84
  model.eval()
85
 
86
+ tf_transform = transforms.Compose([
87
  transforms.Resize((IMG_SIZE, IMG_SIZE)),
88
  transforms.ToTensor(),
89
+ transforms.Normalize(
90
+ [0.485, 0.456, 0.406],
91
+ [0.229, 0.224, 0.225]
92
+ ),
93
  ])
94
+
95
+ _pytorch_model = (model, device, tf_transform)
96
  return _pytorch_model
97
 
98
 
 
104
  return _tf_model
105
 
106
 
 
107
  def read_input_image():
108
  if "image" in request.files and request.files["image"].filename:
109
  return Image.open(io.BytesIO(request.files["image"].read())).convert("RGB")
110
 
111
  image_url = request.form.get("image_url", "").strip()
112
  if image_url:
113
+ with urllib.request.urlopen(image_url) as response:
114
+ return Image.open(io.BytesIO(response.read())).convert("RGB")
115
 
116
  raise ValueError("No image provided")
117
 
118
+
119
  @app.route("/")
120
  def index():
121
  return render_template("index.html")
122
 
123
 
 
 
124
  @app.route("/predict", methods=["POST"])
125
  def predict():
126
+ framework = request.form.get("model", "pytorch")
 
 
 
127
 
128
  try:
129
  pil_img = read_input_image()
 
132
 
133
  try:
134
  if framework == "pytorch":
135
+ model, device, tf_transform = load_pytorch()
136
+ tensor = tf_transform(pil_img).unsqueeze(0).to(device)
137
+
138
  with torch.no_grad():
139
+ out = model(tensor)
140
  probs = torch.exp(out).cpu().numpy()[0]
141
+
142
+ elif framework == "tensorflow":
143
  model = load_tensorflow()
144
+ arr = np.array(
145
+ pil_img.resize((IMG_SIZE, IMG_SIZE)),
146
+ dtype=np.float32
147
+ )
148
+ arr = np.expand_dims(arr, axis=0)
149
+ probs = model.predict(arr, verbose=0)[0]
150
+
151
+ else:
152
+ return jsonify({"error": "Framework non supporté"}), 400
153
 
154
  pred_idx = int(np.argmax(probs))
155
  return jsonify({
156
+ "class": CLASSES[pred_idx],
157
+ "confidence": float(probs[pred_idx]),
158
+ "probabilities": {
159
+ c: float(p) for c, p in zip(CLASSES, probs)
160
+ },
161
  })
162
 
163
  except FileNotFoundError as e:
164
+ return jsonify({
165
+ "error": f"Modèle introuvable : {e}. Placez les fichiers .pth et .keras à la racine."
166
+ }), 500
167
  except Exception as e:
168
  return jsonify({"error": str(e)}), 500
169
 
170
 
171
  if __name__ == "__main__":
172
  port = int(os.environ.get("PORT", 5000))
173
+ app.run(host="0.0.0.0", port=port, debug=False)