opencv 12 Calculate the starting point angle and end point angle of arc graphics

1. Task requirements

Calculate the angle between the arc shape and the center of the circle in the binary image, find the starting point and end point angle of the arc, and visualize it. Assuming that the center of the circle is known (obtained through Hough circle detection in the previous task, please refer to https://blog.csdn.net/m0_74259636/article/details/132655935?spm=1001.2014.3001.5501)
the original picture is as follows: Binary graph
Insert image description here

2. Problem analysis

It is difficult to calculate the starting point and end point angle of an arc graphic in a binary image (opencv does not directly provide the relevant API interface). You need to find all the pixels of the arc graphic yourself and calculate the angle between it and the center of the circle. Then find the maximum and minimum values ​​of the angle.

3. Basic implementation steps

1. Read the image as a grayscale image and perform binarization [imread(“filename”,0),0: grayscale image mode]
2. Optimize the connected domain of the image [the naked eye may see it as a connected domain, In fact, it may be multiple connected domains]
3. Point set optimization, fitting the connected domain of the arc graphic, and only calculating the angle of the inflection point [there are too many points on the arc graphic, which can reduce most of the calculation] 4.
Calculation Find the angle between all inflection points and the center of the circle, find the maximum and minimum values ​​of the angles

4. Key knowledge

4.1 Convert arc graphics into point sets

Find Arc Profile

To find contours, we need to use the findContours function, but we found that in addition to the target area, the found connected domains also have some small interference areas. We can use box filtering (cv2.blur) to reduce noise before finding contours. If the number of connected domains after denoising is still not ideal, you need to adjust the parameters of the findContours function. Parameter list details:
Insert image description here

Find the polygon fitting line (point set) of the contour

There are too many points on the contour found in the above steps. They need to be fitted and only the inflection points on the contour are retained. This can reduce the points on the contour and reduce the running time. To find the polygon fitting line of the contour, use the approxPolyDP function.
The approxPolyDP() function is a function in opencv that performs polygonal approximation on a specified point set. The accuracy of its approximation can be set through parameters.
The corresponding function is:
void approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed);

例如:approxPolyDP(contourMat, approxCurve, 10, true);//找出轮廓的多边形拟合曲线
第一个参数 InputArray curve:输入的点集
第二个参数OutputArray approxCurve:输出的点集,当前点集是能最小包容指定点集的。画出来即是一个多边形。
第三个参数double epsilon:指定的精度,也即是原始曲线与近似曲线之间的最大距离。
第四个参数bool closed:若为true,则说明近似曲线是闭合的;反之,若为false,则断开。
Draw the fitted contour line

Use the drawContours function to visualize the fitted contour line and check whether the fitting of the contour line is accurate.
The function of the cv2.drawContours() function is to draw contours. The input variables are as follows:

cv2.drawContours(image, contours, contourIdx, color, thickness=None, lineType=None, hierarchy=None, maxLevel=None, offset=None)

第一个参数image表示目标图像,
第二个参数contours表示输入的轮廓组,每一组轮廓由点vector构成,
第三个参数contourIdx指明画第几个轮廓,如果该参数为负值,则画全部轮廓,
第四个参数color为轮廓的颜色,
第五个参数thickness为轮廓的线宽,如果为负值或CV_FILLED表示填充轮廓内部,
第六个参数lineType为线型,
第七个参数为轮廓结构信息,
第八个参数为maxLevel

The drawing results are as follows: The gray line is the fitted contour line, and you can see that there are many fewer points.
Insert image description here

4.2 Calculate coordinate angle

Theoretical part

Based on the coordinates of a point on the circle and the coordinates of the center of the circle, the calculation formula for finding the angle between the point and the center of the circle is as shown in the figure below: Establish a coordinate system based on the center of the circle, and take the positive direction of the x-axis as the starting point.
Insert image description here

code part

Refer to https://blog.csdn.net/qq_43928549/article/details/130133144
. The specific code is as follows. math.atan2() The method is used to convert rectangular coordinates (x, y) into polar coordinates (r, theta) and return the resulting angle theta. This method calculates the phase angle theta by calculating the arctangent of y/x, ranging from -pi to pi

def get_angle(cx, cy, x, y):
    '''
    :param cx: 圆心x
    :param cy: 圆心y
    :param x: 点x坐标
    :param y: 点y坐标
    :return: 
    '''
    dx = x - cx
    dy = y - cy
    print((dx,dy))
    # 计算两点之间的切线值,其返回值为弧度
    rads = math.atan2(dy, dx)
    # 将弧度值转换为0-2π的范围
    rads %= 2*math.pi
    # 将弧度转换为角度后
    degs = math.degrees(rads)
    return round(degs,2)
Practical application

Given the coordinates of multiple points (that is, the points of the polygon fitting line) and the coordinates of the center of the circle, the drawn graph is as follows.
Insert image description here
The code is as follows. The 0-degree starting point calculated by the get_angle function is the positive direction of the x-axis. Here, it is rotated 90 degrees clockwise and the 6 o'clock direction is used as the 0-degree starting point.

        for p in approx:
            x,y=p[0]
            angle360=get_angle(cx,cy,x,y)
            angle360-=90 #将起点方向由x轴正方向旋转到6点钟方向
            if angle360<0:
                angle360=angle360+360
            #每隔10个点,画一条线
            if p_i%28==0:
                cv2.line(circle_img,(cx,cy),(x,y),(255,0,0))
                cv2.putText(circle_img,'%.2f'%angle360,(x,y),1,1,(255,128,0))

