[Primeros pasos de OpenCV] Detección de forma/contorno


Mi blog personal: Mouren·Blog
Cuenta pública de WeChat: Saco de Mouren
CSDN: Cao Mouren



Preprocesamiento antes de la detección: imagen binaria de detección de bordes

La denominada detección de forma/contorno consiste en reconocer la figura compuesta por el contorno del borde en la imagen a detectar, y detectar los puntos del contorno, y luego juzgarlos. Por lo tanto, para facilitar la detección de contornos, debemos realizar un preprocesamiento antes de la detección:
escala de grises -> filtro gaussiano -> algoritmo de detección de bordes Canny ->
código de expansión de imagen:

/// <summary>
/// 形状检测前的预处理(灰度->高斯滤波->Canny边缘算法->膨胀)
/// </summary>
/// <param name="imgIn">Mat 类,输入图像</param>
/// <returns>Mat类,预处理后的图像</returns>
Mat preProcessing(Mat imgIn) {
    
    
	Mat imgGray, imgBlur, imgCanny, imgDila;
	//先定义一个内核
	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
	cvtColor(imgIn, imgGray, COLOR_BGR2GRAY, 0);//灰度变换
	GaussianBlur(imgGray, imgBlur, Size(65, 65), 1, 1);//高斯滤波去噪点
	Canny(imgBlur, imgCanny, 40, 120);//Canny边缘检测
	dilate(imgCanny, imgDila, kernel);//图像膨胀
	return imgDila;//返回处理完后的图像
}

función findContours ---- contorno de detección

Rol de función: detectar contornos a partir de imágenes binarias
Definición de función:

void findContours( 
	InputArray image, 
	OutputArrayOfArrays contours,
    OutputArray hierarchy, 
    int mode,
    int method, 
    Point offset = Point()
    );

Explicación de parámetros:

  • imagen: imagen de un solo canal de 8 bits, los píxeles distintos de cero se consideran 1 y los píxeles cero se consideran 0. Aquí, la entrada general se transforma en una imagen 01 a través de la detección de bordes Canny, la selección inRange, Laplace, etc.
  • contornos: una variable de contornos detectados, cada contorno se almacena como un vector de punto. El tipo de perfil se define como:
vector<vector<Point>>

Este es un vector doble, el vector externo almacena los contornos de todas las formas en la imagen a ser detectadas, el vector interno almacena el conjunto de puntos de un cierto contorno (un vector de un conjunto de puntos compuesto por Puntos continuos).

  • jerarquía: El tipo de definición de parámetro es:
vector<Vec4i> hierarchy

Definición de Vec4i:

typedef Vec<int, 4> Vec4i

Cada elemento del vector contiene 4 variables int, por lo que, por definición, la jerarquía es un vector y cada elemento del vector es una matriz que contiene 4 int.
Existe una correspondencia uno a uno entre los elementos de la jerarquía vectorial y los elementos de los contornos del vector de contorno, y la capacidad de los vectores es la misma. Las 4 variables int de cada elemento en la jerarquía son jerarquía[i][0] ~ jerarquía[i][3], que representan respectivamente el número del siguiente contorno, el contorno anterior, el contorno principal y el contorno incrustado del actual contorno i índice. Si el contorno actual no corresponde a uno de los cuatro, la jerarquía correspondiente[i][*] se establece en -1.

  • modo: el modo de detección del contorno, su valor se define de la siguiente manera:
enum RetrievalModes {
    
    
    /** retrieves only the extreme outer contours. It sets `hierarchy[i][2]=hierarchy[i][3]=-1` for
    all the contours. */
    RETR_EXTERNAL  = 0,
    /** retrieves all of the contours without establishing any hierarchical relationships. */
    RETR_LIST      = 1,
    /** retrieves all of the contours and organizes them into a two-level hierarchy. At the top
    level, there are external boundaries of the components. At the second level, there are
    boundaries of the holes. If there is another contour inside a hole of a connected component, it
    is still put at the top level. */
    RETR_CCOMP     = 2,
    /** retrieves all of the contours and reconstructs a full hierarchy of nested contours.*/
    RETR_TREE      = 3,
    RETR_FLOODFILL = 4 //!<
};

La traducción es la siguiente:
RETR_EXTERNAL: solo detecta el contorno más externo y se ignora el contorno interno contenido en el contorno externo;
RETR_LIST: detecta todos los contornos, incluidos los contornos interno y externo, pero los contornos detectados no establecen una relación jerárquica, y entre sí Independientes, sin jerarquía. Esto significa que no hay un contorno principal o un contorno incrustado en este modo de recuperación, por lo que los componentes tercero y cuarto de todos los elementos en el vector de jerarquía se establecerán en -1; RETR_CCOMP: detecta todos los contornos, pero todos los contornos solo se establecen Dos
jerárquicos relaciones, el exterior es la capa superior, si el contorno interior en la periferia exterior contiene otra información de contorno, todos los contornos en la periferia interior pertenecen a la capa superior; RETR_TREE: detecta todos los contornos y crea una estructura de árbol jerárquica para todos los contornos
. El contorno exterior contiene el contorno interior, y el contorno interior puede continuar conteniendo el contorno interior.
RETR_FLOODFILL: No hay presentación oficial, así que no lo sé por ahora.

