Conceptos básicos de Opencv 51-Contorno de imagen 04-Contorno Ajuste gráfico múltiple Explicación y ejemplos

Ajuste de contorno se refiere a la descripción aproximada del contorno en la imagen a través de algunos modelos matemáticos o métodos de ajuste de curvas en el procesamiento de imágenes. El contorno de la imagen se refiere al límite del objeto, y el ajuste de contorno se puede utilizar para extraer, describir y analizar la forma del objeto.

Los métodos de ajuste de contorno comúnmente utilizados incluyen ajuste polinomial, ajuste de curva Bézier, ajuste de curva B-spline, etc. Estos métodos se pueden utilizar para encontrar un modelo matemático que se ajuste al contorno real, describiendo así mejor la forma del objeto. La curva ajustada se puede utilizar para análisis, mediciones, identificación y otras tareas posteriores.

El ajuste de contornos es útil en muchas aplicaciones de procesamiento de imágenes, como:

  1. Análisis de forma : al ajustar el contorno, se pueden extraer las características de forma del objeto, como círculos, elipses, etc.

  2. Detección de objetos : ajustando el contorno del objeto, se puede detectar la presencia del objeto y determinar su ubicación.

  3. Seguimiento de objetivos : ajustar el contorno del objeto a los marcos de imagen continuos puede realizar el seguimiento del objetivo.

  4. Inspección industrial : en la industria manufacturera, la forma y el tamaño del producto se detectan ajustando el contorno del producto.

  5. Análisis de imágenes médicas : en el campo médico, el tamaño y la forma de las lesiones se pueden medir ajustando los contornos.

  6. Diseño asistido por computadora : en campos como CAD, el ajuste de contorno se puede utilizar para generar curvas y formas complejas.

Para decirlo sin rodeos, el ajuste de contorno es usar curvas o líneas matemáticas para aproximar los bordes de los objetos en una imagen. Es como cuando haces un dibujo, usas una línea para delinear la forma del objeto. A través de esta línea aproximada, podemos analizar la forma y el tamaño del objeto de manera más conveniente, o encontrar un objeto específico en la imagen. Mire la imagen de ejemplo a continuación para comprender básicamente qué es el ajuste de contorno

Al calcular el contorno, es posible que no se necesite el contorno real, sino solo un polígono aproximado cerca del contorno.
OpenCV proporciona una variedad de métodos para calcular aproximaciones de contorno a polígonos.

cuadro delimitador rectangular

函数 cv2.boundingRect()Límites rectangulares capaces de dibujar contornos. La sintaxis de esta función es:

retval = cv2.boundingRect(matriz)

En la fórmula:

  • El valor devuelto retval indica el valor de la coordenada del vértice de la esquina superior izquierda del límite del rectángulo devuelto y el ancho y alto del límite del rectángulo.
  • La matriz de argumentos es una imagen o contorno en escala de grises.

La función también puede tener la forma con 4 valores de retorno:

x,y,w,h = cv2.boundingRect(matriz)

Los cuatro valores devueltos aquí representan:

  • La coordenada x del vértice superior izquierdo de los límites del rectángulo.
  • La coordenada y del vértice superior izquierdo de los límites del rectángulo.
  • La longitud en la dirección x de los límites del rectángulo.
  • La longitud en la dirección y del rectángulo delimitador.

Ejemplo de código: muestra los valores de retorno de la función cv2.boundingRect() en diferentes formas.

import cv2
#---------------读取并显示原始图像------------------
o = cv2.imread('cc.bmp')
#---------------提取图像轮廓------------------
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
#找到轮廓,返回的是轮廓的点集,以及它们的层次,contours是点集,hierarchy是层次,这里只有一个轮廓,所以只返回一个,用[0]取出来
contours, hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
#---------------返回顶点及边长------------------
x,y,w,h = cv2.boundingRect(contours[0])
print("顶点及长宽的点形式:")
print("x=",x)
print("y=",y)
print("w=",w)
print("h=",h)
#---------------仅有一个返回值的情况------------------
rect = cv2.boundingRect(contours[0])
print("\n 顶点及长宽的元组(tuple)形式:")
print("rect=",rect)

