2

I am trying to find the direction of triangles in an image. below is the image:

enter image description here

These triangles are pointing upward/downward/leftward/rightward. This is not the actual image. I have already used canny edge detection to find edges then contours and then the dilated image is shown below.

My logic to find the direction:

The logic I am thinking to use is that among the three corner coordinates If I can identify the base coordinates of the triangle (having the same abscissa or ordinates values coordinates), I can make a base vector. Then angle between unit vectors and base vectors can be used to identify the direction. But this method can only determine if it is up/down or left/right but cannot differentiate between up and down or right and left. I tried to find the corners using cv2.goodFeaturesToTrack but as I know it's giving only the 3 most effective points in the entire image. So I am wondering if there is other way to find the direction of triangles.

Here is my code in python to differentiate between the triangle/square and circle:

#blue_masking
mask_blue=np.copy(img1)
row,columns=mask_blue.shape
for i in range(0,row):
    for j in range(0,columns):
        if (mask_blue[i][j]==25):
            mask_blue[i][j]=255
        else: 
            mask_blue[i][j]=0
blue_edges = cv2.Canny(mask_blue,10,10)
kernel_blue = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(2,2))
dilated_blue = cv2.dilate(blue_edges, kernel)
blue_contours,hierarchy = 
cv2.findContours(dilated_blue,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
for cnt in blue_contours:
    area = cv2.contourArea(cnt)
    perimeter = cv2.arcLength(cnt,True)
    M = cv2.moments(cnt)
    cx = int(M['m10']/M['m00'])
    cy = int(M['m01']/M['m00'])
    if(12<(perimeter*perimeter)/area<14.8):
        shape="circle"
    elif(14.8<(perimeter*perimeter)/area<18):
        shape="squarer"
    elif(18<(perimeter*perimeter)/area and area>200):
        shape="triangle"
    print(shape) 
    print(area)
    print((perimeter*perimeter)/area,"\n")
    
cv2.imshow('mask_blue',dilated_blue)
cv2.waitKey(0)
cv2.destroyAllWindows()

  

Source image can be found here: img1

Please help, how can I found the direction of triangles?
Thank you.

UJM
  • 167
  • 8
  • 1
    If your triangles are always the same size, you could use *"template matching"*. – Mark Setchell Apr 05 '21 at 13:22
  • 1
    Just find the most vertical or horizontal line of the triangle. If it's vertical and on the left, the triangle is pointing right. If it's vertical and on the right, it's pointing left. If it's horizontal and on the bottom, it's pointing up. If it's horizontal and on the top, it's pointing down. – Abstract Apr 05 '21 at 15:31
  • @Mark Setchell, I didn't understand how to use "template matching"? – UJM Apr 05 '21 at 16:28
  • @Jon, the word "most" means length of the line here? Could you please elaborate more? – UJM Apr 05 '21 at 16:30
  • 1
    There's an example here https://docs.opencv.org/3.4/de/da9/tutorial_template_matching.html Basically you slide a copy of the shape/object you are looking for across the image and it tells you how well it matches and where. You would slide 4 triangles, one at a time (one for each orientation) across the image and find where the triangle(s) with that orientation exist(s) in your image. – Mark Setchell Apr 05 '21 at 16:40
  • 1
    Just to be clear, I wasn't suggesting you run template matching on the edge image, you can run it directly on your source image after making all the triangles black and everything else white. Also, you can just run it on areas where you have found contours if you want to be more efficient. – Mark Setchell Apr 05 '21 at 18:44
  • @UjjawalM. Did my answer solve your problem? If so please accept my answer. – Baraa Apr 06 '21 at 18:43

2 Answers2

3

Assuming that you only have four cases: [up, down, left, right], this code should work well for you.

The idea is simple:

  1. Get the bounding rectangle for your contour. Use: box = cv2.boundingRect(contour_pnts)
  2. Crop the image using the bounding rectangle.
  3. Reduce the image vertically and horizontally using the Sum option. Now you have the sum of pixels along each axis. The axis with the largest sum determines whether the triangle base is vertical or horizontal.
  4. To identify whether the triangle is pointing left/right or up/down: you need to check whether the bounding rectangle center is before or after the max col/row:

The code (assumes you start from the cropped image):

ver_reduce = cv2.reduce(img,  0, cv2.REDUCE_SUM, None, cv2.CV_32F)
hor_reduce = cv2.reduce(img,  1, cv2.REDUCE_SUM, None, cv2.CV_32F)

#For smoothing the reduced vector, could be removed
ver_reduce = cv2.GaussianBlur(ver_reduce, (3, 1), 0)
hor_reduce = cv2.GaussianBlur(hor_reduce, (1, 3), 0)

_,ver_max, _, ver_col = cv2.minMaxLoc(ver_reduce)
_,hor_max, _, hor_row = cv2.minMaxLoc(hor_reduce)

ver_col = ver_col[0]
hor_row = hor_row[1]

contour_pnts = cv2.findNonZero(img) #in my code I do not have the original contour points

rect_center, size,  angle = cv2.minAreaRect(contour_pnts )

print(rect_center)

if ver_max > hor_max:
    if rect_center[0] > ver_col:
        print ('right')
    else:
        print ('left')
else:
    if rect_center[1] > hor_row:
        print ('down')
    else:
        print ('up')

Photos:

Up case

Right case

Down case

Left case

Baraa
  • 1,476
  • 1
  • 16
  • 19
2

Well, Mark has mentioned a solution that may not be as efficient but perhaps more accurate. I think this one should be equally efficient but perhaps less accurate. But since you already have a code that finds triangles, try adding the following code after you have found triangle contour:

hull = cv2.convexHull(cnt)    # convex hull of contour
hull = cv2.approxPolyDP(hull,0.1*cv2.arcLength(hull,True),True)
# You can double check if the contour is a triangle here
# by something like len(hull) == 3

You should get 3 hull points for a triangle, these should be the 3 vertices of your triangles. Given your triangles always 'face' only in 4 directions; Y coordinate of the hull will have close value to the Y coordinate of the centroid for triangle facing left or right and whether it's pointing left or right will depend on whether hull X is less than or greater than centroid X. Similarly use hull and centroid X and Y for triangle pointing up or down.

Knight Forked
  • 1,529
  • 12
  • 14