Referencia: https://www.cnblogs.com/GaloisY/p/11062065.html

  • método: método de aproximación al contorno, el valor se define de la siguiente manera:
enum ContourApproximationModes {
    
    
    /** stores absolutely all the contour points. That is, any 2 subsequent points (x1,y1) and
    (x2,y2) of the contour will be either horizontal, vertical or diagonal neighbors, that is,
    max(abs(x1-x2),abs(y2-y1))==1. */
    CHAIN_APPROX_NONE      = 1,
    /** compresses horizontal, vertical, and diagonal segments and leaves only their end points.
    For example, an up-right rectangular contour is encoded with 4 points. */
    CHAIN_APPROX_SIMPLE    = 2,
    /** applies one of the flavors of the Teh-Chin chain approximation algorithm @cite TehChin89 */
    CHAIN_APPROX_TC89_L1   = 3,
    /** applies one of the flavors of the Teh-Chin chain approximation algorithm @cite TehChin89 */
    CHAIN_APPROX_TC89_KCOS = 4
};

La traducción es la siguiente:
CHAIN_APPROX_NONE: obtenga cada píxel de cada contorno, y la diferencia de posición de píxel entre dos puntos adyacentes no exceda de 1;
CHAIN_APPROX_SIMPLE: comprima elementos en dirección horizontal, dirección vertical y dirección diagonal, y el valor retiene el foco de esta dirección Coordenadas, si un contorno rectangular solo necesita 4 puntos para guardar la información del contorno;
CHAIN_APPROX_TC89_L1 y CHAIN_APPROX_TC89_KCOS: use uno de los algoritmos de aproximación de cadena Teh-Chinl

Referencia: https://blog.csdn.net/qq_42887760/article/details/86565601

  • Desplazamiento: el desplazamiento del contorno en relación con la imagen original. Por ejemplo: si aquí se escribe Tamaño (-10,-20), el contorno final dibujado se desplaza 10 píxeles hacia la izquierda y 20 píxeles hacia arriba en relación con el contorno real de la imagen original. Si es Tamaño (10,20), significa un desplazamiento hacia la derecha de 10 píxeles y un desplazamiento hacia abajo de 20 píxeles.

La única diferencia entre otra sobrecarga de la función findContours y esta definición es que falta el parámetro de jerarquía y otros parámetros y significados son los mismos, por lo que no los repetiré aquí.

contourArea, función arcLength----área, perímetro

función de área de contorno----área de contorno

Rol de la función: Calcular el área del contorno de la imagen
Definición de la función:

double contourArea( InputArray contour, bool oriented = false );

Explicación de parámetros:

  • contorno: el conjunto de puntos del contorno de entrada
  • orientado: Indica el valor del área del contorno en una dirección determinada, en sentido horario o antihorario, generalmente elija el valor predeterminado falso

función arcLength----longitud del contorno

Papel de la función: calcular el perímetro del contorno de la imagen
Definición de la función:

double arcLength( InputArray curve, bool closed );

Explicación de parámetros:

  • curva: el conjunto de puntos del contorno de entrada
  • cerrado: Indica si la curva está cerrada

función approxPolyDP ---- polilínea curva

Función: ajustar una curva o polígono con el descuento de menos vértices, es decir, convertir una curva suave continua en una curva. El
propósito de la conversión de curvas es obtener el número de vértices de la línea quebrada y luego determinar qué forma función de la que trata el contorno
definición:

void approxPolyDP( 
	InputArray curve,
    OutputArray approxCurve,
    double epsilon, 
    bool closed 
    );

Explicación de parámetros:

  • curva: clase Mat o clase vectorial, conjunto de puntos 2D, generalmente un conjunto de puntos compuesto por puntos de línea de contorno
  • approxCurve: conjunto de puntos de polilínea de salida
  • épsilon: la precisión especificada, que es la distancia máxima entre la curva original y la curva aproximada
  • cerrado: Si es verdadero, significa que la curva de ajuste es cerrada; de lo contrario, si es falso, no es una curva cerrada

función drawContours ---- dibujar contorno

Rol de la función: dibujar el contorno de la imagen que se ha encontrado
Definición de la función:

void drawContours( 
	InputOutputArray image, 
	InputArrayOfArrays contours,
    int contourIdx, 
    const Scalar& color,
    int thickness = 1, 
    int lineType = LINE_8,
    InputArray hierarchy = noArray(),
    int maxLevel = INT_MAX, 
    Point offset = Point() 
    );

Explicación de parámetros:

  • imagen: la imagen para dibujar el contorno
  • contornos: colección de puntos de líneas de contorno
  • contourIdx: Especifica el número del contorno a dibujar, si es un número negativo, se dibujan todos los contornos
  • color: el color de la línea de contorno, generalmente con escalar (azul, verde, rojo)
  • espesor: el ancho de la línea de contorno, si es -1 (cv2.FILLED), es el modo de relleno
  • lineType: tipo de línea, definición de valor:
