AnasHXH commited on
Commit
5cddc47
·
verified ·
1 Parent(s): bb87c82

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +55 -88
app.py CHANGED
@@ -48,43 +48,33 @@ DEMO_INPUTS = {
48
  }
49
 
50
  # ----------------------------
51
- # Model loading (kept placeholder)
52
  # ----------------------------
53
  _model = None
54
  def load_model(model_path: str = DEFAULT_MODEL_PATH):
55
  global _model
56
  if _model is not None:
57
  return _model
58
-
59
- class DummyModel:
60
- pass
61
-
62
- _model = DummyModel()
63
  return _model
64
 
65
-
66
  # ----------------------------
67
  # Helpers
68
  # ----------------------------
69
  def _norm_mae(a: np.ndarray, b: np.ndarray) -> float:
70
- """Normalized mean absolute error in [0,1] for matching demo images."""
71
  if a.shape != b.shape:
72
  b = cv2.resize(b, (a.shape[1], a.shape[0]), interpolation=cv2.INTER_AREA)
73
- a = cv2.cvtColor(a, cv2.COLOR_BGR2GRAY) if a.ndim == 3 else a
74
- b = cv2.cvtColor(b, cv2.COLOR_BGR2GRAY) if b.ndim == 3 else b
75
  a = a.astype(np.float32) / 255.0
76
  b = b.astype(np.float32) / 255.0
77
  return float(np.mean(np.abs(a - b)))
78
 
79
-
80
  def match_demo_image(upload_bgr: np.ndarray) -> str | None:
81
- """
82
- Return 'input_1.png' or 'input_2.png' if the uploaded image matches,
83
- else None. Uses simple normalized MAE with resizing.
84
- """
85
  best_name, best_score = None, 1.0
86
  for fname in DEMO_INPUTS.keys():
87
- if not os.path.exists(fname):
88
  continue
89
  ref = cv2.imread(fname, cv2.IMREAD_COLOR)
90
  if ref is None:
@@ -92,11 +82,8 @@ def match_demo_image(upload_bgr: np.ndarray) -> str | None:
92
  score = _norm_mae(upload_bgr, ref)
93
  if score < best_score:
94
  best_score, best_name = score, fname
95
-
96
- # Loose threshold to cope with minor metadata/encoding differences
97
  return best_name if best_score < 0.01 else None # ~99% similar
98
 
99
-
100
  def draw_boxes_with_x(image_bgr: np.ndarray, boxes, thickness: int = 3):
101
  img = image_bgr.copy()
102
  color = (0, 0, 255)
@@ -105,32 +92,15 @@ def draw_boxes_with_x(image_bgr: np.ndarray, boxes, thickness: int = 3):
105
  cv2.rectangle(img, (x1, y1), (x2, y2), color, thickness)
106
  cv2.line(img, (x1, y1), (x2, y2), color, thickness)
107
  cv2.line(img, (x1, y2), (x2, y1), color, thickness)
108
- txt = f"{label}:{score:.2f}"
109
- cv2.putText(img, txt, (x1, max(y1 - 6, 0)),
110
  cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2, cv2.LINE_AA)
111
  return img
112
 
113
-
114
- def boxes_from_mask(mask: np.ndarray, min_area: int = 50):
115
- mask = (mask > 0).astype(np.uint8)
116
- cnts, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
117
- out = []
118
- for c in cnts:
119
- x, y, w, h = cv2.boundingRect(c)
120
- if w * h >= min_area:
121
- out.append([x, y, x + w, y + h, 1.0, "defect"])
122
- return out
123
-
124
-
125
- def to_csv_file_from_df(df: pd.DataFrame, path="/tmp/defect_report.csv"):
126
  df.to_csv(path, index=False)
127
  return path, df
128
 
129
-
130
- # ----------------------------
131
- # Placeholder inference for non-demo images
132
- # ----------------------------
133
- def infer_placeholder(image_bgr: np.ndarray, conf: float = 0.25):
134
  gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)
135
  e = cv2.Canny(gray, 50, 150)
