Posicionamiento de matrículas OpenCV (C ++)

Recientemente comencé a ponerme en contacto con C ++, así que tomé un pequeño proyecto OpenCV para practicar. En el sistema de reconocimiento automático de matrículas, es un proceso complicado desde la adquisición de la imagen del automóvil hasta el procesamiento del carácter de la matrícula. Este artículo utiliza un método simple para tratar la ubicación de la matrícula.

La matrícula del automóvil en nuestro país generalmente consta de siete caracteres y un punto. La altura y el ancho de los caracteres de la matrícula son fijos, 90 mm y 45 mm respectivamente, la distancia entre los siete caracteres también es fija de 12 mm y el diámetro del separador de puntos es de 10 mm.


Las imágenes utilizadas se encuentran aleatoriamente en Baidu (invadidas y eliminadas), muestran la imagen original y la imagen en escala de grises:

#include <iostream>  
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>

using namespace std;
using namespace cv;

int main() {
    
    
	// 读入原图
	Mat img = imread("license.jpg");
	Mat gray_img;
	// 生成灰度图像
	cvtColor(img, gray_img, CV_BGR2GRAY);
	// 在窗口中显示游戏原画
	imshow("原图", img);
	imshow("灰度图", gray_img);
	waitKey(0);
	return 0;
}

Inserte la descripción de la imagen aquíInserte la descripción de la imagen aquí
Cada píxel de una imagen en escala de grises se cuantifica mediante un número y cada píxel de una imagen en color se cuantifica mediante un vector compuesto por tres números El uso de una imagen en escala de grises será más conveniente para el procesamiento posterior.


Reducción de ruido de imagen

Cada imagen contiene un cierto grado de ruido. En la mayoría de los casos, se necesitan técnicas de suavizado (también llamadas técnicas de filtrado o reducción de ruido) para suprimirlas o eliminarlas. Estas técnicas incluyen el suavizado gaussiano basado en convolución discreta bidimensional, Suavizado medio, suavizado mediano basado en métodos estadísticos, etc. Aquí, el suavizado gaussiano basado en una convolución discreta bidimensional se utiliza para reducir el ruido de la imagen gris. El efecto de imagen procesada es el siguiente:

Inserte la descripción de la imagen aquí


Procesamiento morfológico

Después de completar la eliminación de ruido gaussiana, para extraer con mayor precisión el contorno de la placa de matrícula, es necesario realizar un procesamiento morfológico sobre la imagen. Aquí realizamos la operación de apertura sobre ella, y el procesamiento es el siguiente:

Inserte la descripción de la imagen aquí
La operación abierta consiste en realizar erosionar primero y luego dilatar. El proceso es operación abierta. Elimina áreas pequeñas con mayor brillo y separa objetos en puntos delicados. Para objetos más grandes, se puede suavizar sin cambiar significativamente su área. Frontera y otras funciones.

La operación de erosión también es una operación de erosión, similar a la convolución, pero también una operación de vecindad, pero el cálculo no es una suma ponderada, sino para ordenar los píxeles de la vecindad por valor de gris y luego seleccionar el valor mínimo del grupo Como valor gris de salida.

La operación de dilatación es la operación de expansión, similar a la operación de corrosión, la expansión es tomar el valor máximo en las cercanías de cada ubicación. Dado que es el valor máximo en la vecindad, es obvio que el valor promedio del brillo general de la imagen de salida expandida aumentará en comparación con la imagen original, y el tamaño del objeto más brillante en la imagen será mayor; por el contrario, el tamaño del objeto más oscuro Disminuirá o incluso desaparecerá.


Segmentación de umbral

Después de completar el procesamiento morfológico preliminar, necesitamos establecer el umbral de la imagen. Aquí usamos el procesamiento de umbral Otsu. El efecto procesado es el siguiente:

Inserte la descripción de la imagen aquí
Cuando procesamos digitalmente una imagen, necesitamos dividir la imagen en varias áreas específicas con propiedades únicas. Cada área representa una colección de píxeles y cada colección representa un objeto. La tecnología para completar este proceso generalmente se llama imagen. La segmentación, es un paso clave desde el procesamiento de imágenes hasta el análisis de imágenes. De hecho, este proceso no es difícil de entender, así como los humanos miramos el paisaje, el mundo que vemos está formado por muchos objetos, así como el aula está formada por personas, mesas, libros, pizarrones, etc. A través del procesamiento de umbral, esperamos poder separar nuestros objetos de investigación del fondo.


Detección de bordes

