import gradio as gr import cv2 import numpy as np import matplotlib.pyplot as plt import json import math import os def ransac(image1, image2, detector_type): """ Finds the homography matrix using the RANSAC algorithm with the selected feature detector. """ gray1 = cv2.cvtColor(image1, cv2.COLOR_RGB2GRAY) gray2 = cv2.cvtColor(image2, cv2.COLOR_RGB2GRAY) if detector_type == "SIFT": detector = cv2.SIFT_create() matcher = cv2.FlannBasedMatcher(dict(algorithm=1, trees=5), dict(checks=50)) elif detector_type == "ORB": detector = cv2.ORB_create() matcher = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) elif detector_type == "BRISK": detector = cv2.BRISK_create() matcher = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) elif detector_type == "AKAZE": detector = cv2.AKAZE_create() matcher = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) elif detector_type == "KAZE": detector = cv2.KAZE_create() matcher = cv2.BFMatcher(cv2.NORM_L2, crossCheck=True) else: return None kp1, des1 = detector.detectAndCompute(gray1, None) kp2, des2 = detector.detectAndCompute(gray2, None) if des1 is None or des2 is None or len(kp1) < 2 or len(kp2) < 2: return None try: if detector_type == "SIFT": matches = matcher.knnMatch(des1, des2, k=2) good_matches = [] if matches: for m, n in matches: if m.distance < 0.75 * n.distance: good_matches.append(m) else: matches = matcher.match(des1, des2) good_matches = sorted(matches, key=lambda x: x.distance) except cv2.error as e: print(f"Error during matching: {e}") return None if len(good_matches) > 10: src_pts = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2) dst_pts = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2) H, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0) return H else: return None def get_bounding_box_points(json_data): """ Extracts and calculates the four corner points of the bounding box, assuming x,y are top-left. """ print_area = json_data['printAreas'][0] x = print_area['position']['x'] y = print_area['position']['y'] w = print_area['width'] h = print_area['height'] rotation_deg = print_area['rotation'] points = np.float32([ [0, 0], [w, 0], [w, h], [0, h] ]).reshape(-1, 1, 2) rotation_rad = math.radians(rotation_deg) cos_theta = math.cos(rotation_rad) sin_theta = math.sin(rotation_rad) rotation_matrix = np.array([ [cos_theta, -sin_theta], [sin_theta, cos_theta] ]) rotated_points = np.dot(points.reshape(-1, 2), rotation_matrix.T) final_points = rotated_points + np.array([x, y]) return final_points.reshape(-1, 1, 2) def process_and_plot_all_detectors(image1_np, image2_np, json_file): """ Processes the images with all available detectors and returns image data for display and download. """ if image1_np is None or image2_np is None: return [None] * 6 try: with open(json_file.name, 'r') as f: data = json.load(f) except Exception as e: print(f"Error: Could not read JSON file. {e}") return [None] * 6 detectors = ["SIFT", "ORB", "BRISK", "AKAZE", "KAZE"] gallery_images = [] download_files = [None] * 5 for i, detector_type in enumerate(detectors): H = ransac(image1_np, image2_np, detector_type) if H is not None: box_points = get_bounding_box_points(data) output_flat_img = image1_np.copy() cv2.polylines(output_flat_img, [np.int32(box_points)], isClosed=True, color=(0, 0, 255), thickness=5) transformed_box_points = cv2.perspectiveTransform(box_points, H) output_perspective_img = image2_np.copy() cv2.polylines(output_perspective_img, [np.int32(transformed_box_points)], isClosed=True, color=(0, 0, 255), thickness=5) fig, axes = plt.subplots(1, 3, figsize=(18, 6)) axes[0].imshow(cv2.cvtColor(output_flat_img, cv2.COLOR_BGR2RGB)) axes[0].set_title(f'Original (Flat) - {detector_type}') axes[0].axis('off') axes[1].imshow(cv2.cvtColor(image2_np, cv2.COLOR_BGR2RGB)) axes[1].set_title('Original (Perspective)') axes[1].axis('off') axes[2].imshow(cv2.cvtColor(output_perspective_img, cv2.COLOR_BGR2RGB)) axes[2].set_title('Projected Bounding Box') axes[2].axis('off') plt.tight_layout() file_name = f"result_{detector_type.lower()}.png" plt.savefig(file_name) plt.close(fig) gallery_images.append(file_name) download_files[i] = file_name else: print(f"Warning: Homography matrix could not be found with {detector_type} detector. Skipping this result.") # We don't append None to the gallery_images list to avoid the error. # download_files[i] remains None, which is handled correctly by gr.File. return [gallery_images] + download_files iface = gr.Interface( fn=process_and_plot_all_detectors, inputs=[ gr.Image(type="numpy", label="Image 1 (Flat)"), gr.Image(type="numpy", label="Image 2 (Perspective)"), gr.File(type="filepath", label="JSON File") ], outputs=[ gr.Gallery(label="Results"), gr.File(label="Download SIFT Result"), gr.File(label="Download ORB Result"), gr.File(label="Download BRISK Result"), gr.File(label="Download AKAZE Result"), gr.File(label="Download KAZE Result") ], title="Homography and Bounding Box Projection with All Detectors", description="Upload two images and a JSON file to see the bounding box projection for all 5 feature extraction methods. Each result can be downloaded separately." ) iface.launch()