Ejecute el programa anterior, se muestran los siguientes resultados:

顶点及长宽的点形式:
x= 202
y= 107
w= 157
h= 73
顶点及长宽的元组(tuple)形式:
rect= (202, 107, 157, 73)

Ejemplo 2: utilice la función cv2.drawContours() para dibujar un cuadro delimitador rectangular.

el código se muestra a continuación:

import cv2
import numpy as np
#---------------读取并显示原始图像------------------
o = cv2.imread('cc.bmp')
cv2.imshow("original",o)

#---------------提取图像轮廓------------------
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
#---------------构造矩形边界------------------
x,y,w,h = cv2.boundingRect(contours[0])
brcnt = np.array([[[x, y]], [[x+w, y]], [[x+w, y+h]], [[x, y+h]]])
cv2.drawContours(o, [brcnt], -1, (255, 255,255), 1)
#---------------显示矩形边界------------------
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()

resultado de la operación:

inserte la descripción de la imagen aquí
Ejemplo 2 Implementación de otra función

Ejemplo 3: utilice la función cv2.boundingRect() y cv2.rectangle()dibuje un cuadro delimitador rectangular.

import cv2
#---------------读取并显示原始图像------------------
o = cv2.imread('cc.bmp')
cv2.imshow("original",o)
#---------------提取图像轮廓------------------
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,
 cv2.RETR_LIST,
 cv2.CHAIN_APPROX_SIMPLE)
#---------------构造矩形边界------------------
x,y,w,h = cv2.boundingRect(contours[0])
cv2.rectangle(o,(x,y),(x+w,y+h),(255,255,255),2)

#---------------显示矩形边界------------------
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()

inserte la descripción de la imagen aquí

Rectángulo envolvente mínimo

La función cv2.minAreaRect() puede dibujar el rectángulo mínimo que encierra el contorno, y su formato de sintaxis es:

retval =cv2.minAreaRect( puntos )

En la fórmula:

  • El valor devuelto retval indica la información de la entidad del rectángulo devuelta.
    La estructura del valor es (centro del rectángulo delimitador más pequeño (x, y), (ancho, alto), ángulo de rotación).
  • El argumento apunta es el contorno.

Cabe señalar que la estructura del valor de retorno retval no cumple con los requisitos de estructura de parámetros de la función cv2.drawContours(). Por lo tanto, debe convertirse en una estructura conforme antes de que pueda usarse . La función cv2.boxPoints() puede convertir el valor de retorno anterior en una estructura que cumpla con los requisitos. El formato de sintaxis de la función cv2.boxPoints() es:

puntos = cv2.boxPoints( box )

En la fórmula:

  • Los puntos de valor de retorno son puntos de contorno que se pueden utilizar como parámetros de la función cv2.drawContours().
  • El cuadro de parámetro es un valor del tipo devuelto por la función cv2.minAreaRect().

Ejemplo: utilice la función cv2.minAreaRect() para calcular el rectángulo delimitador mínimo de una imagen.

import cv2
import numpy as np
o = cv2.imread('cc.bmp')
cv2.imshow("original",o)
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)

ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,
 cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
rect = cv2.minAreaRect(contours[0])

print("返回值 rect:\n",rect)
points = cv2.boxPoints(rect)
print("\n 转换后的 points:\n",points)
points = np.int0(points) #取整
image=cv2.drawContours(o,[points],0,(255,255,255),1)
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()

resultado de la operación:

返回值 rect:
 ((280.3700256347656, 138.58999633789062), (63.781028747558594, 154.99778747558594), 81.8698959350586)

 转换后的 points:
 [[199.14005 117.98   ]
 [352.58002  96.06   ]
 [361.6     159.19998]
 [208.16003 181.12   ]]