enum LineTypes {
    
    
    FILLED  = -1,
    LINE_4  = 4, //!< 4-connected line--4连通线型
    LINE_8  = 8, //!< 8-connected line--8连通线型
    LINE_AA = 16 //!< antialiased line--抗锯齿线型
};
  • jerarquía: parámetros opcionales sobre la jerarquía, solo se usan al dibujar parte del contorno
  • maxLevel: El nivel más alto para dibujar contornos, este parámetro es válido solo cuando la jerarquía es válida
    maxLevel=0-----Dibuja todos los contornos que pertenecen al mismo nivel que el contorno de entrada, es decir, el contorno de entrada y su adyacente contornos
    maxLevel=1--- --Dibuja todos los contornos del mismo nivel que el contorno de entrada y sus nodos secundarios
    maxLevel=2-----Dibuja todos los contornos del mismo nivel que el contorno de entrada, sus nodos secundarios y sus nodos secundarios nodos
  • desplazamiento: El desplazamiento del contorno dibujado en relación con la posición del contorno encontrado. Por ejemplo: si aquí se escribe Tamaño (-10,-20), el contorno final dibujado se desplaza 10 píxeles hacia la izquierda y 20 píxeles hacia arriba en relación con la posición del contorno encontrado. Si es Tamaño (10,20), significa un desplazamiento hacia la derecha de 10 píxeles y un desplazamiento hacia abajo de 20 píxeles.

ejemplo

código fuente:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
#ifdef _DEBUG
#pragma comment(lib,"opencv_world453d.lib")
#else
#pragma comment(lib,"opencv_world453.lib")
#endif // _DEBUG


/// <summary>
/// 对预处理后的图像检测轮廓并标出已定义的简单形状
/// </summary>
/// <param name="imgDil">与处理后的图像</param>
/// <param name="img">待检测轮廓的原图</param>
void GetContour(Mat imgDil, Mat img)
{
    
    
	vector<vector<Point>> contour;
	vector<Vec4i> hierarchy;
	findContours(imgDil, contour, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE);
	vector<vector<Point>> conPoly(contour.size());//定义用于存放轮廓折线化后的变量
	vector<Rect> boundRect(contour.size());
	//contour.size()是在该图像中检测到的轮廓个数
	for (int i = 0; i < contour.size(); i++)//所有轮廓遍历
	{
    
    
		string objectType;//形状名称
		float peri = arcLength(contour[i], true);//轮廓周长
		//0.015* peri表示拟合的精度
		approxPolyDP(contour[i], conPoly[i], 0.015 * peri, true);//把一个连续光滑曲线折线化
		boundRect[i] = boundingRect(conPoly[i]);//获得包覆该轮廓最小矩形
		int objCor = (int)conPoly[i].size();//获取折线化后轮廓的顶点个数
#pragma region 定义形状
		if (objCor == 3) //三角形
			objectType = "Tri";
		else if (objCor == 4) {
    
    //矩形
			float aspRatio = (float)boundRect[i].width / boundRect[i].height;//长宽比来区别正方形与矩形
			if (aspRatio > 0.95 && aspRatio < 1.05) 
				objectType = "Square";
			else 
				objectType = "Rect";
		}
		else if (objCor > 4) //圆形
			objectType = "Circle";
#pragma endregion
		drawContours(img, conPoly, i, Scalar(187, 109, 68), 3, LINE_AA);//画出轮廓线
		rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(72,255,104), 4);//用矩形框住选出的图形
		//标出已定义出的图形名称
		putText(img, objectType, {
    
     boundRect[i].x,boundRect[i].y - 5 } , 1, FONT_HERSHEY_PLAIN, Scalar(0, 69, 255), 1);
	}
}

/// <summary>
/// 形状检测前的预处理(灰度->高斯滤波->Canny边缘算法->膨胀)
/// </summary>
/// <param name="imgIn">Mat 类,输入图像</param>
/// <returns>Mat类,预处理后的图像</returns>
Mat PreProcessing(Mat imgIn) {
    
    
	Mat imgGray, imgBlur, imgCanny, imgDila;
	//先定义一个内核
	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
	cvtColor(imgIn, imgGray, COLOR_BGR2GRAY, 0);//灰度变换
	GaussianBlur(imgGray, imgBlur, Size(65, 65), 1, 1);//高斯滤波去噪点
	Canny(imgBlur, imgCanny, 40, 120);//Canny边缘检测
	dilate(imgCanny, imgDila, kernel);//图像膨胀
	return imgDila;//返回处理完后的图像
}

void main() {
    
    
	string path = "D:\\My Bags\\图片\\shapes.png";//原图路径
	Mat img = imread(path);//读取图片
	Mat imgProcessed = PreProcessing(img);//图像预处理
	GetContour(imgProcessed , img);//检测轮廓并标出简单图形名称
	imshow("Image", img);//显示出来
	waitKey(0);
}

resultado de la operación:

Supongo que te gusta

Origin blog.csdn.net/ZBC010/article/details/120690367
Recomendado
Clasificación