1

I'm attempting to wrap my head around the basics of CV. I have a piece of code in which I have detected multiple objects from the single template using the for loop.

inputImage: Trolley_problem.jpg, humanTemplate: h1.jpg

Here's the code:

import numpy as np
import cv2


# Read the main image

inputImage = cv2.imread('Trolley_problem.jpg')

# Convert it to grayscale

inputImageGray = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)

# Read the templates

humanTemplate = cv2.imread('h1.jpg')

# Convert it to grayscale

humanTemplateGray = cv2.cvtColor(humanTemplate, cv2.COLOR_BGR2GRAY)

h2,w2 = humanTemplateGray.shape

# Perform match operations.

result1 = cv2.matchTemplate(inputImageGray,humanTemplateGray, cv2.TM_CCOEFF_NORMED)

# Specify a threshold

threshold = 0.75

# Store the coordinates of matched area in a numpy array

loc2 = np.where( result1 >= threshold)

# Draw a rectangle around the matched region.

for pt2 in zip(*loc2[::-1]):
    cv2.rectangle(inputImage,pt2, (pt2[0] + w2, pt2[1] + h2), (0,0,255), 1)

# Show result

cv2.imwrite(r'Result Image.jpg', inputImage)

Now, this code works fine, and it detects 6 objects. The problem is to get the coordinates of the location. Since cv2.rectangle is in the for loop, I get multiple (more than 6) values for just 6 objects.

When I print pt2, I get:

((610, 29), (656, 69))
((609, 30), (655, 70))
((610, 30), (656, 70))
((611, 30), (657, 70))
((608, 31), (654, 71))
((609, 31), (655, 71))
((610, 31), (656, 71))
((611, 31), (657, 71))
((612, 31), (658, 71))
((607, 32), (653, 72))
((608, 32), (654, 72))
((609, 32), (655, 72))
((610, 32), (656, 72))
((611, 32), (657, 72))
((612, 32), (658, 72))
((613, 32), (659, 72))
((607, 33), (653, 73))
((608, 33), (654, 73))
((609, 33), (655, 73))
((610, 33), (656, 73))
((611, 33), (657, 73))
((612, 33), (658, 73))
((613, 33), (659, 73))
((607, 34), (653, 74))
((608, 34), (654, 74))
((609, 34), (655, 74))
((610, 34), (656, 74))
((611, 34), (657, 74))
((612, 34), (658, 74))
((613, 34), (659, 74))
((608, 35), (654, 75))
((609, 35), (655, 75))
((610, 35), (656, 75))
((611, 35), (657, 75))
((612, 35), (658, 75))
((609, 36), (655, 76))
((610, 36), (656, 76))
((611, 36), (657, 76))
((610, 37), (656, 77))
((1117, 106), (1163, 146))
((1118, 106), (1164, 146))
((1119, 106), (1165, 146))
((1120, 106), (1166, 146))
((1121, 106), (1167, 146))
((1116, 107), (1162, 147))
((1117, 107), (1163, 147))
((1118, 107), (1164, 147))
((1119, 107), (1165, 147))
((1120, 107), (1166, 147))
((1121, 107), (1167, 147))
((1122, 107), (1168, 147))
((965, 108), (1011, 148))
((966, 108), (1012, 148))
((967, 108), (1013, 148))
((968, 108), (1014, 148))
((969, 108), (1015, 148))
((1042, 108), (1088, 148))
((1043, 108), (1089, 148))
((1044, 108), (1090, 148))
((1079, 108), (1125, 148))
((1080, 108), (1126, 148))
((1081, 108), (1127, 148))
((1082, 108), (1128, 148))
((1083, 108), (1129, 148))
((1116, 108), (1162, 148))
((1117, 108), (1163, 148))
((1118, 108), (1164, 148))
((1119, 108), (1165, 148))
((1120, 108), (1166, 148))
((1121, 108), (1167, 148))
((1122, 108), (1168, 148))
((964, 109), (1010, 149))
((965, 109), (1011, 149))
((966, 109), (1012, 149))
((967, 109), (1013, 149))
((968, 109), (1014, 149))
((969, 109), (1015, 149))
((970, 109), (1016, 149))
((1004, 109), (1050, 149))
((1005, 109), (1051, 149))
((1006, 109), (1052, 149))
((1007, 109), (1053, 149))
((1041, 109), (1087, 149))
((1042, 109), (1088, 149))
((1043, 109), (1089, 149))
((1044, 109), (1090, 149))
((1045, 109), (1091, 149))
((1118, 109), (1164, 149))
((1119, 109), (1165, 149))
((1120, 109), (1166, 149))
((965, 110), (1011, 150))
((966, 110), (1012, 150))
((1004, 110), (1050, 150))
((1005, 110), (1051, 150))
((1006, 110), (1052, 150))
((1007, 110), (1053, 150))

