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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +130 -117
app.py CHANGED
@@ -4,7 +4,6 @@ import json
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,15 +31,20 @@ def preprocess_gray_clahe(img):
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,8 +60,10 @@ def detect_and_match(img1_gray, img2_gray, method="SIFT", ratio_thresh=0.78):
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
  )
@@ -70,127 +76,135 @@ def add_title(img_bgr, title):
70
  0.8, (0,0,0), 2, cv2.LINE_AA)
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,7 +224,6 @@ 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()
 
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
 
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
  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
  )
 
76
  0.8, (0,0,0), 2, cv2.LINE_AA)
77
  return np.vstack([bar, img_bgr])
78
 
79
+ def resize_to_height(img, target_height):
80
+ """Resize image to target height while maintaining aspect ratio"""
81
+ h, w = img.shape[:2]
82
+ ratio = target_height / h
83
+ new_width = int(w * ratio)
84
+ return cv2.resize(img, (new_width, target_height))
85
+
86
  def parse_xml_roi_points(xml_path):
87
+ """Parse your XML structure, return list of polygons (Nx2 points)."""
88
+ if xml_path is None:
89
+ return None
90
+
91
  polys = []
92
+ try:
93
+ tree = ET.parse(xml_path)
94
+ root = tree.getroot()
95
+ # Transform ROI points (FourPoint)
96
+ for tr in root.findall(".//transform"):
97
+ pts = []
98
+ for p in tr.findall("point"):
99
+ x = float(p.get("x")); y = float(p.get("y"))
100
+ pts.append([x, y])
101
+ if len(pts) >= 3:
102
+ polys.append(np.array(pts, dtype=np.float32))
103
+ # Overlay polygons
104
+ for ov in root.findall(".//overlay"):
105
+ pts = []
106
+ for p in ov.findall("point"):
107
+ x = float(p.get("x")); y = float(p.get("y"))
108
+ pts.append([x, y])
109
+ if len(pts) >= 3:
110
+ polys.append(np.array(pts, dtype=np.float32))
111
+ except Exception as e:
112
+ print("XML parse error:", e)
113
  return polys if polys else None
114
 
 
 
 
 
 
 
 
 
 
115
  # ---------------- Main Function ----------------
116
  def homography_all_detectors(flat_file, persp_file, json_file, xml_file):
117
+ flat_img = cv2.imread(flat_file)
118
+ persp_img = cv2.imread(persp_file)
119
+
120
+ # Use the file paths directly (no .name needed)
121
+ mockup = json.load(open(json_file))
122
+ roi = mockup["printAreas"][0]
123
+ roi_x, roi_y = roi["position"]["x"], roi["position"]["y"]
124
+ roi_w, roi_h = roi["width"], roi["height"]
125
+ roi_rot_deg = roi["rotation"]
126
+
127
+ xml_polys = parse_xml_roi_points(xml_file) if xml_file else None
128
+
129
+ flat_gray = preprocess_gray_clahe(flat_img)
130
+ persp_gray = preprocess_gray_clahe(persp_img)
131
+
132
  results = []
133
  download_files = []
 
134
 
135
+ for method in ["SIFT","ORB","BRISK","KAZE","AKAZE"]:
136
+ try:
137
+ kp1, kp2, good, matches_img = detect_and_match(flat_gray, persp_gray, method)
138
+ if kp1 is None or len(good) < 4:
139
+ continue
140
+
141
+ src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2)
142
+ dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,1,2)
143
+ H, _ = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
144
+
145
+ # top-left = flat
146
+ top_left = add_title(flat_img.copy(), "Flat Image")
147
+
148
+ # top-right = feature matches
149
+ if matches_img is None:
150
+ matches_img = flat_img.copy()
151
+ top_right = add_title(matches_img, "Feature Matches (Flat→Perspective)")
152
+
153
+ # bottom-left = homography ROI
154
+ bottom_left = persp_img.copy()
155
+ if H is not None:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  roi_corners = get_rotated_rect_corners(roi_x, roi_y, roi_w, roi_h, roi_rot_deg)
157
  roi_persp = cv2.perspectiveTransform(roi_corners.reshape(-1,1,2), H).reshape(-1,2)
158
  cv2.polylines(bottom_left, [roi_persp.astype(int)], True, (0,255,0), 3)
159
+ bottom_left = add_title(bottom_left, "ROI via Homography (from JSON)")
160
+
161
+ # bottom-right = XML ROI
162
+ bottom_right = persp_img.copy()
163
+ if xml_polys:
164
+ for poly in xml_polys:
165
+ cv2.polylines(bottom_right, [poly.astype(int)], True, (0,0,255), 3)
166
+ else:
167
+ cv2.putText(bottom_right, "No XML ROI", (50,100),
168
+ cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 3)
169
+ bottom_right = add_title(bottom_right, "Ground Truth ROI (XML)")
170
+
171
+ # Resize all images to the same height before stacking
172
+ target_height = max(top_left.shape[0], top_right.shape[0],
173
+ bottom_left.shape[0], bottom_right.shape[0])
174
+
175
+ top_left_resized = resize_to_height(top_left, target_height)
176
+ top_right_resized = resize_to_height(top_right, target_height)
177
+ bottom_left_resized = resize_to_height(bottom_left, target_height)
178
+ bottom_right_resized = resize_to_height(bottom_right, target_height)
179
+
180
+ # stack horizontally
181
+ top = np.hstack([top_left_resized, top_right_resized])
182
+ bottom = np.hstack([bottom_left_resized, bottom_right_resized])
183
+
184
+ # Make sure top and bottom have the same width
185
+ min_width = min(top.shape[1], bottom.shape[1])
186
+ top = cv2.resize(top, (min_width, top.shape[0]))
187
+ bottom = cv2.resize(bottom, (min_width, bottom.shape[0]))
188
+
189
+ composite = np.vstack([top, bottom])
190
+
191
+ base_name = os.path.splitext(os.path.basename(persp_file))[0]
192
+ file_name = f"{base_name}_{method.lower()}_grid.png"
193
+ cv2.imwrite(file_name, composite)
194
+ results.append((cv2.cvtColor(composite, cv2.COLOR_BGR2RGB), f"{method} Grid"))
195
+ download_files.append(file_name)
196
+ except Exception as e:
197
+ print(f"Error in {method}: {str(e)}")
198
+ continue
 
 
199
 
200
  while len(download_files) < 5:
201
  download_files.append(None)
202
+
203
+ # Return the results in the correct format
204
+ gallery_output = results
205
+ file_outputs = download_files[:5]
206
+
207
+ return [gallery_output] + file_outputs
208
 
209
  # ---------------- Gradio UI ----------------
210
  iface = gr.Interface(
 
224
  gr.File(label="Download AKAZE Grid")
225
  ],
226
  title="Homography ROI vs XML ROI",
227
+ description="Each detector produces one 2×2 grid: Flat, Matches, Homography ROI, Ground Truth ROI."
228
  )
229
+ iface.launch()