Ayesha352 commited on
Commit
9ff5f9d
·
verified ·
1 Parent(s): 1a59aa1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +120 -106
app.py CHANGED
@@ -4,6 +4,7 @@ import json
4
  import gradio as gr
5
  import os
6
  import xml.etree.ElementTree as ET
 
7
 
8
  # ---------------- Helper functions ----------------
9
  def get_rotated_rect_corners(x, y, w, h, rotation_deg):
@@ -31,20 +32,15 @@ def preprocess_gray_clahe(img):
31
 
32
  def detect_and_match(img1_gray, img2_gray, method="SIFT", ratio_thresh=0.78):
33
  if method == "SIFT":
34
- detector = cv2.SIFT_create(nfeatures=5000)
35
- norm = cv2.NORM_L2
36
  elif method == "ORB":
37
- detector = cv2.ORB_create(5000)
38
- norm = cv2.NORM_HAMMING
39
  elif method == "BRISK":
40
- detector = cv2.BRISK_create()
41
- norm = cv2.NORM_HAMMING
42
  elif method == "KAZE":
43
- detector = cv2.KAZE_create()
44
- norm = cv2.NORM_L2
45
  elif method == "AKAZE":
46
- detector = cv2.AKAZE_create()
47
- norm = cv2.NORM_HAMMING
48
  else:
49
  return None, None, [], None
50
 
@@ -60,10 +56,8 @@ def detect_and_match(img1_gray, img2_gray, method="SIFT", ratio_thresh=0.78):
60
  matches_img = None
61
  if len(good) >= 4:
62
  matches_img = cv2.drawMatches(
63
- cv2.cvtColor(img1_gray, cv2.COLOR_GRAY2BGR),
64
- kp1,
65
- cv2.cvtColor(img2_gray, cv2.COLOR_GRAY2BGR),
66
- kp2,
67
  good, None,
68
  flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS
69
  )
@@ -77,107 +71,126 @@ def add_title(img_bgr, title):
77
  return np.vstack([bar, img_bgr])
78
 
79
  def parse_xml_roi_points(xml_path):
80
- """Parse your XML structure, return list of polygons (Nx2 points)."""
81
- if xml_path is None:
82
- return None
83
-
84
  polys = []
85
- try:
86
- tree = ET.parse(xml_path)
87
- root = tree.getroot()
88
- # Transform ROI points (FourPoint)
89
- for tr in root.findall(".//transform"):
90
- pts = []
91
- for p in tr.findall("point"):
92
- x = float(p.get("x")); y = float(p.get("y"))
93
- pts.append([x, y])
94
- if len(pts) >= 3:
95
- polys.append(np.array(pts, dtype=np.float32))
96
- # Overlay polygons
97
- for ov in root.findall(".//overlay"):
98
- pts = []
99
- for p in ov.findall("point"):
100
- x = float(p.get("x")); y = float(p.get("y"))
101
- pts.append([x, y])
102
- if len(pts) >= 3:
103
- polys.append(np.array(pts, dtype=np.float32))
104
- except Exception as e:
105
- print("XML parse error:", e)
106
  return polys if polys else None
107
 
 
 
 
 
 
 
 
 
 
108
  # ---------------- Main Function ----------------
109
  def homography_all_detectors(flat_file, persp_file, json_file, xml_file):
110
- flat_img = cv2.imread(flat_file)
111
- persp_img = cv2.imread(persp_file)
112
-
113
- # FIX: Use the file paths directly (no .name needed)
114
- mockup = json.load(open(json_file))
115
- roi = mockup["printAreas"][0]
116
- roi_x, roi_y = roi["position"]["x"], roi["position"]["y"]
117
- roi_w, roi_h = roi["width"], roi["height"]
118
- roi_rot_deg = roi["rotation"]
119
-
120
- xml_polys = parse_xml_roi_points(xml_file) if xml_file else None
121
-
122
- flat_gray = preprocess_gray_clahe(flat_img)
123
- persp_gray = preprocess_gray_clahe(persp_img)
124
-
125
  results = []
126
  download_files = []
 
127
 
