File size: 3,124 Bytes
f1502b4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
from __future__ import annotations

import io
import os
from pathlib import Path
from typing import Dict, List

import gradio as gr
import numpy as np
import tensorflow as tf
from PIL import Image
from tensorflow.keras.applications import EfficientNetB2


# Label wajah sesuai backend Flask
CLASS_NAMES: List[str] = ["Heart", "Oblong", "Oval", "Round", "Square"]
IMG_SIZE = 244
NUM_CLASSES = len(CLASS_NAMES)


def _load_image_to_rgb(image: Image.Image) -> np.ndarray:
    if image.mode != "RGB":
        image = image.convert("RGB")
    return np.asarray(image)


def _resize_to_model(img_rgb: np.ndarray) -> np.ndarray:
    im = Image.fromarray(img_rgb)
    im = im.resize((IMG_SIZE, IMG_SIZE), Image.NEAREST)
    return np.asarray(im)


def _preprocess(image: Image.Image) -> np.ndarray:
    rgb = _load_image_to_rgb(image)
    resized = _resize_to_model(rgb)
    arr = resized.astype("float32")
    return np.expand_dims(arr, axis=0)  # [1, IMG_SIZE, IMG_SIZE, 3]


def _create_model(num_classes: int):
    base_model = EfficientNetB2(
        weights=None,
        include_top=False,
        pooling="avg",
        input_shape=(IMG_SIZE, IMG_SIZE, 3),
    )
    model = tf.keras.Sequential(
        [
            base_model,
            tf.keras.layers.Dropout(0.2),
            tf.keras.layers.Dense(num_classes, activation="softmax"),
        ]
    )
    return model


def _resolve_model_path() -> Path:
    base_dir = Path(__file__).resolve().parent
    candidates = [
        base_dir / "best_model.keras",
    ]
    for p in candidates:
        if p.exists():
            return p
    # default fallback
    return candidates[0]


class PreTrainedModel:
    def __init__(self) -> None:
        self.model_path = _resolve_model_path()
        self.model = _create_model(NUM_CLASSES)
        if self.model_path.exists():
            # Memuat bobot yang sama seperti backend Flask
            self.model.load_weights(self.model_path)
            print(f"Loaded weights from: {self.model_path}")
        else:
            print(f"Warning: Model file not found at: {self.model_path}")

    def predict_image(self, image: Image.Image) -> Dict[str, float]:
        x = _preprocess(image)
        preds = self.model.predict(x)
        if isinstance(preds, (list, tuple)):
            preds = preds[0]
        probs = np.asarray(preds).squeeze().tolist()
        return {label: float(score) for label, score in zip(CLASS_NAMES, probs)}


model = PreTrainedModel()


def predict(image: Image.Image):
    predictions = model.predict_image(image)
    probs_percent = {label: round(p * 100, 2)
                     for label, p in predictions.items()}
    max_label = max(probs_percent, key=probs_percent.get)
    return {
        "label": max_label,
        "percentage": probs_percent[max_label],
        "probabilities": probs_percent,
    }


iface = gr.Interface(
    fn=predict,
    inputs=gr.Image(type="pil"),
    outputs=gr.JSON(),
    title="Face Shape Classification",
    description="Upload an image to classify face shape (Heart, Oblong, Oval, Round, Square).",
)


if __name__ == "__main__":
    iface.launch()