OPENCV encuentra el rectángulo inscrito más grande de un gráfico

  Para el rectángulo circunscrito más grande del contorno, Opencv tiene un algoritmo listo para usar, y el círculo inscrito más grande también tiene un algoritmo proporcionado. Pero no existe un algoritmo listo para usar para rectángulos inscritos. Este artículo usa C++ para realizar el método de tomar el rectángulo inscrito más grande del contorno, para su referencia.

La idea básica de la realización es:

  1. Procesando la imagen como una imagen en escala de grises
    De hecho, el código implementado lee directamente una imagen en escala de grises, la cual se omite. Por supuesto, si se implementa, opencv también es muy fácil de implementar.

  2. Transformación de coordenadas
    Encuentra los bordes del contorno, encuentra el ángulo de la dirección principal del contorno. Con una transformación afín, la dirección principal actúa como el eje x.

  3. Ráster estadísticamente válido.
    En los gráficos convertidos, busque el área con un valor de gris de 255. En aras de la eficiencia del procesamiento, el área no se procesa directamente según el píxel, sino que se divide en pequeñas áreas de cuadrícula una por una. Cada área registra si es un valor válido y registra cuántas áreas igualmente válidas hay en el lado izquierdo del área. Lo mismo es válido actualmente para trabajar directamente con píxeles. Este ejemplo solo proporciona una forma de pensar, porque en algunos casos, se espera que se ignore el punto de ruido, por lo que una cuadrícula más pequeña que un cierto tamaño no afectará el rectángulo delimitador máximo encontrado.

  4. Método de histograma para contar el área más grande
    Después del procesamiento anterior, se obtiene la información del área efectiva de cada fila, incluido el número de fila y los datos del área efectiva de cada fila. Puede suponer que esto es un histograma y usa el histograma para encontrar el área más grande. Como se muestra en la siguiente figura:
    el valor pico máximo local se puede calcular utilizando la pila de estructura de datos, que es fácil de implementar.
    inserte la descripción de la imagen aquí

  5. encontrar vértices de rectángulos

Mediante el método anterior se pueden encontrar las coordenadas de las cuatro áreas correspondientes al área mayor del histograma, es decir, los cuatro vértices del rectángulo inscrito.

  1. Las coordenadas se convierten en las coordenadas de la imagen original mediante una transformación afín.

código fuente