128
- for method in ["SIFT","ORB","BRISK","KAZE","AKAZE"]:
129
- kp1, kp2, good, matches_img = detect_and_match(flat_gray, persp_gray, method)
130
- if kp1 is None or len(good) < 4:
131
- continue
132
-
133
- src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2)
134
- dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,1,2)
135
- H, _ = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
136
-
137
- # top-left = flat
138
- top_left = add_title(flat_img.copy(), "Flat Image")
139
-
140
- # top-right = feature matches
141
- top_right = add_title(matches_img if matches_img is not None else flat_img.copy(),
142
- "Feature Matches (Flat→Perspective)")
143
-
144
- # bottom-left = homography ROI
145
- bottom_left = persp_img.copy()
146
- if H is not None:
147
- roi_corners = get_rotated_rect_corners(roi_x, roi_y, roi_w, roi_h, roi_rot_deg)
148
- roi_persp = cv2.perspectiveTransform(roi_corners.reshape(-1,1,2), H).reshape(-1,2)
149
- cv2.polylines(bottom_left, [roi_persp.astype(int)], True, (0,255,0), 3)
150
- bottom_left = add_title(bottom_left, "ROI via Homography (from JSON)")
151
-
152
- # bottom-right = XML ROI
153
- bottom_right = persp_img.copy()
154
- if xml_polys:
155
- for poly in xml_polys:
156
- cv2.polylines(bottom_right, [poly.astype(int)], True, (0,0,255), 3)
157
- else:
158
- cv2.putText(bottom_right, "No XML ROI", (50,100),
159
- cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 3)
160
- bottom_right = add_title(bottom_right, "Ground Truth ROI (XML)")
161
-
162
- # stack horizontally
163
- top = np.hstack([top_left, top_right])
164
- bottom = np.hstack([bottom_left, bottom_right])
165
- composite = np.vstack([top, bottom])
166
-
167
- base_name = os.path.splitext(os.path.basename(persp_file))[0]
168
- file_name = f"{base_name}_{method.lower()}_grid.png"
169
- cv2.imwrite(file_name, composite)
170
- results.append((cv2.cvtColor(composite, cv2.COLOR_BGR2RGB), f"{method} Grid"))
171
- download_files.append(file_name)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
 
173
  while len(download_files) < 5:
174
  download_files.append(None)
175
-
176
- # Return the results in the correct format
177
- gallery_output = results
178
- file_outputs = download_files[:5]
179
-
180
- return [gallery_output] + file_outputs
181
 
182
  # ---------------- Gradio UI ----------------
183
  iface = gr.Interface(
@@ -197,6 +210,7 @@ iface = gr.Interface(
197
  gr.File(label="Download AKAZE Grid")
198
  ],
199
  title="Homography ROI vs XML ROI",
200
- description="Each detector produces one 2×2 grid: Flat, Matches, Homography ROI, Ground Truth ROI."
201
  )
202
- iface.launch()
 
 
4
  import gradio as gr
5
  import os
6
  import xml.etree.ElementTree as ET
7
+ import traceback
8
 
9
  # ---------------- Helper functions ----------------
10
  def get_rotated_rect_corners(x, y, w, h, rotation_deg):
 
32
 
33
  def detect_and_match(img1_gray, img2_gray, method="SIFT", ratio_thresh=0.78):
34
  if method == "SIFT":
35
+ detector = cv2.SIFT_create(nfeatures=5000); norm = cv2.NORM_L2
 
36
  elif method == "ORB":
37
+ detector = cv2.ORB_create(5000); norm = cv2.NORM_HAMMING
 
38
  elif method == "BRISK":
39
+ detector = cv2.BRISK_create(); norm = cv2.NORM_HAMMING
 
40
  elif method == "KAZE":
41
+ detector = cv2.KAZE_create(); norm = cv2.NORM_L2
 
42
  elif method == "AKAZE":
43
+ detector = cv2.AKAZE_create(); norm = cv2.NORM_HAMMING
 
44
  else:
45
  return None, None, [], None
46
 
 
56
  matches_img = None
57
  if len(good) >= 4:
58
  matches_img = cv2.drawMatches(
59
+ cv2.cvtColor(img1_gray, cv2.COLOR_GRAY2BGR), kp1,
60
+ cv2.cvtColor(img2_gray, cv2.COLOR_GRAY2BGR), kp2,
 
 
61
  good, None,
62
  flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS
63
  )
 
71
  return np.vstack([bar, img_bgr])
72
 
73
  def parse_xml_roi_points(xml_path):
 
 
 
 
74
  polys = []
75
+ tree = ET.parse(xml_path)
76
+ root = tree.getroot()
77
+ for tr in root.findall(".//transform"):
78
+ pts = []
79
+ for p in tr.findall("point"):
80
+ x = float(p.get("x")); y = float(p.get("y"))
81
+ pts.append([x, y])
82
+ if len(pts) >= 3:
83
+ polys.append(np.array(pts, dtype=np.float32))
84
+ for ov in root.findall(".//overlay"):
85
+ pts = []
86
+ for p in ov.findall("point"):
87
+ x = float(p.get("x")); y = float(p.get("y"))
88
+ pts.append([x, y])
89
+ if len(pts) >= 3:
90
+ polys.append(np.array(pts, dtype=np.float32))
 
 
 
 
 
91
  return polys if polys else None
92
 
93
+ def make_error_image(msg, width=800, height=600):
94
+ img = np.ones((height, width, 3), dtype=np.uint8)*255
95
+ y0 = 100
96
+ for i, line in enumerate(msg.split("\n")):
97
+ y = y0 + i*40
98
+ cv2.putText(img, line, (50, y), cv2.FONT_HERSHEY_SIMPLEX,
99
+ 0.8, (0,0,255), 2, cv2.LINE_AA)
100
+ return img
101
+
102
  # ---------------- Main Function ----------------
103
  def homography_all_detectors(flat_file, persp_file, json_file, xml_file):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  results = []