136
  cnts, _ = cv2.findContours(e, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
@@ -138,55 +108,51 @@ def infer_placeholder(image_bgr: np.ndarray, conf: float = 0.25):
138
  h, w = gray.shape[:2]
139
  for c in cnts:
140
  x, y, bw, bh = cv2.boundingRect(c)
141
- if bw * bh < max(0.0005 * w * h, 150):
142
  continue
143
- boxes.append([x, y, x + bw, y + bh, 0.5, "defect"])
144
  if len(boxes) >= 20:
145
  break
146
- return boxes
147
-
148
 
149
  # ----------------------------
150
- # Gradio handler
151
  # ----------------------------
152
- def process(image: Image.Image, conf: float, draw_x: bool, min_area: int):
153
- if image is None:
154
- return None, pd.DataFrame(), None, "Please upload an image."
155
-
156
- # PIL -> BGR
157
- img_rgb = np.array(image.convert("RGB"))
158
- img_bgr = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)
159
-
160
- # 1) Check if this is one of our demo images
161
- match_name = match_demo_image(img_bgr)
162
- if match_name is not None:
163
- meta = DEMO_INPUTS[match_name]
164
- # Use the pre-rendered annotated image
165
- vis_pil = Image.open(meta["output_path"]).convert("RGB")
166
- df = pd.DataFrame(meta["rows"], columns=["row", "cell", "defect"])
167
- csv_path, df = to_csv_file_from_df(df)
168
- return vis_pil, df, csv_path, meta["summary"]
169
-
170
- # 2) Otherwise fall back to placeholder detection
171
- boxes = infer_placeholder(img_bgr, conf=conf)
172
- boxes = [b for b in boxes if b[4] >= conf]
173
- vis = draw_boxes_with_x(img_bgr, boxes) if draw_x else img_bgr.copy()
174
- vis_rgb = cv2.cvtColor(vis, cv2.COLOR_BGR2RGB)
175
- vis_pil = Image.fromarray(vis_rgb)
176
-
177
- # Build a simple table for non-demo case
178
- if boxes:
179
- df = pd.DataFrame(
180
- [{"x1": b[0], "y1": b[1], "x2": b[2], "y2": b[3], "score": b[4], "label": b[5]} for b in boxes]
181
- )
182
- summary = f"Detected {len(boxes)} defect region(s)."
183
- else:
184
- df = pd.DataFrame(columns=["x1", "y1", "x2", "y2", "score", "label"])
185
- summary = "No defects detected by placeholder."
186
-
187
- csv_path, df = to_csv_file_from_df(df)
188
- return vis_pil, df, csv_path, summary
189
-
190
 
191
  # ----------------------------
192
  # UI
@@ -194,16 +160,16 @@ def process(image: Image.Image, conf: float, draw_x: bool, min_area: int):
194
  with gr.Blocks(title="AI-Driven EL Defect Recognition") as demo:
195
  gr.Markdown(
196
  "## AI-Driven Defect Recognition in EL Images\n"
197
- "Upload an electroluminescence (EL) image. For the demo, uploading **input_1.png** "
198
- "or **input_2.png** will return your predefined annotated results and a CSV.\n"
199
- "For other images, a lightweight placeholder detector runs."
200
  )
201
  with gr.Row():
202
  with gr.Column():
203
- inp = gr.Image(type="pil", label="Upload EL image")
 
204
  conf = gr.Slider(0.0, 1.0, value=0.25, step=0.01, label="Confidence threshold")
205
  draw_x = gr.Checkbox(True, label="Draw red box + X (non-demo only)")
206
- min_area = gr.Slider(10, 5000, value=120, step=10, label="Min defect area (pixels, for masks)")
207
  run_btn = gr.Button("Run inference", variant="primary")
208
  with gr.Column():
209
  out_img = gr.Image(type="pil", label="Annotated output")
@@ -216,4 +182,5 @@ with gr.Blocks(title="AI-Driven EL Defect Recognition") as demo:
216
 
217
  if __name__ == "__main__":
218
  load_model()
219
- demo.launch()
 
 
48
  }
49
 
50
  # ----------------------------
51
+ # Model stub (kept)
52
  # ----------------------------
53
  _model = None
54
  def load_model(model_path: str = DEFAULT_MODEL_PATH):
55
  global _model
56
  if _model is not None:
57
  return _model
58
+ class Dummy: ...
59
+ _model = Dummy()
 
 
 
60
  return _model
61
 
 
62
  # ----------------------------
63
  # Helpers
64
  # ----------------------------
65
  def _norm_mae(a: np.ndarray, b: np.ndarray) -> float:
 
66
  if a.shape != b.shape:
67
  b = cv2.resize(b, (a.shape[1], a.shape[0]), interpolation=cv2.INTER_AREA)
68
+ if a.ndim == 3: a = cv2.cvtColor(a, cv2.COLOR_BGR2GRAY)
69
+ if b.ndim == 3: b = cv2.cvtColor(b, cv2.COLOR_BGR2GRAY)
70
  a = a.astype(np.float32) / 255.0
71
  b = b.astype(np.float32) / 255.0
72
  return float(np.mean(np.abs(a - b)))
73
 
 
74
  def match_demo_image(upload_bgr: np.ndarray) -> str | None:
 
 
 
 
75
  best_name, best_score = None, 1.0
76
  for fname in DEMO_INPUTS.keys():
77
+ if not os.path.exists(fname):
78
  continue
79
  ref = cv2.imread(fname, cv2.IMREAD_COLOR)
80
  if ref is None:
 
82
  score = _norm_mae(upload_bgr, ref)
83
  if score < best_score:
84
  best_score, best_name = score, fname
 
 
85
  return best_name if best_score < 0.01 else None # ~99% similar
