reikoayano commited on
Commit
5d7a2e7
Β·
verified Β·
1 Parent(s): 9dc3df4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +9 -16
app.py CHANGED
@@ -9,8 +9,8 @@ from PIL import Image
9
  app = FastAPI(title="DyslexiaLens Prediction API")
10
 
11
  # ─── RE-REGISTER CUSTOM ARCHITECTURE COMPONENTS ───
12
- # Keras requires these custom classes explicitly defined to unpack the .keras file
13
- @keras.saving.register_keras_serializable()
14
  class AdaptiveContrastNorm(layers.Layer):
15
  def __init__(self, epsilon: float = 1e-6, **kwargs):
16
  super().__init__(**kwargs)
@@ -33,7 +33,8 @@ class AdaptiveContrastNorm(layers.Layer):
33
  config.update({'epsilon': self.epsilon})
34
  return config
35
 
36
- @keras.saving.register_keras_serializable()
 
37
  class MaskedHuberLoss(keras.losses.Loss):
38
  def __init__(self, delta: float = 0.5, **kwargs):
39
  super().__init__(**kwargs)
@@ -61,7 +62,6 @@ model = None
61
  def load_model():
62
  global model
63
  try:
64
- # Pass custom mapping into custom_objects for accurate loading
65
  model = keras.models.load_model(
66
  MODEL_PATH,
67
  custom_objects={
@@ -91,31 +91,24 @@ async def predict(
91
  raise HTTPException(status_code=503, detail="Model is loading or uninitialized.")
92
 
93
  try:
94
- # 1. Image Preprocessing (Matches tf.data Pipeline)
95
  contents = await file.read()
96
- image = Image.open(io.BytesIO(contents)).convert('L') # Force standard Grayscale
97
- image = image.resize((128, 128), Image.BILINEAR) # Downsample from 224x224 source
98
- img_array = np.array(image, dtype=np.float32) / 255.0 # Normalize pixels [0, 1]
99
- img_tensor = np.expand_dims(img_array, axis=(0, -1)) # Reshape to matching batch: (1, 128, 128, 1)
100
 
101
- # 2. Clinical Vector Compilation
102
  feature_vector = np.array([
103
  stroke_density, center_of_mass_x, center_of_mass_y,
104
  bounding_box_ratio, stroke_transitions, horizontal_symmetry
105
- ], dtype=np.float32).reshape(1, 6) # Shape: (1, 6)
106
 
107
- # 3. Model Pipeline Dual-Head Inference
108
- # Keys must explicitly map to the functional API layer names defined during your build phase
109
  predictions = model.predict({
110
  'image_input': img_tensor,
111
  'feature_input': feature_vector
112
  })
113
 
114
- # Parse head outputs
115
  clf_probability = float(predictions[0][0][0])
116
  severity_score = float(predictions[1][0][0])
117
-
118
- # Proven threshold division border evaluated via your sweep analysis
119
  is_dyslexia = clf_probability >= 0.40
120
 
121
  return {
 
9
  app = FastAPI(title="DyslexiaLens Prediction API")
10
 
11
  # ─── RE-REGISTER CUSTOM ARCHITECTURE COMPONENTS ───
12
+ # CHANGED: Swapped .saving to .utils to match the tf.keras wrapper ecosystem
13
+ @keras.utils.register_keras_serializable(package="Custom")
14
  class AdaptiveContrastNorm(layers.Layer):
15
  def __init__(self, epsilon: float = 1e-6, **kwargs):
16
  super().__init__(**kwargs)
 
33
  config.update({'epsilon': self.epsilon})
34
  return config
35
 
36
+ # CHANGED: Swapped .saving to .utils here as well
37
+ @keras.utils.register_keras_serializable(package="Custom")
38
  class MaskedHuberLoss(keras.losses.Loss):
39
  def __init__(self, delta: float = 0.5, **kwargs):
40
  super().__init__(**kwargs)
 
62
  def load_model():
63
  global model
64
  try:
 
65
  model = keras.models.load_model(
66
  MODEL_PATH,
67
  custom_objects={
 
91
  raise HTTPException(status_code=503, detail="Model is loading or uninitialized.")
92
 
93
  try:
 
94
  contents = await file.read()
95
+ image = Image.open(io.BytesIO(contents)).convert('L')
96
+ image = image.resize((128, 128), Image.BILINEAR)
97
+ img_array = np.array(image, dtype=np.float32) / 255.0
98
+ img_tensor = np.expand_dims(img_array, axis=(0, -1))
99
 
 
100
  feature_vector = np.array([
101
  stroke_density, center_of_mass_x, center_of_mass_y,
102
  bounding_box_ratio, stroke_transitions, horizontal_symmetry
103
+ ], dtype=np.float32).reshape(1, 6)
104
 
 
 
105
  predictions = model.predict({
106
  'image_input': img_tensor,
107
  'feature_input': feature_vector
108
  })
109
 
 
110
  clf_probability = float(predictions[0][0][0])
111
  severity_score = float(predictions[1][0][0])
 
 
112
  is_dyslexia = clf_probability >= 0.40
113
 
114
  return {