inserte la descripción de la imagen aquí
Como se puede ver en la salida:

  • El valor devuelto rect representa la información de la característica del rectángulo devuelta.
    La estructura del valor es (centro del rectángulo delimitador más pequeño (x, y), (ancho, alto), ángulo de rotación)
  • Los puntos convertidos son algunos puntos, que son puntos de contorno que se pueden usar como parámetros de la función cv2.drawContours().

círculo envolvente más pequeño

La función cv2.minEnclosingCircle() utiliza un algoritmo iterativo para construir un círculo envolvente con el área más pequeña de un objeto.
La sintaxis de esta función es:

centro, radio = cv2.minEnclosingCircle(puntos)

En la fórmula:

  • El centro de valor de retorno es el centro del círculo más pequeño que lo encierra.
  • El radio del valor de retorno es el radio del círculo más pequeño que lo encierra.
  • El argumento apunta es el contorno.

Ejemplo: Use la función cv2.minEnclosingCircle() para construir el círculo mínimo que encierra una imagen.

el código se muestra a continuación:

import cv2
o = cv2.imread('cc.bmp')
cv2.imshow("original",o)
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,
 cv2.RETR_LIST,
 cv2.CHAIN_APPROX_SIMPLE)
(x,y),radius = cv2.minEnclosingCircle(contours[0])
center = (int(x),int(y))
radius = int(radius)
cv2.circle(o,center,radius,(255,255,255),2)
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()

resultado de ejecución:
inserte la descripción de la imagen aquí

elipse de mejor ajuste

En OpenCV, la función cv2.fitEllipse() se puede usar para construir la elipse que mejor se ajuste. La sintaxis de esta función es:

retval = cv2.fitEllipse(puntos)

En la fórmula:

  • El valor de retorno retval es un valor de tipo RotatedRect. Esto se debe a que la función devuelve el rectángulo circunscrito de la elipse ajustada, y retval contiene información de parámetros como el centroide, el ancho, la altura y el ángulo de rotación del rectángulo circunscrito, que coinciden con el punto central, la longitud del eje y el ángulo de rotación de la elipse
  • El argumento apunta es el contorno.

Ejemplo: Use la función cv2.fitEllipse() para construir una elipse de mejor ajuste.

el código se muestra a continuación:

import cv2
o = cv2.imread('cc.bmp')
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
cv2.imshow("original",o)
ellipse = cv2.fitEllipse(contours[0])
print("ellipse=",ellipse)
cv2.ellipse(o,ellipse,(0,255,0),3)
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()

resultado de la operación:

inserte la descripción de la imagen aquí
Al mismo tiempo, el programa también mostrará los siguientes resultados de ejecución:

ellipse= ((276.2112731933594, 139.6067352294922), (63.01350021362305,
166.72308349609375), 82.60102844238281)

Entre los resultados de ejecución anteriores:

  • (276.2112731933594, 139.6067352294922) es el punto central de la elipse.
  • (63.01350021362305, 166.72308349609375) es la longitud del eje de la elipse.
  • 82.60102844238281 es el ángulo de rotación de la elipse.

línea de mejor ajuste

En OpenCV, la función cv2.fitLine() se utiliza para construir la mejor línea de ajuste. La sintaxis de esta función es:

línea = cv2.fitLine( puntos, distType, param, reps, aeps )

En la fórmula, la línea es el valor devuelto, que es el parámetro de la mejor línea de ajuste devuelta.
Los parámetros en la fórmula son los siguientes:

  • puntos: contorno.
  • distType: tipo de distancia. Al ajustar una línea recta, la suma de las distancias desde el punto de entrada hasta la línea recta ajustada debe minimizarse, y sus tipos se muestran en la Tabla 12-2.
  • param: parámetro de distancia, relacionado con el tipo de distancia seleccionado. Cuando este parámetro se establece en 0, la función elegirá automáticamente el valor óptimo.
  • reps: Se utiliza para indicar la precisión radial necesaria para el ajuste de una línea recta, normalmente el valor se establece en 0,01.
  • aeps: Se utiliza para indicar la precisión angular necesaria para el ajuste de una línea recta, normalmente el valor se establece en 0,01.