86
 
 
87
  def draw_boxes_with_x(image_bgr: np.ndarray, boxes, thickness: int = 3):
88
  img = image_bgr.copy()
89
  color = (0, 0, 255)
 
92
  cv2.rectangle(img, (x1, y1), (x2, y2), color, thickness)
93
  cv2.line(img, (x1, y1), (x2, y2), color, thickness)
94
  cv2.line(img, (x1, y2), (x2, y1), color, thickness)
95
+ cv2.putText(img, f"{label}:{score:.2f}", (x1, max(y1-6,0)),
 
96
  cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2, cv2.LINE_AA)
97
  return img
98
 
99
+ def to_csv(df: pd.DataFrame, path="/tmp/defect_report.csv"):
 
 
 
 
 
 
 
 
 
 
 
 
100
  df.to_csv(path, index=False)
101
  return path, df
102
 
103
+ def placeholder_boxes(image_bgr: np.ndarray, conf: float = 0.25):
 
 
 
 
104
  gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)
105
  e = cv2.Canny(gray, 50, 150)
106
  cnts, _ = cv2.findContours(e, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
 
108
  h, w = gray.shape[:2]
109
  for c in cnts:
110
  x, y, bw, bh = cv2.boundingRect(c)
111
+ if bw * bh < max(0.0005 * w * h, 150):
112
  continue
113
+ boxes.append([x, y, x+bw, y+bh, 0.5, "defect"])
114
  if len(boxes) >= 20:
115
  break
116
+ return [b for b in boxes if b[4] >= conf]
 
117
 
118
  # ----------------------------
119
+ # Gradio handler (filepath input)
120
  # ----------------------------
121
+ def process(image_path: str, conf: float, draw_x: bool, min_area: int):
122
+ try:
123
+ if not image_path or not os.path.exists(image_path):
124
+ return None, pd.DataFrame(), None, "Please upload an image."
125
+
126
+ # read as BGR
127
+ pil_img = Image.open(image_path).convert("RGB")
128
+ img_bgr = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
129
+
130
+ # Demo match?
131
+ match_name = match_demo_image(img_bgr)
132
+ if match_name is not None:
133
+ meta = DEMO_INPUTS[match_name]
134
+ vis_pil = Image.open(meta["output_path"]).convert("RGB")
135
+ df = pd.DataFrame(meta["rows"], columns=["row", "cell", "defect"])
136
+ csv_path, df = to_csv(df)
137
+ return vis_pil, df, csv_path, meta["summary"]
138
+
139
+ # Fallback placeholder
140
+ boxes = placeholder_boxes(img_bgr, conf=conf)
141
+ vis = draw_boxes_with_x(img_bgr, boxes) if draw_x else img_bgr.copy()
142
+ vis_pil = Image.fromarray(cv2.cvtColor(vis, cv2.COLOR_BGR2RGB))
143
+ if boxes:
144
+ df = pd.DataFrame(
145
+ [{"x1": b[0], "y1": b[1], "x2": b[2], "y2": b[3], "score": b[4], "label": b[5]} for b in boxes]
146
+ )
147
+ summary = f"Detected {len(boxes)} region(s) by placeholder."
148
+ else:
149
+ df = pd.DataFrame(columns=["x1","y1","x2","y2","score","label"])
150
+ summary = "No defects detected by placeholder."
151
+ csv_path, df = to_csv(df)
152
+ return vis_pil, df, csv_path, summary
153
+ except Exception as e:
154
+ # surface errors to UI instead of crashing
155
+ return None, pd.DataFrame(), None, f"Error: {type(e).__name__}: {e}"
 
 
 
156
 
157
  # ----------------------------
158
  # UI
 
160
  with gr.Blocks(title="AI-Driven EL Defect Recognition") as demo:
161
  gr.Markdown(
162
  "## AI-Driven Defect Recognition in EL Images\n"
163
+ "Demo: uploading **input_1.png** or **input_2.png** returns predefined annotated results and CSV.\n"
164
+ "Other images run a lightweight placeholder."
 
165
  )
166
  with gr.Row():
167
  with gr.Column():
168
+ # Use filepath to avoid large base64 uploads
169
+ inp = gr.Image(type="filepath", label="Upload EL image")
170
  conf = gr.Slider(0.0, 1.0, value=0.25, step=0.01, label="Confidence threshold")
171
  draw_x = gr.Checkbox(True, label="Draw red box + X (non-demo only)")
172
+ min_area = gr.Slider(10, 5000, value=120, step=10, label="Min defect area (for masks)")
173
  run_btn = gr.Button("Run inference", variant="primary")
174
  with gr.Column():
175
  out_img = gr.Image(type="pil", label="Annotated output")
 
182
 
183
  if __name__ == "__main__":
184
  load_model()
185
+ # Disable SSR to avoid upload edge-cases
186
+ demo.launch(ssr_mode=False)