OpenCV license plate positioning (C++)

Recently I started to get in touch with C++, so I took a small OpenCV project to practice. In the automatic license plate recognition system, it is a complicated process from the acquisition of the car image to the license plate character processing. This article uses a simple method to deal with the license plate location.

The car license plate in our country generally consists of seven characters and a dot. The height and width of the license plate characters are fixed, 90mm and 45mm respectively, the distance between the seven characters is also a fixed 12mm, and the diameter of the dot separator is 10mm.


The pictures used are randomly found from Baidu (invaded and deleted), show the original picture and grayscale picture:

#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;
}

Insert picture description hereInsert picture description here
Each pixel of a grayscale image is quantized by a number, and each pixel of a color image is quantized by a vector composed of three numbers. The use of a grayscale image will be more convenient for subsequent processing.


Image noise reduction

Each image contains a certain degree of noise. In most cases, smoothing techniques (also often called filtering or noise reduction techniques) are needed to suppress or remove. These techniques include Gaussian smoothing based on two-dimensional discrete convolution, Mean smoothing, median smoothing based on statistical methods, etc. Here, Gaussian smoothing based on two-dimensional discrete convolution is used to denoise the gray image. The processed image effect is as follows:

Insert picture description here


Morphological processing

After the Gaussian denoising is completed, in order to extract the contour of the license plate more accurately, we need to perform morphological processing on the image. Here, we perform the opening operation on it, and the processing is as follows:

Insert picture description here
Open operation is to perform erode first and then dilate. The process is open operation. It eliminates small areas with higher brightness and separates objects at delicate points. For larger objects, it can be smoothed without changing its area significantly. Border and other functions.

Erode operation is also an erosion operation, similar to convolution, but also a neighborhood operation, but the calculation is not a weighted sum, but to sort the pixels in the neighborhood by gray value, and then select the minimum value of the group As the output gray value.

The dilate operation is the expansion operation, similar to the corrosion operation, the expansion is to take the maximum value in the neighborhood of each location. Since it is the maximum value in the neighborhood, it is obvious that the average value of the overall brightness of the expanded output image will increase compared with the original image, and the size of the brighter object in the image will become larger; on the contrary, the size of the darker object Will decrease or even disappear.


Threshold segmentation

After completing the preliminary morphological processing, we need to threshold the image. Here we use Otsu threshold processing. The processed effect is as follows:

Insert picture description here
When digitally processing an image, we need to divide the image into several specific areas with unique properties. Each area represents a collection of pixels, and each collection represents an object. The technology to complete this process is usually called an image. Segmentation, it is a key step from image processing to image analysis. In fact, this process is not difficult to understand. Just like we humans look at the scenery, the world we see is made up of many objects, just like the classroom is made up of people, tables, books, blackboards, etc. Through threshold processing, we hope to separate our research objects from the background.


Edge detection

After Otsu threshold segmentation, we need to perform edge detection on the image. We use Canny edge detection here. The result of the processing is as follows:
Insert picture description here
Next, perform a closed operation and an open operation to fill the small black hole in the white object and Smooth the border, and the effect after processing is as follows: At
Insert picture description here
this time, the outline of the license plate has been initially selected, but there are still some white blocks interfering.

The code for the above process:

// 得出轮廓
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;
}

Select contour

Now that we have the contour, we need to filter out the contour where the license plate is located. Since the ratio of the width and height of the license plate is fixed, we filter according to this geometric feature. The effect is as
Insert picture description here
Insert picture description here
follows: The code is as follows:

// 车牌轮廓点
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;
}

The final main function:

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;
}

Guess you like

Origin blog.csdn.net/weixin_44613063/article/details/109409564