inserte la descripción de la imagen aquí

Ejemplo: Use la función cv2.fitLine() para construir una línea de mejor ajuste.

el código se muestra a continuación:

import cv2
o = cv2.imread('cc.bmp')
cv2.imshow("original",o)
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
rows,cols = o.shape[:2]
[vx,vy,x,y] = cv2.fitLine(contours[0], cv2.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((cols-x)*vy/vx)+y)
cv2.line(o,(cols-1,righty),(0,lefty),(0,255,0),2)
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()

resultado de la operación:
inserte la descripción de la imagen aquí

triángulo envolvente más pequeño

En OpenCV, la función cv2.minEnclosingTriangle() se usa para construir el triángulo envolvente mínimo. La sintaxis de esta función es:

retval, triángulo = cv2.minTriánguloEncerrado(puntos)

Hay dos valores de retorno en la fórmula:

  • retval: El área del triángulo más pequeño que lo encierra.
  • triángulo: El conjunto de tres vértices del triángulo más pequeño que lo encierra.
    Los puntos de parámetro en la fórmula son el contorno.

Ejemplo: use la función cv2.minEnclosingTriangle() para construir el triángulo envolvente mínimo.

el código se muestra a continuación:

import cv2
o = cv2.imread('cc.bmp')
cv2.imshow("original",o)
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,
 cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
#
area,trgl = cv2.minEnclosingTriangle(contours[0])
print("area=",area)
print("trgl:",trgl)
for i in range(0, 3):
  cv2.line(o, tuple(trgl[i][0]),tuple(trgl[(i + 1) % 3][0]), (255,255,255), 2)
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()

Error de ejecución:

inserte la descripción de la imagen aquí
Nos recuerda que en opecv4.7, el tipo del valor del parámetro pasado por la función de línea ha cambiado y el tipo actual no lo admite. Vamos a convertir el tipo e intentarlo de nuevo.

el código se muestra a continuación:

import cv2

o = cv2.imread('cc.bmp')
cv2.imshow("original", o)
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,
                                       cv2.RETR_LIST,
                                       cv2.CHAIN_APPROX_SIMPLE)
#
area, trgl = cv2.minEnclosingTriangle(contours[0])
print("area=", area)
print("trgl:", trgl)
for i in range(0, 3):
    original_coordinate = tuple(trgl[i][0])
    original_coordinate2 = tuple(trgl[(i + 1) % 3][0])
    # 将坐标的每个分量分别转换为整数
    x_int = int(original_coordinate[0])
    y_int = int(original_coordinate[1])
    x_int2 = int(original_coordinate2[0])
    y_int2 = int(original_coordinate2[1])
    # 将坐标转换为整数,
    coordinate_as_int = (x_int, y_int)
    coordinate_as_int2 = (x_int2, y_int2)

    cv2.line(o, coordinate_as_int, coordinate_as_int2, (255, 255, 255), 2)
cv2.imshow("result", o)
cv2.waitKey()
cv2.destroyAllWindows()

El resultado de la operación es el siguiente:

inserte la descripción de la imagen aquí
De los resultados anteriores se puede ver que:

  • El área de valor de retorno es el área del triángulo envolvente más pequeño.
  • El valor de retorno trgl son los tres vértices del triángulo envolvente más pequeño.

polígono de aproximación

La función cv2.approxPolyDP() se utiliza para construir una curva poligonal aproximada con la precisión especificada. La sintaxis de esta función es:

approxCurve = cv2.approxPolyDP(curva, epsilon, cerrado)

En la fórmula, el valor devuelto approxCurve es el conjunto de puntos que se aproxima al polígono.

Los parámetros en la fórmula son los siguientes:

  • la curva es el contorno.
  • epsilon es la precisión, la distancia máxima entre el punto límite del contorno original y el límite del polígono de aproximación.
  • cerrado es un valor booleano. Cuando el valor es True, el polígono de aproximación está cerrado; de lo contrario, el polígono de aproximación no está cerrado.
    函数 cv2.approxPolyDP()采用的是 Douglas-Peucker 算法(DP 算法).

