L3gionOfDooM
L3gionOfDooM

Reputation: 75

OpenCV Feature Matching multiple similar objects in an image

I currently have a project in which I need to find the circled X's as listed in the photo using OpenCV and Python. I have tried using template matching and feature matching, however I am only able to get the one X that I cropped out of the photo to use as a query image. The query photo is not exact to the other X's, but it is quite similar so I am confused as to why feature matching won't detect the others. This code was pulled from another tutorial, but I can't seem to make this work. Please help!

The Current Code:

    import cv2
from matplotlib import pyplot as plt

MIN_MATCH_COUNT = 3

img1 = cv2.imread('template.jpg', 0)  # queryImage
img2 = cv2.imread('originalPic.jpg', 0) # trainImage

orb = cv2.ORB_create(10000, 1.2, nlevels=8, edgeThreshold = 5)

# find the keypoints and descriptors with ORB
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)

import numpy as np
from sklearn.cluster import MeanShift, estimate_bandwidth

x = np.array([kp2[0].pt])

for i in range(len(kp2)):
    x = np.append(x, [kp2[i].pt], axis=0)

x = x[1:len(x)]

bandwidth = estimate_bandwidth(x, quantile=0.1, n_samples=500)

ms = MeanShift(bandwidth=bandwidth, bin_seeding=True, cluster_all=True)
ms.fit(x)
labels = ms.labels_
cluster_centers = ms.cluster_centers_

labels_unique = np.unique(labels)
n_clusters_ = len(labels_unique)
print("number of estimated clusters : %d" % n_clusters_)

s = [None] * n_clusters_
for i in range(n_clusters_):
    l = ms.labels_
    d, = np.where(l == i)
    print(d.__len__())
    s[i] = list(kp2[xx] for xx in d)

des2_ = des2

for i in range(n_clusters_):

    kp2 = s[i]
    l = ms.labels_
    d, = np.where(l == i)
    des2 = des2_[d, ]

    FLANN_INDEX_KDTREE = 0
    index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
    search_params = dict(checks = 50)

    flann = cv2.FlannBasedMatcher(index_params, search_params)

    des1 = np.float32(des1)
    des2 = np.float32(des2)

    matches = flann.knnMatch(des1, des2, 2)

    # store all the good matches as per Lowe's ratio test.
    good = []
    for m,n in matches:
        if m.distance < 0.7*n.distance:
            good.append(m)

    if len(good)>3:
        src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2)
        dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2)

        M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 2)

        if M is None:
            print ("No Homography")
        else:
            matchesMask = mask.ravel().tolist()

            h,w = img1.shape
            pts = np.float32([ [0,0],[0,h-1],[w-1,h-1],[w-1,0] ]).reshape(-1,1,2)
            dst = cv2.perspectiveTransform(pts,M)

            img2 = cv2.polylines(img2,[np.int32(dst)],True,255,3, cv2.LINE_AA)

            draw_params = dict(matchColor=(0, 255, 0),  # draw matches in green color
                               singlePointColor=None,
                               matchesMask=matchesMask,  # draw only inliers
                               flags=2)

            img3 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params)

            plt.imshow(img3, 'gray'), plt.show()

    else:
        print ("Not enough matches are found - %d/%d" % (len(good),MIN_MATCH_COUNT))
        matchesMask = None

Query Object | Image To Search Through

Upvotes: 3

Views: 4295

Answers (1)

Dlamini
Dlamini

Reputation: 285

This is a simpler way, using only openCV and numpy. Since the size of your query image was much less than the train image size, I first downsized the train image by a factor of 0.33 to fit on my screen, and then created a function to iterate through various sizes of the query image because for this method you have to match sizes too.

You can, of course adjust the variable fx and fy, mult and threshold to see how many of the Xs you can get. My highest number was 3 from rough iterations, but this setting below achieves 2:

import cv2
import numpy as np

originalPicRead = cv2.imread('originalPic.jpg')
img_bgr = cv2.resize(originalPicRead, (0,0), fx=0.33, fy=0.33)
img_gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)

templateR = cv2.imread('template.jpg',0)

w,h = templateR.shape[::-1]

for magn in range(1,11):
    mult = magn*0.35
    w,h = int(mult*w),int(mult*h)
    template = cv2.resize(templateR, (0,0), fx=mult, fy = mult)

    res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
    threshold = 0.35
    loc = np.where(res >= threshold)

    for pt in zip(*loc[::-1]):
        cv2.rectangle(img_bgr, pt, (pt[0]+w, pt[1]+h), (0,255,255), 2)

cv2.imshow('Detected', img_bgr)
cv2.waitKey(0)
cv2.destroyAllWindows()

Upvotes: 2

Related Questions