Search code examples
python-3.xopencvimage-processingcontourtriangle

How to find the direction of triangles in an image using OpenCV


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.


Solution

  • 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