Is there a way to eliminate this many location coordinates and get only 6 points?

I tried numpy.delete:

a = (pt2[0] == pt2[:,1]) | (pt2[0,:] == pt2[1,:])
vals = numpy.delete(pt2, numpy.where(a), axis=0)

But it shows an error:

tuple indices must be integers, not tuple

because the np.shape(pt2) is (2,) [This is what I get when I print np.shape(pt2)].

Can somebody please help me?

Note: If anybody has the better title, please help me to edit it.

Thanks!

EDIT 1:

The result of the code: Result Image.jpg

Rao208
  • 147
  • 1
  • 3
  • 15
  • How does your image look after drawing the rectangles? – Rick M. Apr 18 '18 at 06:28
  • @RickM. I have edited my question, please have a look at the result of my code. – Rao208 Apr 18 '18 at 06:32
  • 1
    It looks like your algorithm is drawing more than just 6 rectangles. Try thresholding by taking the minMaxLoc value (min or max depending on your match method), something like [this](http://answers.opencv.org/question/165740/template-matching-multiple-objects/). If this does't work, you could run a function which selects one point from the several (x1+5, y1+5),(x2+5, y2+5) – Rick M. Apr 18 '18 at 06:38
  • @RickM. I did have a look at the code that you mentioned in the comments above, but the problem is (as far as my knowledge goes) the cv2.minMaxLoc() simply gives the absolute minimum and maximum values, so I cannot grab **multiple** values with this function. If I decrease the threshold value below 0.75 the code doesn't detect all the objects (i.e 6) and if I increase the threshold above 0.75, it gives me false positives. – Rao208 Apr 18 '18 at 06:58
  • @RickM. I have more than 100 values for 6 objects. The minthreshold sets the threshold value, and though I agree that the threshold value decides the number of the output of loc2, but it doesn't decrease from 100 values to 6 values. (I have tried it, and this comment is based on the result of my code) – Rao208 Apr 18 '18 at 07:14
  • Either you fix your template to only include the black part or you select one of the several points depending on common y2/y1 value – Rick M. Apr 18 '18 at 07:19
  • @RickM.How do I select one of the several points like ((607, 32), (653, 72)) ((1117, 106), (1163, 146)) ((966, 108), (1012, 148))? I tried: unique, index = np.unique(pt2, return_index=True), but the output of this is (array([ 110, 1007]). Do you know some way to do? – Rao208 Apr 18 '18 at 07:23
  • As I mentioned previously, either you can compare values of (x1, y1), (x2,y2) in pt2 and select one from all the points where they are equal. However, the better way would still be to change your template OR try descriptor matching – Rick M. Apr 18 '18 at 09:19
  • 2
    Something like [non-maximum suppression](https://www.pyimagesearch.com/2014/11/17/non-maximum-suppression-object-detection-python/). – Catree Apr 18 '18 at 09:59
  • @Riya208 FYI, look at [this](https://stackoverflow.com/questions/48330389/filter-numpy-array-to-retain-only-one-row-for-a-given-value) – Rick M. Apr 19 '18 at 07:52
  • Thank you very much @RickM.! It solved my problem :) – Rao208 Apr 30 '18 at 16:22
  • @Riya208 how did you solve it? With the answer in the link? – Rick M. May 02 '18 at 08:04
  • @RickM. Yes, first I tried with the answer in the link which worked almost fine (but still not the desired output). Then, I created my own function using the inbuilt functions(like np.flatnonzero and .argsort()) mentioned in the link and I got the desired output. The link that you mentioned was very helpful! – Rao208 May 02 '18 at 16:35
  • @Riya208 Great, write it as an answer then! – Rick M. May 03 '18 at 07:13

0 Answers0