void InnerRect::getInnerRect(const cv::Mat& image, std::vector<cv::Point2f>& Rect_points, const double grid_spacing_in_pixel)
{
    
    
	const int grid_spacing_as_int = std::floor(grid_spacing_in_pixel);
	const int half_grid_spacing_as_int = std::floor(grid_spacing_in_pixel*0.5);

	// *********************** I. Find the main directions of the map and rotate it in this manner. ***********************
	cv::Mat R;
	cv::Rect bbox;
	cv::Mat rotated_image;
	Rotator image_rotation;
	image_rotation.computeImageRotationMatrix(image, R, bbox);
	image_rotation.rotateImage(image, rotated_image, R, bbox);

	// compute min/max room coordinates
	cv::Point min_room(1000000, 1000000), max_room(0, 0);
	for (int v=0; v<rotated_image.rows; ++v)
	{
    
    
		for (int u=0; u<rotated_image.cols; ++u)
		{
    
    
			if (rotated_image.at<uchar>(v,u)==255)
			{
    
    
				min_room.x = std::min(min_room.x, u);
				min_room.y = std::min(min_room.y, v);
				max_room.x = std::max(max_room.x, u);
				max_room.y = std::max(max_room.y, v);
			}
		}
	}

	cv::Mat inflated_rotated_image;
	cv::erode(rotated_image, inflated_rotated_image, cv::Mat(), cv::Point(-1, -1), half_grid_spacing_as_int);

	// *********************** II. Find the nodes and their neighbors ***********************
	// get the nodes in the free space
	std::vector<std::vector<InnerExploratorNode> > nodes; // 2-dimensional vector to easily find the neighbors
	int number_of_nodes = 0;


	// todo: create grid in external class - it is the same in all approaches
	// todo: if first/last row or column in grid has accessible areas but center is inaccessible, create a node in the accessible area
	for(int y=min_room.y+half_grid_spacing_as_int; y<max_room.y; y+=grid_spacing_as_int)
	{
    
    
		// for the current row create a new set of neurons to span the network over time
		std::vector<InnerExploratorNode> current_row;
		for(int x=min_room.x+half_grid_spacing_as_int; x<max_room.x; x+=grid_spacing_as_int)
		{
    
    
			// create node if the current point is in the free space
			InnerExploratorNode current_node;
			current_node.center_ = cv::Point(x,y);
			//if(rotated_image.at<uchar>(y,x) == 255)				
			// could make sense to test all pixels of the cell, not only the center
			if (completeCellTest(inflated_rotated_image, current_node.center_, grid_spacing_as_int) == true)
			{
    
    
				//如果是第一个元素,则为0,否则是前一个元素计数基础上加1
				current_node.leftCount = (x == (min_room.x+half_grid_spacing_as_int) ? 0 : (current_row.back().leftCount +1));
				++number_of_nodes;
			}
			// add the obstacle nodes as already visited
			else
			{
    
    
				current_node.leftCount = 0;
				++number_of_nodes;
			}
			current_row.push_back(current_node);
		}

		// insert the current row into grid
		nodes.push_back(current_row);
	}
	std::cout << "found " << number_of_nodes <<  " nodes" << std::endl;

	if(nodes.empty())
	{
    
    
		return;
	}

	//采用柱状直方图统计方式,对每一列找最大面积
	int max_area = 0;
	int max_up = 0;
	int max_down = 0;
	int max_left = 0;
	int max_right = 0;
	int m = nodes.size();
	int n = nodes[0].size();
	for(int j = 0; j < n; j++)
	{
    
    
		std::vector<int> up(m, 0), down(m, 0);
		
		std::stack<int> stk;
		for(int i = 0; i < m; i++)
		{
    
    
			while(!stk.empty() && nodes[stk.top()][j].leftCount >= nodes[i][j].leftCount)
			{
    
    
				stk.pop();
			}
			up[i] = stk.empty() ? -1 : stk.top();
			stk.push(i);
		}
		stk = std::stack<int>();
		for (int i = m-1; i >= 0; i--){
    
    
			while(!stk.empty() && nodes[stk.top()][j].leftCount >= nodes[i][j].leftCount)
			{
    
    
				stk.pop();
			}
			down[i] = stk.empty() ? m : stk.top();
			stk.push(i);
		}


		for(int i = 0; i < m; i++)
		{
    
    
			int height = down[i] - up[i] -1;
			int area = height * nodes[i][j].leftCount;
			if(max_area < area)
			{
    
    
				max_area = area;
				max_up = up[i] + 1;
				max_down = down[i];
				max_left = j - nodes[i][j].leftCount + 1;
				max_right = j;
			}
		}
	}
	
	int min_x = min_room.x + max_left * grid_spacing_as_int + half_grid_spacing_as_int;
	int min_y = min_room.y + max_up * grid_spacing_as_int + half_grid_spacing_as_int;
	int max_x = min_room.x + max_right * grid_spacing_as_int + half_grid_spacing_as_int;
	int max_y = min_room.y + max_down * grid_spacing_as_int;



	//transform the calculated path back to the originally rotated map
	std::vector<cv::Point2f> fov_poses;
	std::vector<cv::Point2f> fov_coverage_path;

	fov_coverage_path.push_back(cv::Point2f(min_x, min_y));
	fov_coverage_path.push_back(cv::Point2f(max_x, min_y));
	fov_coverage_path.push_back(cv::Point2f(max_x, max_y));
	fov_coverage_path.push_back(cv::Point2f(min_x, max_y));
	fov_coverage_path.push_back(cv::Point2f(min_x, min_y));

	image_rotation.transformPathBackToOriginalRotation(fov_coverage_path, fov_poses, R);

	Rect_points.clear();
	Rect_points.insert(Rect_points.end(), fov_poses.begin(), fov_poses.end());

}

El código fuente completo se ha subido a github y se puede descargar directamente:

rectángulo máximo inscrito

Efecto de logro:
Imagen original:
inserte la descripción de la imagen aquí
Realice el efecto de rectángulo inscrito encontrado:
inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/yangcunbiao/article/details/126208668
Recomendado
Clasificación