Tome la Figura 12-23 como ejemplo, el algoritmo primero encuentra los dos puntos con la distancia más alejada del contorno y conecta los dos puntos (vea (b) de la Figura 12-23).

A continuación, busque un punto en el contorno que esté más alejado de la línea recta actual y conecte este punto con la línea recta original para formar un polígono cerrado.En este momento, se obtiene un triángulo, como se muestra en © en la Figura 12-23 .

El proceso anterior se repite continuamente, y el punto recién encontrado más alejado del polígono actual se agrega al resultado. Cuando la distancia de todos los puntos del contorno al polígono actual es menor que el valor del parámetro epsilon de la función cv2.approxPolyDP(), la iteración se detiene. Finalmente, se puede obtener el resultado del procesamiento que se muestra en (f) de la Figura 12-23.

A través del proceso anterior, podemos saber que épsilon es la información de precisión del polígono de aproximación. Normalmente, esta precisión se establece como un porcentaje de la longitud total del polígono.

inserte la descripción de la imagen aquí

Ejemplo: use la función cv2.approxPolyDP() para construir polígonos de aproximación con diferentes precisiones.

el código se muestra a continuación:

import cv2
#----------------读取并显示原始图像-------------------------------
o = cv2.imread('cc.bmp')
cv2.imshow("original",o)
#----------------获取轮廓-------------------------------
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,
 cv2.RETR_LIST,
 cv2.CHAIN_APPROX_SIMPLE)
#----------------epsilon=0.1*周长-------------------------------
adp = o.copy()
epsilon = 0.1*cv2.arcLength(contours[0],True)
approx = cv2.approxPolyDP(contours[0],epsilon,True)
adp=cv2.drawContours(adp,[approx],0,(0,0,255),2)
cv2.imshow("result0.1",adp)
#----------------epsilon=0.09*周长-------------------------------
adp = o.copy()
epsilon = 0.09*cv2.arcLength(contours[0],True)
approx = cv2.approxPolyDP(contours[0],epsilon,True)
adp=cv2.drawContours(adp,[approx],0,(0,0,255),2)
cv2.imshow("result0.09",adp)
#----------------epsilon=0.055*周长-------------------------------
adp = o.copy()
epsilon = 0.055*cv2.arcLength(contours[0],True)
approx = cv2.approxPolyDP(contours[0],epsilon,True)
adp=cv2.drawContours(adp,[approx],0,(0,0,255),2)
cv2.imshow("result0.055",adp)
#----------------epsilon=0.05*周长-------------------------------
adp = o.copy()
epsilon = 0.05*cv2.arcLength(contours[0],True)
approx = cv2.approxPolyDP(contours[0],epsilon,True)
adp=cv2.drawContours(adp,[approx],0,(0,0,255),2)
cv2.imshow("result0.05",adp)
#----------------epsilon=0.02*周长-------------------------------
adp = o.copy()
epsilon = 0.02*cv2.arcLength(contours[0],True)
approx = cv2.approxPolyDP(contours[0],epsilon,True)
adp=cv2.drawContours(adp,[approx],0,(0,0,255),2)
cv2.imshow("result0.02",adp)
#----------------等待释放窗口-------------------------------
cv2.waitKey()
cv2.destroyAllWindows()

resultado de la operación:

inserte la descripción de la imagen aquí
Se puede ver a partir de los resultados de la ejecución del programa que en la función cv2.approxPolyDP(), la precisión de la aproximación de polígonos se puede controlar mediante el parámetro epsilon.
Cabe señalar que las imágenes utilizadas en esta sección son todas las imágenes con un solo contorno, y los contornos procesados ​​son todos los contornos[0]. Si hay varios contornos en la imagen original para procesar, debe prestar atención al índice del contorno de control, es decir, el valor i en contornos[i], para que apunte a un contorno específico.

La imagen original del experimento.

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/hai411741962/article/details/132187043
Recomendado
Clasificación