5. Specific implementation

5.1 Basic implementation

According to key knowledge 4.1, all inflection points on the arc can be obtained, and according to key knowledge 4.2, the angle between the point on the circle and the center of the circle can be calculated. To get the starting point and end point of the arc, you need to traverse all the inflection points, calculate the angle between them and the center of the circle, and find the maximum angle (the end point of the arc) and the minimum angle (the starting point of the arc).
其中需要注意,当圆弧横跨0度起点时,所得到的最大角度与最小角度是错误的,因为横跨0度起点时,最小角度通常为0~10度,最大角度通常为30~359.9度。
Insert image description here

5.2 Implementation code

For the situation described in 5.1, we temporarily define the starting point of 0 degrees as the positive direction of the x-axis. At this time, the maximum and minimum angles calculated have nothing to do with the starting point of 0 degrees, so the correct maximum and minimum angles can be obtained. Then rotate it 90° to get the correct angle value.
The complete code is as follows:

import cv2,os
import numpy as np
import random,math
def get_angle(cx, cy, x, y):
    '''
    :param cx: 圆心x
    :param cy: 圆心y
    :param x: 点x坐标
    :param y: 点y坐标
    :return: 
    '''
    dx = x - cx
    dy = y - cy
    print((dx,dy))
    # 计算两点之间的切线值,其返回值为弧度
    rads = math.atan2(dy, dx)
    # 将弧度值转换为0-2π的范围
    rads %= 2*math.pi
    # 将弧度转换为角度后
    degs = math.degrees(rads)
    return round(degs,2)


def get_coil_head(img,circle_img,cx,cy):
    if True:
        #减少联通域的个数(肉眼看到是一个连通域,实际上是多个)
        img = cv2.blur(img, (3, 3)) #方框滤波,去除噪声
        #找连通域的轮廓(联通域个数不理想时,要调节参数)  参数见上图 https://img-blog.csdnimg.cn/20210825231233389.png
        contours, hierarchy = cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

        #联通域上的点太多,对其进行拟合,只保留轮廓的拐点
        approx = cv2.approxPolyDP(contours[0], 0.5, True)
        h,w=img.shape                        
        #draw_img = np.ones((h,w,3),np.uint8)*255
        circle_img = cv2.cvtColor(circle_img,cv2.COLOR_GRAY2BGR)
        circle_img = cv2.drawContours(circle_img, [approx], -1, (0,0,255), 2)

        yd=np.pi/180
        p_i=0
        max_angle=-1
        max_ax,max_ay=0,0
        min_angle=361
        min_ax,min_ay=0,0


        #所有的点,都不经过原点时生效
        for p in approx:
            x,y=p[0]
            angle360=get_angle(cx,cy,x,y)
            angle360-=90 #将起点方向由x轴正方向旋转到6点钟方向
            if angle360<0:
                angle360=angle360+360
            
            if angle360>max_angle:
                max_angle=angle360
                max_ax,max_ay=x,y
            
            if angle360<min_angle:
                min_angle=angle360
                min_ax,min_ay=x,y

            #每隔10个点,画一条线
            # if p_i%28==0:
            #     cv2.line(circle_img,(cx,cy),(x,y),(255,0,0))
            #     cv2.putText(circle_img,'%.2f'%angle360,(x,y),1,1,(255,128,0))
            p_i+=1

            #按照随机概率划线
            # rad= random.random() #0~1之间的随机数
            # if rad<0.05:
            #     cv2.line(ret,(cx,cy),(x,y),(255,0,0))
        
        #当弧线上的点经过0度时,上述方法找出来的最大值与最小值不通用了
        if max_angle+min_angle>340 and max_angle+min_angle<380 and min_angle<30 and max_angle>340 :
            max_angle=-1
            max_ax,max_ay=0,0
            min_angle=361
            min_ax,min_ay=0,0
             
            for p in approx:
                x,y=p[0]
                angle360=get_angle(cx,cy,x,y)
                #print(x,y,angle,angle360)
                angle360-=90
                if angle360>max_angle:
                    max_angle=angle360
                    max_ax,max_ay=x,y
                
                if angle360<min_angle:
                    min_angle=angle360
                    min_ax,min_ay=x,y
            #print("这张图找出来的结果不对,已经进行矫正")



        cv2.line(circle_img,(cx,cy),(min_ax,min_ay),(255,0,0))
        cv2.putText(circle_img,'%.2f'%min_angle,(min_ax,min_ay),1,1,(128,128,0))


        cv2.line(circle_img,(cx,cy),(max_ax,max_ay),(255,0,0))
        cv2.putText(circle_img,'%.2f'%max_angle,(max_ax,max_ay),1,1,(128,128,0))


        cv2.circle(circle_img,(cx,cy),3,(255,0,255),-1)
        return circle_img

img=cv2.imread('txqy/a6.jpg',0)
#图像resize,减少轮廓的面积
img = cv2.resize(img,None,fx=0.5,fy=0.5)
h,w=img.shape
cx,cy=w//2,h//2 #假定的圆心(在实际代码中采用霍夫圆的圆心)

ret=get_coil_head(img,img,cx,cy)
cv2.imshow('ret',ret)
cv2.waitKey()

5.3 Operation effect

Insert image description here
Insert image description here
Insert image description here

Guess you like

Origin blog.csdn.net/m0_74259636/article/details/132927460