Compare commits
No commits in common. "6ae5797ebb02b8e6ef8b928c2bd747cdc647e9ce" and "2798729373a0ca560e858aebb8beb11561886929" have entirely different histories.
6ae5797ebb
...
2798729373
|
@ -295,6 +295,9 @@ def create_blank (w, h, rgb_color = (255, 255, 255)) :
|
||||||
blank[:] = color
|
blank[:] = color
|
||||||
return blank
|
return blank
|
||||||
|
|
||||||
|
def place (bottom, top, x, y) :
|
||||||
|
return bottom.paste(top, (x, y))
|
||||||
|
|
||||||
def get_mean_rect (holePunches) :
|
def get_mean_rect (holePunches) :
|
||||||
left = 0
|
left = 0
|
||||||
right = 0
|
right = 0
|
||||||
|
@ -305,7 +308,7 @@ def get_mean_rect (holePunches) :
|
||||||
left += float(hp['x'])
|
left += float(hp['x'])
|
||||||
top += float(hp['y'])
|
top += float(hp['y'])
|
||||||
elif hp['order'] == 2 :
|
elif hp['order'] == 2 :
|
||||||
right += float(hp['x'])
|
right += float(hp['y'])
|
||||||
top += float(hp['y'])
|
top += float(hp['y'])
|
||||||
elif hp['order'] == 3 :
|
elif hp['order'] == 3 :
|
||||||
left += float(hp['x'])
|
left += float(hp['x'])
|
||||||
|
@ -313,9 +316,9 @@ def get_mean_rect (holePunches) :
|
||||||
elif hp['order'] == 5 :
|
elif hp['order'] == 5 :
|
||||||
right += float(hp['x'])
|
right += float(hp['x'])
|
||||||
bottom += float(hp['y'])
|
bottom += float(hp['y'])
|
||||||
w = round((right / 2.0) - (left / 2.0))
|
x = round((right / 2.0) - (left / 2.0))
|
||||||
h = round((bottom / 2.0) - (top / 2.0))
|
y = round((bottom / 2.0) - (top / 2.0))
|
||||||
return (w, h)
|
return (x, y)
|
||||||
|
|
||||||
|
|
||||||
def center_within (larger, smaller) :
|
def center_within (larger, smaller) :
|
||||||
|
@ -325,19 +328,7 @@ def center_within (larger, smaller) :
|
||||||
h2 = smaller[1]
|
h2 = smaller[1]
|
||||||
x = ((w1 - w2) / 2)
|
x = ((w1 - w2) / 2)
|
||||||
y = ((h1 - h2) / 2)
|
y = ((h1 - h2) / 2)
|
||||||
return (int(x), int(y))
|
return (x, y)
|
||||||
|
|
||||||
# If we consider (0,0) as top left corner of image called
|
|
||||||
# im with left-to-right as x direction and top-to-bottom
|
|
||||||
# as y direction. and we have (x1,y1) as the top-left vertex
|
|
||||||
# and (x2,y2) as the bottom-right vertex of a rectangle
|
|
||||||
# region within that image, then:
|
|
||||||
#
|
|
||||||
# roi = im[y1:y2, x1:x2]
|
|
||||||
|
|
||||||
def crop (img, xoffset, yoffset, w, h) :
|
|
||||||
#crop_img = img[y:y+h, x:x+w].copy()
|
|
||||||
return im[yoffset:yoffset+w, xoffset:xoffset+w].copy()
|
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print('Please provide path of scan to normalize')
|
print('Please provide path of scan to normalize')
|
||||||
|
@ -367,7 +358,6 @@ normalHeight = round(float(width) / pageRatio)
|
||||||
|
|
||||||
holePunches = find_hole_punches(img)
|
holePunches = find_hole_punches(img)
|
||||||
rotated = correct_rotation(img, original, holePunches)
|
rotated = correct_rotation(img, original, holePunches)
|
||||||
rotatedHeight, rotatedWidth = rotated.shape[:2]
|
|
||||||
|
|
||||||
holePunches = find_hole_punches(rotated)
|
holePunches = find_hole_punches(rotated)
|
||||||
blank = create_blank(width, normalHeight)
|
blank = create_blank(width, normalHeight)
|
||||||
|
@ -388,13 +378,11 @@ print(f'Mean rectangle: {meanRect[0]},{meanRect[1]}')
|
||||||
# offset is the position within the new normal image
|
# offset is the position within the new normal image
|
||||||
# the top left hole punch should be centered to
|
# the top left hole punch should be centered to
|
||||||
offset = center_within((width, normalHeight), meanRect)
|
offset = center_within((width, normalHeight), meanRect)
|
||||||
print(f'Offset : {offset[0]},{offset[1]}')
|
print(f'Offset: {offset[0]},{offset[1]}')
|
||||||
print(f'Topleft: {tl["x"]},{tl["y"]}')
|
|
||||||
print(f'Rotated: {rotatedWidth},{rotatedHeight}')
|
|
||||||
print(f'Blank : {width},{normalHeight}')
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
display(img)
|
||||||
#normal = place(blank, rotated, 0, 0)
|
#normal = place(blank, rotated, 0, 0)
|
||||||
|
|
||||||
display(blank)
|
#display(normal)
|
|
@ -1,81 +0,0 @@
|
||||||
import math
|
|
||||||
import numpy
|
|
||||||
import scipy.ndimage
|
|
||||||
import imagecodecs
|
|
||||||
import imreg
|
|
||||||
from matplotlib import pyplot, patches
|
|
||||||
|
|
||||||
|
|
||||||
def brute_force_scale_invariant_template_matching(
|
|
||||||
template, # grayscale image
|
|
||||||
search, # scaled and cropped grayscale image
|
|
||||||
zooms=(1.0, 0.5, 0.25), # sequence of zoom factors to try
|
|
||||||
size=None, # power-of-two size of square sliding window
|
|
||||||
delta=None, # advance of sliding windows. default: half window size
|
|
||||||
min_overlap=0.25, # minimum overlap of search with window
|
|
||||||
max_diff=0.05, # max average of search - window differences in overlap
|
|
||||||
max_angle=0.5, # no rotation
|
|
||||||
):
|
|
||||||
"""Return yoffset, xoffset, and scale of first match of search in template.
|
|
||||||
|
|
||||||
Iterate over scaled versions of the template image in overlapping sliding
|
|
||||||
windows and run FFT-based algorithm for translation, rotation and
|
|
||||||
scale-invariant image registration until a match of the search image is
|
|
||||||
found in the sliding window.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if size is None:
|
|
||||||
size = int(pow(2, int(math.log(min(search.shape), 2))))
|
|
||||||
if delta is None:
|
|
||||||
delta = size // 2
|
|
||||||
search = search[:size, :size]
|
|
||||||
for zoom in zooms:
|
|
||||||
windows = numpy.lib.stride_tricks.sliding_window_view(
|
|
||||||
scipy.ndimage.zoom(template, zoom), search.shape
|
|
||||||
)[::delta, ::delta]
|
|
||||||
for i in range(windows.shape[0]):
|
|
||||||
for j in range(windows.shape[1]):
|
|
||||||
print('.', end='')
|
|
||||||
window = windows[i, j]
|
|
||||||
im2, scale, angle, (t0, t1) = imreg.similarity(window, search)
|
|
||||||
diff = numpy.abs(im2 - window)[im2 != 0]
|
|
||||||
if (
|
|
||||||
abs(angle) < max_angle
|
|
||||||
and diff.size / window.size > min_overlap
|
|
||||||
and numpy.mean(diff) < max_diff
|
|
||||||
):
|
|
||||||
return (
|
|
||||||
(i * delta - t0) / zoom,
|
|
||||||
(j * delta - t1) / zoom,
|
|
||||||
1 / scale / zoom,
|
|
||||||
)
|
|
||||||
raise ValueError('no match of search image found in template')
|
|
||||||
|
|
||||||
|
|
||||||
def rgb2gray(rgb, scale=None):
|
|
||||||
"""Return float grayscale image from RGB24 or RGB48 image."""
|
|
||||||
scale = numpy.iinfo(rgb.dtype).max if scale is None else scale
|
|
||||||
scale = numpy.array([[[0.299, 0.587, 0.114]]], numpy.float32) / scale
|
|
||||||
return numpy.sum(rgb * scale, axis=-1)
|
|
||||||
|
|
||||||
|
|
||||||
template = imagecodecs.imread('cw1_IMG_9037.jpg')
|
|
||||||
search = imagecodecs.imread('cw1_p1_9037_kzw.jpg')
|
|
||||||
|
|
||||||
yoffset, xoffset, scale = brute_force_scale_invariant_template_matching(
|
|
||||||
rgb2gray(template), rgb2gray(search), zooms=(0.5,)
|
|
||||||
)
|
|
||||||
print(yoffset, xoffset, scale)
|
|
||||||
|
|
||||||
figure, ax = pyplot.subplots()
|
|
||||||
ax.imshow(template)
|
|
||||||
rect = patches.Rectangle(
|
|
||||||
(xoffset, yoffset),
|
|
||||||
scale * search.shape[1],
|
|
||||||
scale * search.shape[0],
|
|
||||||
linewidth=1,
|
|
||||||
edgecolor='r',
|
|
||||||
facecolor='none',
|
|
||||||
)
|
|
||||||
ax.add_patch(rect)
|
|
||||||
pyplot.show()
|
|
|
@ -1,33 +0,0 @@
|
||||||
# Initiate SIFT detector
|
|
||||||
sift = cv2.SIFT_create()
|
|
||||||
# find the keypoints and descriptors with SIFT
|
|
||||||
# Here img1 and img2 are grayscale images
|
|
||||||
kp1, des1 = sift.detectAndCompute(img1,None)
|
|
||||||
kp2, des2 = sift.detectAndCompute(img2,None)
|
|
||||||
|
|
||||||
# FLANN parameters
|
|
||||||
# I literally copy-pasted the defaults
|
|
||||||
FLANN_INDEX_KDTREE = 1
|
|
||||||
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
|
|
||||||
search_params = dict(checks=50) # or pass empty dictionary
|
|
||||||
# do the matching
|
|
||||||
flann = cv2.FlannBasedMatcher(index_params,search_params)
|
|
||||||
matches = flann.knnMatch(des1,des2,k=2)
|
|
||||||
|
|
||||||
## OPTIONAL - Show matches ##
|
|
||||||
|
|
||||||
# Need to draw only good matches, so create a mask
|
|
||||||
matchesMask = [[0,0] for i in range(len(matches))]
|
|
||||||
# ratio test as per Lowe's paper <- this is a criterion for matches selection
|
|
||||||
for i,(m,n) in enumerate(matches):
|
|
||||||
if m.distance < 0.7*n.distance:
|
|
||||||
matchesMask[i]=[1,0]
|
|
||||||
draw_params = dict(matchColor = (0,255,0),
|
|
||||||
singlePointColor = (255,0,0),
|
|
||||||
matchesMask = matchesMask,
|
|
||||||
flags = cv2.DrawMatchesFlags_DEFAULT)
|
|
||||||
|
|
||||||
img3 = cv2.drawMatchesKnn(img1,kp1,img2,kp2,matches,None, **draw_params)
|
|
||||||
f, ax = plt.subplots(1, figsize=(15,15))
|
|
||||||
ax.imshow(img3)
|
|
||||||
plt.show()
|
|
Loading…
Reference in New Issue