Spaces:
Running
Running
| """ | |
| Image based feature alignment | |
| Credits: https://www.learnopencv.com/image-alignment-feature-based-using-opencv-c-python/ | |
| """ | |
| import cv2 | |
| import numpy as np | |
| from src.processors.interfaces.ImagePreprocessor import ImagePreprocessor | |
| from src.utils.image import ImageUtils | |
| from src.utils.interaction import InteractionUtils | |
| class FeatureBasedAlignment(ImagePreprocessor): | |
| def __init__(self, *args, **kwargs): | |
| super().__init__(*args, **kwargs) | |
| options = self.options | |
| config = self.tuning_config | |
| # process reference image | |
| self.ref_path = self.relative_dir.joinpath(options["reference"]) | |
| ref_img = cv2.imread(str(self.ref_path), cv2.IMREAD_GRAYSCALE) | |
| self.ref_img = ImageUtils.resize_util( | |
| ref_img, | |
| config.dimensions.processing_width, | |
| config.dimensions.processing_height, | |
| ) | |
| # get options with defaults | |
| self.max_features = int(options.get("maxFeatures", 500)) | |
| self.good_match_percent = options.get("goodMatchPercent", 0.15) | |
| self.transform_2_d = options.get("2d", False) | |
| # Extract keypoints and description of source image | |
| self.orb = cv2.ORB_create(self.max_features) | |
| self.to_keypoints, self.to_descriptors = self.orb.detectAndCompute( | |
| self.ref_img, None | |
| ) | |
| def __str__(self): | |
| return self.ref_path.name | |
| def exclude_files(self): | |
| return [self.ref_path] | |
| def apply_filter(self, image, _file_path): | |
| config = self.tuning_config | |
| # Convert images to grayscale | |
| # im1Gray = cv2.cvtColor(im1, cv2.COLOR_BGR2GRAY) | |
| # im2Gray = cv2.cvtColor(im2, cv2.COLOR_BGR2GRAY) | |
| image = cv2.normalize(image, 0, 255, norm_type=cv2.NORM_MINMAX) | |
| # Detect ORB features and compute descriptors. | |
| from_keypoints, from_descriptors = self.orb.detectAndCompute(image, None) | |
| # Match features. | |
| matcher = cv2.DescriptorMatcher_create( | |
| cv2.DESCRIPTOR_MATCHER_BRUTEFORCE_HAMMING | |
| ) | |
| # create BFMatcher object (alternate matcher) | |
| # matcher = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) | |
| matches = np.array(matcher.match(from_descriptors, self.to_descriptors, None)) | |
| # Sort matches by score | |
| matches = sorted(matches, key=lambda x: x.distance, reverse=False) | |
| # Remove not so good matches | |
| num_good_matches = int(len(matches) * self.good_match_percent) | |
| matches = matches[:num_good_matches] | |
| # Draw top matches | |
| if config.outputs.show_image_level > 2: | |
| im_matches = cv2.drawMatches( | |
| image, from_keypoints, self.ref_img, self.to_keypoints, matches, None | |
| ) | |
| InteractionUtils.show("Aligning", im_matches, resize=True, config=config) | |
| # Extract location of good matches | |
| points1 = np.zeros((len(matches), 2), dtype=np.float32) | |
| points2 = np.zeros((len(matches), 2), dtype=np.float32) | |
| for i, match in enumerate(matches): | |
| points1[i, :] = from_keypoints[match.queryIdx].pt | |
| points2[i, :] = self.to_keypoints[match.trainIdx].pt | |
| # Find homography | |
| height, width = self.ref_img.shape | |
| if self.transform_2_d: | |
| m, _inliers = cv2.estimateAffine2D(points1, points2) | |
| return cv2.warpAffine(image, m, (width, height)) | |
| # Use homography | |
| h, _mask = cv2.findHomography(points1, points2, cv2.RANSAC) | |
| return cv2.warpPerspective(image, h, (width, height)) | |