I’m trying to use OpenCV to line up a scanned in document with the document that we were expecting back and sometimes when scanning in a multi page document and the pages are out of order and the result is a spectacular failure. Like visually I can identify such failures pretty quick but idk how to identify them programmatically.
First, here’s an examples of one of these spectacular failures:
This question was originally posed on stackoverflow a few years ago and the superficially most helpful response was this one:
A normal homography will have the xx/yy coefficients close to 1, and the xy/yx close to 0 (exactly 0 if there is no rotation). The coefficients in the denominator should also be small (0 if no perspective). Observe typical values to detect pathological situations.
Only problem: idk how to do that.
Here’s my code:
import sys
import cv2
import numpy as np
if len(sys.argv) != 4:
print('USAGE')
print(' python3 align.py ref.png new.png output.png')
sys.exit()
FLANN_INDEX_LSH = 6
def filter_matches(kp1, kp2, matches, ratio = 0.75):
mkp1, mkp2 = [], []
for m in matches:
if len(m) == 2 and m[0].distance < m[1].distance * ratio:
m = m[0]
mkp1.append( kp1[m.queryIdx] )
mkp2.append( kp2[m.trainIdx] )
p1 = np.float32([kp.pt for kp in mkp1])
p2 = np.float32([kp.pt for kp in mkp2])
kp_pairs = zip(mkp1, mkp2)
return p1, p2, list(kp_pairs)
def alignImages(im1, im2):
detector = cv2.AKAZE_create()
flann_params= dict(algorithm = FLANN_INDEX_LSH,
table_number = 6, # 12
key_size = 12, # 20
multi_probe_level = 1) #2
matcher = cv2.FlannBasedMatcher(flann_params, {})
kp1, desc1 = detector.detectAndCompute(im1, None)
kp2, desc2 = detector.detectAndCompute(im2, None)
raw_matches = matcher.knnMatch(desc1, trainDescriptors = desc2, k = 2)
p1, p2, kp_pairs = filter_matches(kp1, kp2, raw_matches)
if len(p1) < 4:
print('%d matches found, not enough for homography estimation' % len(p1))
sys.exit()
H, matches = cv2.findHomography(p1, p2, cv2.RANSAC, 5.0)
print(str(len(matches.ravel().tolist())));
height, width = im2.shape
imResult = cv2.warpPerspective(im1, H, (width, height))
return imResult
refFilename = sys.argv[1]
imFilename = sys.argv[2]
outFilename = sys.argv[3]
imRef = cv2.imread(refFilename, cv2.IMREAD_GRAYSCALE)
im = cv2.imread(imFilename, cv2.IMREAD_GRAYSCALE)
imNew = alignImages(im, imRef)
cv2.imwrite(outFilename, imNew)