Search code examples
pythonopencvcameracamera-calibrationpose-estimation

How to calculate Angle of a square object with respect to image in 2D plane using camera?


I have captured an image using a webcam, attached upside-down above a table horizontally. On the table, I have a square object or piece of card. I have successfully detected the object and found its center coordinates (centroid). Now I want to find the rotation angle of the object with respect to the image. Considering everything in the 2D image plane. How can I calculate the angle? This image represents what I trying to achieve:

enter image description here


Solution

  • I got the Solution. I write the code to perform the required operation as I said in my Question above. I am using OpenCV 4 + Python 3.8.3 + Spyder IDE

    This is my working code:

    # This code is used to Find the Origin and Rotation Angle of a Rectangle
    
    #First of all place a blue colored rectangle card on the table below the camera
    # Then execute the code. The code will detect the Rectangle in Blue color then find the origin and rotation values. 
    
    #[Resources]
    # https://stackoverflow.com/questions/34237253/detect-centre-and-angle-of-rectangles-in-an-image-using-opencv
    # https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_contours/py_contours_begin/py_contours_begin.html#how-to-draw-the-contours
    # https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_contours/py_contour_features/py_contour_features.html#b-rotated-rectangle
    # https://stackoverflow.com/questions/52247821/find-width-and-height-of-rotatedrect
    
    import numpy as np
    import cv2
    import sys
    import yaml
    import os
    import warnings
    warnings.filterwarnings("ignore")
    
    #Global Variables
    cx = 0.0    #x locaton of Rectangle
    cy = 0.0    #y location of Rectangle
    angle = 0.0 #Angle of rotation of Rectangle
    
    if __name__ == "__main__":
        while(1):
            try:
                cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
       
                while(1):
                    _,frame = cap.read()
                    k = cv2.waitKey(5)
                    if k == 27: #exit by pressing Esc key
                        cv2.destroyAllWindows()
                        sys.exit()
                    if k == 13: #Save the centroid and angle values of the rectangle in a file
                        result_file = r'rectangle_position.yaml'    
                        try:
                            os.remove(result_file)  #Delete old file first
                        except:
                            pass
                        print("Saving Rectangle Position Matrix in: ",result_file)
                        data={"rect_position": [cx,cy,angle]}
                        with open(result_file, "w") as f:
                            yaml.dump(data, f, default_flow_style=False)
                    
                    #Detecting Blue Color
                    red = np.matrix(frame[:,:,2])  #extracting red layer (layer No 2) from RGB
                    green = np.matrix(frame[:,:,1]) #extracting green layer (layer No 1) from RGB
                    blue = np.matrix(frame[:,:,0])  #extracting blue layer (layer No 0) from RGB
                    #it will display only the Blue colored objects bright with black background
                    blue_only = np.int16(blue)-np.int16(red)-np.int16(green)
                    blue_only[blue_only<0] =0
                    blue_only[blue_only>255] =255
                    blue_only = np.uint8(blue_only)            
                    # cv2.namedWindow('blue_only', cv2.WINDOW_AUTOSIZE)
                    # cv2.imshow("blue_only",blue_only)
                    # cv2.waitKey(1)
                    
                    #https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_thresholding/py_thresholding.html#otsus-binarization
                    #Gaussian filtering
                    blur = cv2.GaussianBlur(blue_only,(5,5),cv2.BORDER_DEFAULT)
                    #Otsu's thresholding
                    ret3,thresh = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
                    cv2.namedWindow('Threshold', cv2.WINDOW_AUTOSIZE)
                    cv2.imshow("Threshold",thresh)
                    cv2.waitKey(1)
                    #Finding Conture of detected Rectangle
                    contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
        
        
                    for contour in contours:
                        area = cv2.contourArea(contour)
                        if area>100000:
                            contours.remove(contour)
                     
                    cnt = contours[0] #Conture of our rectangle
                    
                    #https://stackoverflow.com/a/34285205/3661547
                    #fit bounding rectangle around contour            
                    rotatedRect = cv2.minAreaRect(cnt)
                    #getting centroid, width, height and angle of the rectangle conture
                    (cx, cy), (width, height), angle = rotatedRect
                    
                    #centetoid of the rectangle conture
                    cx=int(cx)
                    cy=int(cy)
                    print (cx,cy) #centroid of conture of rectangle
                                   
                    # we want to choose the Shorter edge of the rotated rect to compute the angle between Vertical
                    #https://stackoverflow.com/a/21427814/3661547
                    if(width > height):
                        angle = angle+180
                    else:
                        angle = angle+90
                    print("Angle b/w shorter side with Image Vertical: \n", angle)
                    
                    
                    #Draw rectangle around the detected object
                    #https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_contours/py_contours_begin/py_contours_begin.html#how-to-draw-the-contours
                    im = cv2.drawContours(frame,[cnt],0,(0,0,255),2)                
                    cv2.circle(im, (cx,cy), 2,(200, 255, 0),2) #draw center
                    cv2.putText(im, str("Angle: "+str(int(angle))), (int(cx)-40, int(cy)+60), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,255), 1, cv2.LINE_AA)
                    cv2.putText(im, str("Center: "+str(cx)+","+str(cy)), (int(cx)-40, int(cy)-50), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,255), 1, cv2.LINE_AA)
                    cv2.namedWindow('Detected Rect', cv2.WINDOW_AUTOSIZE)
                    cv2.imshow('Detected Rect',im)
                    cv2.waitKey(1)
    
            except Exception as e:
                print("Error in Main Loop\n",e)
                cv2.destroyAllWindows()
                sys.exit()
        
        cv2.destroyAllWindows()
    

    The code is working well and calculation the origin of the Rectangle as well as its rotation angle with respect to Image Vertical.

    The Result: detected rectangle with its origin and angle threshold the blue colored rectangle

    I got help from these links: