FourZeroFive
FourZeroFive

Reputation: 133

Multiple occurrences of bounding boxes around a same location

I implemented the following code, to match nodes in a plant, using as template a small cropped image.

img_rgb = cv2.imread('Exp.2 - Florestópolis, 35DAE, 2017-2018, T4, R2, P4.jpg')
img_rgb = cv2.medianBlur(img_rgb, 7)

img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)

template_image = cv2.imread('TemplateNode.jpg',0)
template_image = cv2.medianBlur(template_image, 5)
width, height = template_image.shape[::-1]

res = cv2.matchTemplate(img_gray, template_image, cv2.TM_CCOEFF_NORMED)

threshold =  0.6

locations = np.where(res >= threshold)

for position_tuple in zip(*locations[::-1]):

        cv2.rectangle(img_rgb, position_tuple, (position_tuple[0] + width, position_tuple[1] + height), (0,255,0), 1)

However, it generates too many bounding boxes (tuples), around a same location, as show:

Output Image from Template Matching

So, there is a work around to solve this issue?

Upvotes: 0

Views: 476

Answers (1)

MjH
MjH

Reputation: 1570

Here is one possible approach. Code is unlikely an efficient one. I think k-means clustering from some package may work better. Idea is to group together locations, which are too close:

def group_locations(locations, min_radius):
    x = locations[:, 0][ : , None]
    dist_x = x - x.T
    y = locations[:, 1][ : , None]
    dist_y = y - y.T
    dist = np.sqrt(dist_x**2 + dist_y**2)
    np.fill_diagonal(dist, min_radius+1)
    too_close = np.nonzero(dist <= min_radius)
    groups = []
    points = np.arange(len(locations))
    i = 0
    j = 0
    while i < len(points):
        groups.append([points[i]])
        for j in range(len(too_close[0])):
            if too_close[0][j] == points[i]:
                groups[i].append(too_close[1][j])
                points = np.delete(points, np.nonzero(points == too_close[1][j]))
        i += 1

    new_locations = []
    for group in groups:
        new_locations.append(np.mean(locations[group], axis=0))

    return np.array(new_locations)

So you take your locations and group them before plotting:

locations = []
size = 600
for _ in range(50):
    locations.append((random.randint(0, size), random.randint(0, size)))

locations = np.array(locations)
min_radius = 50

new_locations = group_locations(locations, min_radius)
#I run it second time as sometimes locations form chains which are longer than radius 
new_locations = group_locations(new_locations, min_radius) 
plt.scatter(locations[:, 0], locations[:, 1], c='blue', label='Original locations')
plt.scatter(new_locations[:, 0], new_locations[:, 1], c='red', marker='x', label='New grouped locations')
plt.legend()
plt.show()

enter image description here


actually tried it with image provided

img_rgb = cv2.imread('obsvu.jpg')
img_rgb = cv2.medianBlur(img_rgb, 7)

img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)

template_image = cv2.imread('template.jpg',0)
template_image = cv2.medianBlur(template_image, 5)
width, height = template_image.shape[::-1]

res = cv2.matchTemplate(img_gray, template_image, cv2.TM_CCOEFF_NORMED)

threshold =  0.6

locations = np.where(res >= threshold)

new_locations = group_locations(np.array(locations).T, 50).T

for position_tuple in zip(*new_locations.astype(int)[::-1]):
        cv2.rectangle(img_rgb, position_tuple, (position_tuple[0] + width, position_tuple[1] + height), (0,255,0), 5)

Original locations: 723
New locations: 6 (yep, template selection not the best)

result

Upvotes: 1

Related Questions