diff --git a/fourcell/notes/brute_force_scale_invariant_template_matching.py b/fourcell/notes/brute_force_scale_invariant_template_matching.py new file mode 100644 index 0000000..271b3cf --- /dev/null +++ b/fourcell/notes/brute_force_scale_invariant_template_matching.py @@ -0,0 +1,81 @@ +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() \ No newline at end of file diff --git a/fourcell/notes/sift.py b/fourcell/notes/sift.py new file mode 100644 index 0000000..dfdc314 --- /dev/null +++ b/fourcell/notes/sift.py @@ -0,0 +1,33 @@ +# 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() \ No newline at end of file