Después de la segmentación del umbral de Otsu, debemos realizar la detección de bordes en la imagen. Aquí utilizamos la detección de bordes Canny. El resultado del procesamiento es el siguiente:
Inserte la descripción de la imagen aquí
A continuación, realice una operación cerrada y una operación abierta para llenar el pequeño agujero negro en el objeto blanco y Alise el borde y el efecto después del procesamiento es el siguiente: en
Inserte la descripción de la imagen aquí
este momento, el contorno de la placa de matrícula se ha seleccionado inicialmente, pero todavía hay algunos bloques blancos que interfieren.

El código para el proceso anterior:

// 得出轮廓
bool contour(Mat image, vector<vector<Point>> &contours, vector<Vec4i> &hierarchy) {
    
    
	Mat img_gau, img_open, img_seg, img_edge;
	// 高斯模糊
	GaussianBlur(image, img_gau, Size(7, 7), 0, 0);
	// 开运算
	Mat element = getStructuringElement(MORPH_RECT, Size(23, 23));
	morphologyEx(img_gau, img_open, MORPH_OPEN, element);
	addWeighted(img_gau, 1, img_open, -1, 0, img_open);
	// 阈值分割
	threshold(img_open, img_seg, 0, 255, THRESH_BINARY + THRESH_OTSU);
	// 边缘检测
	Canny(img_seg, img_edge, 200, 100);
	element = getStructuringElement(MORPH_RECT, Size(22, 22));
	morphologyEx(img_edge, img_edge, MORPH_CLOSE, element);
	morphologyEx(img_edge, img_edge, MORPH_OPEN, element);
	findContours(img_edge, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
	return true;
}

Seleccionar contorno

Ahora que tenemos el contorno, necesitamos filtrar el contorno donde se encuentra la matrícula. Dado que la relación entre el ancho y la altura de la matrícula es fija, filtramos de acuerdo con esta característica geométrica. El efecto es el
Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí
siguiente: El código es el siguiente:

// 车牌轮廓点
Point2f(*choose_contour(vector<vector<Point>> contours))[2] {
    
    
	int size = (int)contours.size();
	int i_init = 0;
	static Point2f contour[4][2];
	Point2f (*contours_result)[2] = new Point2f[size][2];
	for (int i = 0; i < size; i++){
    
    
		// 获取边框数据
		RotatedRect number_rect = minAreaRect(contours[i]);
		Point2f rect_point[4];
		number_rect.points(rect_point);
		float width = rect_point[0].x - rect_point[1].x;
		float height = rect_point[0].y - rect_point[3].y;
		// 用宽高比筛选
		if (width < height) {
    
    
			float temp = width;
			width= height;
			height = temp;
		}
		float ratio = width / height;
		if (2.5 < ratio && ratio < 5.5) {
    
    
			contours_result[i_init][0] = rect_point[0];
			contours_result[i_init][1] = rect_point[2];
			i_init++;
		}
		
	}
	return contours_result;
}

// 截取车牌区域
int license_gain(Point2f (*choose_license)[2], Mat img) {
    
    
	int size = (int)(_msize(choose_license) / sizeof(choose_license[0]));
	// 绘制方框
	for (int i = 0; i < size; i++) {
    
    
		if ((int)choose_license[i][0].x > 1 && (int)choose_license[i][0].y > 1) {
    
    
			int x = (int)choose_license[i][1].x;
			int y = (int)choose_license[i][1].y;
			int height = (int)(choose_license[i][0].x) - (int)(choose_license[i][1].x);
			int width = (int)(choose_license[i][0].y) - (int)(choose_license[i][1].y);
			//cout << height << " " << width << endl;
			Rect choose_rect(x, y, height, width);
			Mat number_img = img(choose_rect);
			rectangle(img, choose_license[i][0], choose_license[i][1], Scalar(0, 0, 255), 2, 1, 0);
			imshow("车牌单独显示" + to_string(i), number_img);
		}
	}
	imshow("绘制方框", img);
	return 0;
}

La función principal final:

int main() {
    
    
	// 读入原图
	Mat img = imread("license.jpg");
	Mat gray_img;
	// 生成灰度图像
	cvtColor(img, gray_img, CV_BGR2GRAY);
	// 得出轮廓
	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	contour(gray_img, contours, hierarchy);
	// 截取车牌
	Point2f (*choose_license)[2] = choose_contour(contours);
	license_gain(choose_license, img);
	delete [] choose_license;
	waitKey(0);
	return 0;
}

Supongo que te gusta

Origin blog.csdn.net/weixin_44613063/article/details/109409564
Recomendado
Clasificación