DimasMP3 commited on
Commit
318b10c
·
1 Parent(s): ac7382e

fix import

Browse files
Files changed (2) hide show
  1. app.py +29 -40
  2. inference.py +52 -58
app.py CHANGED
@@ -1,45 +1,34 @@
1
- def predict(image: Image.Image) -> Dict[str, float]:
2
- """Prediksi satu gambar. Return dict label->prob."""
3
- if image is None:
4
- return {"Error": "No image"}
5
- return _MODEL.predict_dict(image)
6
 
 
 
 
 
7
 
8
- def predict_batch(images: Iterable[Any]) -> List[Dict[str, float]]:
9
- """
10
- Prediksi banyak gambar. Setiap elemen boleh berupa:
11
- - PIL.Image.Image
12
- - path string / os.PathLike
13
- - Gradio image object (akan dicoba dibuka via PIL)
14
- Return: list of dict label->prob (panjang sama dengan input).
15
- """
16
- results: List[Dict[str, float]] = []
17
 
18
- def _as_pil(x: Any) -> Optional[Image.Image]:
19
- if x is None:
20
- return None
21
- if isinstance(x, Image.Image):
22
- return x
23
- # path-like
24
- if isinstance(x, (str, bytes, os.PathLike)):
25
- try:
26
- return Image.open(x).convert("RGB")
27
- except Exception:
28
- return None
29
- try:
30
- return Image.open(x).convert("RGB")
31
- except Exception:
32
- return None
33
 
34
- images = images or []
35
- for x in images:
36
- pil = _as_pil(x)
37
- if pil is None:
38
- results.append({"Error": "Invalid image"})
39
- else:
40
- results.append(_MODEL.predict_dict(pil))
41
- return results
42
 
43
-
44
- # Optional explicit export
45
- __all__ = ["predict", "predict_batch"]
 
1
+ # app.py
2
+ import os
3
+ import gradio as gr
4
+ from inference import predict, predict_batch # <-- gunakan fungsi dari inference.py
 
5
 
6
+ def _payload(d):
7
+ # ubah dict {label: prob} -> payload gradio.Label
8
+ items = sorted(d.items(), key=lambda kv: kv[1], reverse=True)
9
+ return {"label": items[0][0], "confidences": [{"label": k, "confidence": float(v)} for k, v in items]}
10
 
11
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
12
+ gr.Markdown("# Face Shape Classification — EfficientNetB4 (300×300)")
13
+ with gr.Row():
14
+ inp = gr.Image(type="pil", label="Upload face (frontal)")
15
+ out = gr.Label(num_top_classes=5, label="Predictions")
16
+ btn = gr.Button("Predict")
17
+ btn.click(lambda im: _payload(predict(im)), inputs=inp, outputs=out)
 
 
18
 
19
+ # Batch tab (opsional)
20
+ with gr.Tab("Batch"):
21
+ gal = gr.Gallery(label="Images").style(grid=4)
22
+ out_gal = gr.JSON(label="Batch outputs")
23
+ runb = gr.Button("Run batch")
24
+ runb.click(lambda ims: [predict_batch(ims)], inputs=gal, outputs=out_gal)
 
 
 
 
 
 
 
 
 
25
 
26
+ # Examples (jika ada folder examples/)
27
+ if os.path.exists("examples"):
28
+ gr.Examples(
29
+ examples=[os.path.join("examples", f) for f in os.listdir("examples")],
30
+ inputs=inp
31
+ )
 
 
32
 
33
+ if __name__ == "__main__":
34
+ demo.launch()
 
inference.py CHANGED
@@ -1,31 +1,24 @@
1
- import os
2
- import json
3
- import time
4
  from typing import List, Dict, Iterable, Any, Optional
5
  import numpy as np
6
  from PIL import Image
7
  import tensorflow as tf
8
 
9
- # -------------------- Label utilities --------------------
10
-
11
- _DEFAULT_LABELS = ["Heart", "Oblong", "Oval", "Round", "Square"]
12
 
13
  def _load_labels() -> List[str]:
14
- """Prioritas: models/class_indices.json -> models/idx2class.json -> default."""
15
- p_ci = os.path.join("models", "class_indices.json")
16
- p_i2c = os.path.join("models", "idx2class.json")
17
-
18
- # 1) class_indices.json (label->idx) → urutkan by idx
19
  try:
20
  with open(p_ci, "r") as f:
21
  ci = json.load(f)
22
- labels = [k for k, _ in sorted(ci.items(), key=lambda kv: kv[1])]
23
  print("[LABEL] from class_indices.json ->", labels)
24
  return labels
25
  except Exception:
26
  pass
27
-
28
- # 2) idx2class.json (idx->label) → urutkan by idx
29
  try:
30
  with open(p_i2c, "r") as f:
31
  i2c = json.load(f)
@@ -34,23 +27,14 @@ def _load_labels() -> List[str]:
34
  return labels
35
  except Exception:
36
  pass
37
-
38
- # 3) fallback
39
  print("[LABEL] fallback default ->", _DEFAULT_LABELS)
40
  return _DEFAULT_LABELS
41
 
42
-
43
- def _generate_config_if_missing(model: tf.keras.Model, labels: List[str], path: str = "config.json"):
44
- """Buat config.json jika belum ada (metadata untuk Space)."""
45
- if os.path.exists(path):
46
- return
47
  ishape = model.input_shape
48
- try:
49
- h = int(ishape[1])
50
- assert h > 0
51
- except Exception as e:
52
- raise AssertionError(f"Input shape tidak valid untuk config: {ishape}") from e
53
-
54
  cfg = {
55
  "architectures": ["EfficientNetB4"],
56
  "image_size": h,
@@ -58,31 +42,25 @@ def _generate_config_if_missing(model: tf.keras.Model, labels: List[str], path:
58
  "id2label": {str(i): lbl for i, lbl in enumerate(labels)},
59
  "label2id": {lbl: i for i, lbl in enumerate(labels)},
60
  }
61
- with open(path, "w") as f:
62
- json.dump(cfg, f, indent=2)
63
  print(f"[CFG] wrote {path} (image_size={h}, num_labels={len(labels)})")
64
 
65
-
66
- # -------------------- Model wrapper --------------------
67
-
68
  class FaceShapeModel:
69
- def __init__(self, model_path: str = "models/model.keras"):
70
- self.labels: List[str] = _load_labels()
71
-
72
  full_path = os.path.join(os.getcwd(), model_path)
73
  print(f"[LOAD] {full_path}")
74
- self.model: tf.keras.Model = tf.keras.models.load_model(full_path, compile=False)
75
 
76
- # ambil ukuran input (H=W)
77
  ishape = self.model.input_shape
78
- self.img_size: int = int(ishape[1])
79
  print(f"[MODEL] input img_size = {self.img_size}")
80
 
81
-
82
  names_lower = [l.name.lower() for l in self.model.layers[:10]]
83
- has_internal_pp = any(("rescaling" in n) or ("normalization" in n) for n in names_lower)
84
- self.external_rescale: bool = not has_internal_pp
85
- print(f"[MODEL] internal_preproc={has_internal_pp} -> external_rescale={self.external_rescale}")
86
 
87
  _generate_config_if_missing(self.model, self.labels)
88
 
@@ -91,32 +69,48 @@ class FaceShapeModel:
91
  except Exception as e:
92
  print("[WARN] warmup failed:", e)
93
 
94
- # ---- preprocessing ----
95
  def _to_rgb(self, img: Image.Image) -> Image.Image:
96
  return img if img.mode == "RGB" else img.convert("RGB")
97
 
98
  def _preprocess(self, img: Image.Image) -> np.ndarray:
99
- img = self._to_rgb(img)
100
- img = img.resize((self.img_size, self.img_size))
101
  x = np.asarray(img, dtype=np.float32)
102
- if self.external_rescale:
103
- x = x / 255.0
104
- return np.expand_dims(x, axis=0)
105
 
106
- # ---- predict ----
107
  def predict_dict(self, img: Image.Image) -> Dict[str, float]:
108
- """Kembalikan mapping {label: prob} (cocok untuk gradio.Label)."""
109
  t0 = time.perf_counter()
110
- x = self._preprocess(img)
111
- probs = self.model.predict(x, verbose=0)[0] # (C,)
112
- out = {lbl: float(p) for lbl, p in zip(self.labels, probs)}
113
  dt = (time.perf_counter() - t0) * 1000.0
114
  print(f"[INF] {len(self.labels)}-class in {dt:.1f} ms")
115
- return out
116
 
117
-
118
- # singleton model (di-load sekali saat import)
119
  _MODEL = FaceShapeModel()
120
 
121
-
122
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # inference.py
2
+ import os, json, time
 
3
  from typing import List, Dict, Iterable, Any, Optional
4
  import numpy as np
5
  from PIL import Image
6
  import tensorflow as tf
7
 
8
+ # ---------- Label utils ----------
9
+ _DEFAULT_LABELS = ["Heart","Oblong","Oval","Round","Square"]
 
10
 
11
  def _load_labels() -> List[str]:
12
+ p_ci = os.path.join("models", "class_indices.json") # {"Label": idx}
13
+ p_i2c = os.path.join("models", "idx2class.json") # {"0":"Label"}
 
 
 
14
  try:
15
  with open(p_ci, "r") as f:
16
  ci = json.load(f)
17
+ labels = [k for k,_ in sorted(ci.items(), key=lambda kv: kv[1])]
18
  print("[LABEL] from class_indices.json ->", labels)
19
  return labels
20
  except Exception:
21
  pass
 
 
22
  try:
23
  with open(p_i2c, "r") as f:
24
  i2c = json.load(f)
 
27
  return labels
28
  except Exception:
29
  pass
 
 
30
  print("[LABEL] fallback default ->", _DEFAULT_LABELS)
31
  return _DEFAULT_LABELS
32
 
33
+ def _generate_config_if_missing(model: tf.keras.Model, labels: List[str], path="config.json"):
34
+ if os.path.exists(path): return
 
 
 
35
  ishape = model.input_shape
36
+ h = int(ishape[1])
37
+ assert h > 0, f"Input shape aneh: {ishape}"
 
 
 
 
38
  cfg = {
39
  "architectures": ["EfficientNetB4"],
40
  "image_size": h,
 
42
  "id2label": {str(i): lbl for i, lbl in enumerate(labels)},
43
  "label2id": {lbl: i for i, lbl in enumerate(labels)},
44
  }
45
+ with open(path, "w") as f: json.dump(cfg, f, indent=2)
 
46
  print(f"[CFG] wrote {path} (image_size={h}, num_labels={len(labels)})")
47
 
48
+ # ---------- Model wrapper ----------
 
 
49
  class FaceShapeModel:
50
+ def __init__(self, model_path="models/model.keras"):
51
+ self.labels = _load_labels()
 
52
  full_path = os.path.join(os.getcwd(), model_path)
53
  print(f"[LOAD] {full_path}")
54
+ self.model = tf.keras.models.load_model(full_path, compile=False)
55
 
 
56
  ishape = self.model.input_shape
57
+ self.img_size = int(ishape[1])
58
  print(f"[MODEL] input img_size = {self.img_size}")
59
 
 
60
  names_lower = [l.name.lower() for l in self.model.layers[:10]]
61
+ has_pp = any(("rescaling" in n) or ("normalization" in n) for n in names_lower)
62
+ self.external_rescale = not has_pp
63
+ print(f"[MODEL] internal_preproc={has_pp} -> external_rescale={self.external_rescale}")
64
 
65
  _generate_config_if_missing(self.model, self.labels)
66
 
 
69
  except Exception as e:
70
  print("[WARN] warmup failed:", e)
71
 
 
72
  def _to_rgb(self, img: Image.Image) -> Image.Image:
73
  return img if img.mode == "RGB" else img.convert("RGB")
74
 
75
  def _preprocess(self, img: Image.Image) -> np.ndarray:
76
+ img = self._to_rgb(img).resize((self.img_size, self.img_size))
 
77
  x = np.asarray(img, dtype=np.float32)
78
+ if self.external_rescale: x = x / 255.0
79
+ return np.expand_dims(x, 0) # (1,H,W,3)
 
80
 
 
81
  def predict_dict(self, img: Image.Image) -> Dict[str, float]:
 
82
  t0 = time.perf_counter()
83
+ p = self.model.predict(self._preprocess(img), verbose=0)[0]
 
 
84
  dt = (time.perf_counter() - t0) * 1000.0
85
  print(f"[INF] {len(self.labels)}-class in {dt:.1f} ms")
86
+ return {lbl: float(prob) for lbl, prob in zip(self.labels, p)}
87
 
88
+ # singleton
 
89
  _MODEL = FaceShapeModel()
90
 
91
+ # ---------- Public API ----------
92
+ def predict(image: Image.Image) -> Dict[str, float]:
93
+ if image is None:
94
+ return {"Error": "No image"}
95
+ return _MODEL.predict_dict(image)
96
+
97
+ def predict_batch(images: Iterable[Any]) -> List[Dict[str, float]]:
98
+ from PIL import Image as _PILImage
99
+ import os as _os
100
+ results: List[Dict[str, float]] = []
101
+
102
+ def _as_pil(x: Any) -> Optional[_PILImage.Image]:
103
+ if x is None: return None
104
+ if isinstance(x, _PILImage.Image): return x
105
+ if isinstance(x, (str, bytes, _os.PathLike)):
106
+ try: return _PILImage.open(x).convert("RGB")
107
+ except Exception: return None
108
+ try: return _PILImage.open(x).convert("RGB")
109
+ except Exception: return None
110
+
111
+ for x in (images or []):
112
+ im = _as_pil(x)
113
+ results.append({"Error": "Invalid image"} if im is None else _MODEL.predict_dict(im))
114
+ return results
115
+
116
+ __all__ = ["predict", "predict_batch"]