opencv 12 Calcular el ángulo del punto inicial y el ángulo del punto final de los gráficos de arco

1. Requisitos de la tarea

Calcule el ángulo entre la forma del arco y el centro del círculo en la imagen binaria, encuentre el ángulo del punto inicial y final del arco y visualícelo. Suponiendo que se conoce el centro del círculo (obtenido mediante la detección del círculo de Hough en la tarea anterior, consulte https://blog.csdn.net/m0_74259636/article/details/132655935?spm=1001.2014.3001.5501)
la imagen original es el siguiente: Gráfico binario
Insertar descripción de la imagen aquí

2. Análisis de problemas

Es difícil calcular el ángulo del punto inicial y final de un gráfico de arco en una imagen binaria (opencv no proporciona directamente la interfaz API relevante). Debe encontrar todos los píxeles del gráfico de arco usted mismo y calcular el ángulo entre ellos. y el centro del círculo. Luego encuentra los valores máximo y mínimo del ángulo.

3. Pasos básicos de implementación

1. Lea la imagen como una imagen en escala de grises y realice la binarización [imread(“filename”,0),0: modo de imagen en escala de grises]
2. Optimice el dominio conectado de la imagen [el ojo desnudo puede verlo como un dominio conectado, en De hecho, pueden ser múltiples dominios conectados]
3. Optimización del conjunto de puntos, ajustando el dominio conectado del gráfico de arco y calculando solo el ángulo del punto de inflexión [hay demasiados puntos en el gráfico de arco, lo que puede reducir la mayor parte del cálculo] 4.
Cálculo Encuentre el ángulo entre todos los puntos de inflexión y el centro del círculo, encuentre los valores máximo y mínimo de los ángulos

4. Conocimientos clave

4.1 Convertir gráficos de arco en conjuntos de puntos

Buscar perfil de arco

Para encontrar contornos, necesitamos usar la función findContours, pero descubrimos que además del área objetivo, los dominios conectados encontrados también tienen algunas áreas de interferencia pequeñas. Podemos usar el filtrado de cuadros (cv2.blur) para reducir el ruido antes de encontrar contornos. . Si la cantidad de dominios conectados después de la eliminación de ruido aún no es la ideal, debe ajustar los parámetros de la función findContours. Detalles de la lista de parámetros:
Insertar descripción de la imagen aquí

Encuentre la línea de ajuste del polígono (conjunto de puntos) del contorno

Hay demasiados puntos en el contorno que se encuentran en los pasos anteriores. Es necesario ajustarlos y solo se conservan los puntos de inflexión en el contorno. Esto puede reducir los puntos en el contorno y reducir el tiempo de ejecución. Para encontrar la línea de ajuste del polígono del contorno, use la función approxPolyDP.
La función approxPolyDP() es una función en opencv que realiza una aproximación poligonal en un conjunto de puntos específico. La precisión de su aproximación se puede establecer mediante parámetros.
La función correspondiente es:
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,则断开。
Dibuja la línea de contorno ajustada.

Utilice la función drawContours para visualizar la línea de contorno ajustada y comprobar si el ajuste de la línea de contorno es preciso.
La función de la función cv2.drawContours() es dibujar contornos, las variables de entrada son las siguientes:

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

Los resultados del dibujo son los siguientes: La línea gris es la línea de contorno ajustada y puede ver que hay muchos menos puntos.
Insertar descripción de la imagen aquí

4.2 Calcular el ángulo coordenado

parte teorica

Con base en las coordenadas de un punto en el círculo y las coordenadas del centro del círculo, la fórmula de cálculo para encontrar el ángulo entre el punto y el centro del círculo es como se muestra en la siguiente figura: Establezca un sistema de coordenadas basado en el centro del círculo y tome la dirección positiva del eje x como punto de partida.
Insertar descripción de la imagen aquí

parte del código

Consultehttps://blog.csdn.net/qq_43928549/article/details/130133144
. El código específico es el siguiente: math.atan2() el método se utiliza para convertir coordenadas rectangulares (x, y) en coordenadas polares (r, theta) y devolver el ángulo theta resultante. Este método calcula el ángulo de fase theta calculando el arcotangente de y/x, que va desde -pi a 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)
Aplicación práctica

Dadas las coordenadas de múltiples puntos (es decir, los puntos de la línea de ajuste del polígono) y las coordenadas del centro del círculo, el gráfico dibujado es el siguiente.
Insertar descripción de la imagen aquí
El código es el siguiente: el punto inicial de 0 grados calculado por la función get_angle es la dirección positiva del eje x. Aquí, se gira 90 grados en el sentido de las agujas del reloj y la dirección de las 6 en punto se utiliza como punto inicial de 0 grados. punto.

        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. Implementación específica

5.1 Implementación básica

Según el conocimiento clave 4.1, se pueden obtener todos los puntos de inflexión del arco y, según el conocimiento clave 4.2, se puede calcular el ángulo entre el punto del círculo y el centro del círculo. Para obtener el punto inicial y el punto final del arco, debe atravesar todos los puntos de inflexión, calcular el ángulo entre ellos y el centro del círculo y encontrar el ángulo máximo (el punto final del arco) y el ángulo mínimo. (el punto de partida del arco).
其中需要注意,当圆弧横跨0度起点时,所得到的最大角度与最小角度是错误的,因为横跨0度起点时,最小角度通常为0~10度,最大角度通常为30~359.9度。
Insertar descripción de la imagen aquí

5.2 Código de implementación

Para la situación descrita en 5.1, definimos temporalmente el punto inicial de 0 grados como la dirección positiva del eje X. En este momento, los ángulos máximo y mínimo calculados no tienen nada que ver con el punto inicial de 0 grados, por lo que el Se pueden obtener ángulos máximos y mínimos correctos. Luego gírelo 90° para obtener el valor de ángulo correcto.
El código completo es el siguiente:

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 Efecto de la operación

Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/m0_74259636/article/details/132927460
Recomendado
Clasificación