105
  download_files = []
106
+ methods = ["SIFT","ORB","BRISK","KAZE","AKAZE"]
107
 
108
+ try:
109
+ flat_img = cv2.imread(flat_file)
110
+ persp_img = cv2.imread(persp_file)
111
+ if flat_img is None or persp_img is None:
112
+ raise ValueError("Could not read input images")
113
+
114
+ mockup = json.load(open(json_file.name))
115
+ roi = mockup["printAreas"][0]
116
+ roi_x, roi_y = roi["position"]["x"], roi["position"]["y"]
117
+ roi_w, roi_h = roi["width"], roi["height"]
118
+ roi_rot_deg = roi["rotation"]
119
+
120
+ xml_polys = parse_xml_roi_points(xml_file.name) if xml_file else None
121
+
122
+ flat_gray = preprocess_gray_clahe(flat_img)
123
+ persp_gray = preprocess_gray_clahe(persp_img)
124
+
125
+ for method in methods:
126
+ try:
127
+ kp1, kp2, good, matches_img = detect_and_match(flat_gray, persp_gray, method)
128
+ if kp1 is None or len(good) < 4:
129
+ raise ValueError(f"{method}: Not enough good matches")
130
+
131
+ src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2)
132
+ dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,1,2)
133
+ H, _ = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
134
+ if H is None:
135
+ raise ValueError(f"{method}: Homography could not be estimated")
136
+
137
+ # top-left = flat
138
+ top_left = add_title(flat_img.copy(), "Flat Image")
139
+ # top-right = matches
140
+ top_right = add_title(matches_img if matches_img is not None else flat_img.copy(),
141
+ "Feature Matches")
142
+ # bottom-left = homography ROI
143
+ bottom_left = persp_img.copy()
144
+ roi_corners = get_rotated_rect_corners(roi_x, roi_y, roi_w, roi_h, roi_rot_deg)
145
+ roi_persp = cv2.perspectiveTransform(roi_corners.reshape(-1,1,2), H).reshape(-1,2)
146
+ cv2.polylines(bottom_left, [roi_persp.astype(int)], True, (0,255,0), 3)
147
+ bottom_left = add_title(bottom_left, "ROI via Homography (JSON)")
148
+ # bottom-right = XML ROI
149
+ bottom_right = persp_img.copy()
150
+ if xml_polys:
151
+ for poly in xml_polys:
152
+ cv2.polylines(bottom_right, [poly.astype(int)], True, (0,0,255), 3)
153
+ else:
154
+ cv2.putText(bottom_right, "No XML ROI", (50,100),
155
+ cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 3)
156
+ bottom_right = add_title(bottom_right, "Ground Truth ROI (XML)")
157
+
158
+ top = np.hstack([top_left, top_right])
159
+ bottom = np.hstack([bottom_left, bottom_right])
160
+ composite = np.vstack([top, bottom])
161
+
162
+ base_name = os.path.splitext(os.path.basename(persp_file))[0]
163
+ file_name = f"{base_name}_{method.lower()}_grid.png"
164
+ cv2.imwrite(file_name, composite)
165
+ results.append((cv2.cvtColor(composite, cv2.COLOR_BGR2RGB), f"{method} Grid"))
166
+ download_files.append(file_name)
167
+
168
+ except Exception as e:
169
+ err_msg = f"Error in {method}: {str(e)}"
170
+ print(err_msg)
171
+ tb = traceback.format_exc()
172
+ err_img = make_error_image(err_msg + "\n" + tb)
173
+ results.append((cv2.cvtColor(err_img, cv2.COLOR_BGR2RGB), f"{method} ERROR"))
174
+ # make txt file also
175
+ err_file = f"{method.lower()}_error.txt"
176
+ with open(err_file, "w") as f:
177
+ f.write(err_msg + "\n\n" + tb)
178
+ download_files.append(err_file)
179
+
180
+ except Exception as e:
181
+ err_msg = f"Global Error: {str(e)}"
182
+ tb = traceback.format_exc()
183
+ err_img = make_error_image(err_msg + "\n" + tb)
184
+ results.append((cv2.cvtColor(err_img, cv2.COLOR_BGR2RGB), f"GLOBAL ERROR"))
185
+ err_file = "global_error.txt"
186
+ with open(err_file, "w") as f:
187
+ f.write(err_msg + "\n\n" + tb)
188
+ download_files = [err_file]*5
189
 
190
  while len(download_files) < 5:
191
  download_files.append(None)
192
+
193
+ return [results] + download_files[:5]
 
 
 
 
194
 
195
  # ---------------- Gradio UI ----------------
196
  iface = gr.Interface(
 
210
  gr.File(label="Download AKAZE Grid")
211
  ],
212
  title="Homography ROI vs XML ROI",
213
+ description="Each detector produces one 2×2 grid: Flat, Matches, Homography ROI, Ground Truth ROI. If any error occurs, it will be shown in the output image + downloadable txt log."
214
  )
215
+
216
+ iface.launch()