Skip to content

Commit fba60d1

Browse files
author
lixiaoxu
committed
车牌尺寸识别
1 parent 4b1f013 commit fba60d1

36 files changed

+3269
-101
lines changed

OpenCVCarBoardDetect/.idea/workspace.xml

Lines changed: 388 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

OpenCVCarBoardDetect/CMakeLists.txt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,17 @@ project(OpenCVCarBoardDetect)
33

44
set(CMAKE_CXX_STANDARD 14)
55

6-
add_executable(OpenCVCarBoardDetect main.cpp)
6+
find_package(OpenCV REQUIRED)
7+
8+
include_directories("/usr/local/Cellar/opencv/4.1.0_2/include/opencv4")
9+
link_libraries("/usr/local/Cellar/opencv/4.1.0_2/lib")
10+
11+
12+
add_executable(OpenCVCarBoardDetect main.cpp PlateRecognize.cpp PlateRecognize.h common.h PlateLocate.cpp PlateLocate.h SobelLocate.cpp SobelLocate.h)
13+
14+
target_link_libraries(OpenCVCarBoardDetect
15+
opencv_videoio
16+
opencv_core
17+
opencv_highgui
18+
opencv_video
19+
opencv_objdetect)

OpenCVCarBoardDetect/PlateLocate.cpp

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
//
2+
// Created by lixiaoxu on 2019-10-15.
3+
//
4+
5+
#include "PlateLocate.h"
6+
7+
PlateLocate::PlateLocate() {
8+
9+
}
10+
11+
PlateLocate::~PlateLocate() {
12+
13+
}
14+
15+
/**
16+
* 尺寸校验(宽高比&面积)
17+
* @param rotatedRect
18+
* @return
19+
*/
20+
int PlateLocate::verifySizes(RotatedRect rotatedRect) {
21+
//容错率
22+
float error = 0.75f;
23+
//理想宽高比
24+
float aspect = float(136) / float(36);
25+
//真实宽高比
26+
float realAspect = float(rotatedRect.size.width) / float(rotatedRect.size.height);
27+
if (realAspect < 1)
28+
realAspect = float(rotatedRect.size.height) / float(rotatedRect.size.width);
29+
//真实面积
30+
float area = rotatedRect.size.height * rotatedRect.size.width;
31+
//最小最大面积 不符合的丢弃
32+
//给个大概就行,随时调整
33+
//初步筛选
34+
int areaMin = 44 * aspect * 14;
35+
int areaMax = 440 * aspect * 140;
36+
37+
//比例浮动 error认为也满足
38+
//最小宽高比
39+
float aspectMin = aspect - aspect * error;
40+
//最大宽高比
41+
float aspectMax = aspect + aspect * error;
42+
43+
if ((area < areaMin || area > areaMax) || (realAspect < aspectMin || realAspect > aspectMax)) {
44+
return 0;
45+
}
46+
return 1;
47+
}
48+
49+
/**
50+
* 矩形矫正
51+
* @param src
52+
* @param rects
53+
* @param dst_plates
54+
*/
55+
void PlateLocate::tortuosity(Mat src, vector<RotatedRect> &rects, vector<Mat> &dst_plates) {
56+
//循环要处理的矩形
57+
for (RotatedRect roi_rect:rects) {
58+
//矩形角度
59+
float roi_angle = roi_rect.angle;
60+
float r = (float) roi_rect.size.width / (float) roi_rect.size.height;
61+
if (r < 1) {
62+
roi_angle = 90 + roi_angle;
63+
}
64+
65+
//矩形大小
66+
Size roi_rect_size = roi_rect.size;
67+
68+
//让rect在一个安全范围内
69+
Rect2f safe_rect;
70+
safeRect(src, roi_rect, safe_rect);
71+
72+
//候选车牌
73+
//抠图 这里不是产生一张新图片 而是在src上定位到一个Mat,来处理
74+
//数据和src是同一份
75+
Mat src_rect = src(safe_rect);
76+
//真正的候选车牌
77+
Mat dst;
78+
//不需要旋转的 旋转角度小没必要旋转了
79+
if (roi_angle - 5 < 0 && roi_angle + 5 > 0) {
80+
dst = src_rect.clone();
81+
} else {
82+
//相对于roi的中心点,不见去左上角坐标是相对于整个图的
83+
//减去左上角则是相对于候选车牌的中心点 坐标
84+
Point2f roi_ref_center = roi_rect.center - safe_rect.tl();
85+
Mat rotated_mat;
86+
//矫正 rotated_mat 矫正后的图片
87+
rotation(src_rect, rotated_mat, roi_rect_size, roi_ref_center, roi_angle);
88+
dst = rotated_mat;
89+
}
90+
91+
//调整大小
92+
Mat plate_mat;
93+
//高和宽
94+
plate_mat.create(36, 136, CV_8UC3);
95+
resize(dst, plate_mat, plate_mat.size());
96+
97+
dst_plates.push_back(plate_mat);
98+
dst.release();
99+
}
100+
}
101+
102+
/**
103+
* 转换安全矩形
104+
* @param src
105+
* @param rect
106+
* @param safa_rect
107+
*/
108+
void PlateLocate::safeRect(Mat src, RotatedRect rect, Rect2f &safe_rect) {
109+
//RotatedRect 没有坐标
110+
//转为正常的带坐标的边框
111+
Rect2f boundRect = rect.boundingRect2f();
112+
113+
//左上角x,y
114+
float tl_x = boundRect.x > 0 ? boundRect.x : 0;
115+
float tl_y = boundRect.y > 0 ? boundRect.y : 0;
116+
117+
//这里是拿坐标x,y从0开始的所以 - 1
118+
//比如宽长度是10,x坐标最大是9,所以src.cols - 1
119+
//右下角
120+
float br_x = boundRect.x + boundRect.width < src.cols ? boundRect.x + boundRect.width - 1 : src.cols - 1;
121+
float br_y = boundRect.y + boundRect.height < src.rows ? boundRect.y + boundRect.height - 1 : src.rows - 1;
122+
123+
float w = br_x - tl_x;
124+
float h = br_y - tl_y;
125+
if (w <= 0 || h <= 0) return;
126+
safe_rect = Rect2f(tl_x, tl_y, w, h);
127+
}
128+
129+
130+
/**
131+
* 旋转
132+
* @param src
133+
* @param dst
134+
* @param rect_size
135+
* @param center
136+
* @param angle
137+
*/
138+
void PlateLocate::rotation(Mat src, Mat &dst, Size rect_size, Point2f center, double angle) {
139+
//获得旋转矩阵
140+
Mat rot_mat = getRotationMatrix2D(center, angle, 1);
141+
142+
//运用放射变换
143+
Mat mat_rotated;
144+
//矫正后 大小会不一样,但是对角线肯定能容纳
145+
int max = sqrt(pow(src.rows, 2) + pow(src.cols, 2));
146+
warpAffine(src, mat_rotated, rot_mat, Size(max, max), INTER_CUBIC);
147+
// imshow("before rotate",src);
148+
// imshow("after rotate",mat_rotated);
149+
//截取 尽量吧车牌多余的区域截取掉
150+
getRectSubPix(mat_rotated, Size(rect_size.width, rect_size.height), center, dst);
151+
// imshow("cut" ,dst);
152+
// waitKey();
153+
154+
mat_rotated.release();
155+
rot_mat.release();
156+
}
157+
158+
159+

OpenCVCarBoardDetect/PlateLocate.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// Created by lixiaoxu on 2019-10-15.
3+
//
4+
5+
#ifndef OPENCVCARBOARDDETECT_PLATELOCATE_H
6+
#define OPENCVCARBOARDDETECT_PLATELOCATE_H
7+
8+
9+
#include "common.h"
10+
11+
class PlateLocate {
12+
public:
13+
PlateLocate();
14+
15+
//作为父类加入虚构函数
16+
virtual ~PlateLocate();
17+
18+
/**
19+
* 车牌定位:1.原图,2.输出候选车牌
20+
* @param src
21+
* @param dst_plates
22+
* @return
23+
*/
24+
virtual void locate(Mat src, vector<Mat> &dst_plates) = 0;
25+
26+
protected:
27+
int verifySizes(RotatedRect rotatedRect);
28+
29+
void tortuosity(Mat src, vector<RotatedRect> &rect, vector<Mat> &dst_plates);
30+
31+
void safeRect(Mat src, RotatedRect rotatedRect, Rect2f& rect2f);
32+
33+
void rotation(Mat src, Mat &dst, Size rect_size, Point2f center, double angle);
34+
};
35+
36+
37+
#endif //OPENCVCARBOARDDETECT_PLATELOCATE_H
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// Created by lixiaoxu on 2019-10-15.
3+
//
4+
5+
#include "PlateRecognize.h"
6+
7+
8+
PlateRecognize::PlateRecognize() {
9+
sobelLocate = new SobelLocate();
10+
}
11+
12+
PlateRecognize::~PlateRecognize() {
13+
if (sobelLocate) {
14+
delete sobelLocate;
15+
sobelLocate = 0;
16+
}
17+
}
18+
19+
/**
20+
* 车牌识别:车牌定位+检测+字符识别
21+
* @param src
22+
* @return
23+
*/
24+
string PlateRecognize::recognize(Mat src) {
25+
//1.车牌定位
26+
//sobel定位
27+
vector<Mat> dst_plates;
28+
sobelLocate->locate(src, dst_plates);
29+
30+
return string("12345");
31+
}
32+
33+
34+

OpenCVCarBoardDetect/PlateRecognize.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// Created by lixiaoxu on 2019-10-15.
3+
//
4+
5+
6+
7+
#ifndef OPENCVCARBOARDDETECT_PLATERECOGNIZE_H
8+
#define OPENCVCARBOARDDETECT_PLATERECOGNIZE_H
9+
10+
#include <string>
11+
#include "common.h"
12+
#include "SobelLocate.h"
13+
14+
class PlateRecognize {
15+
public:
16+
PlateRecognize();
17+
18+
//没有加入虚函数,因为没有作为父类
19+
~PlateRecognize();
20+
21+
string recognize(Mat src);
22+
private:
23+
SobelLocate *sobelLocate = 0;
24+
};
25+
26+
27+
#endif //OPENCVCARBOARDDETECT_PLATERECOGNIZE_H

OpenCVCarBoardDetect/SobelLocate.cpp

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//
2+
// Created by lixiaoxu on 2019-10-15.
3+
//
4+
5+
#include "SobelLocate.h"
6+
7+
SobelLocate::SobelLocate() {
8+
9+
}
10+
11+
SobelLocate::~SobelLocate() {
12+
13+
}
14+
15+
/**
16+
* 车牌定位,1.原图,2.输出候选车牌
17+
* @param src
18+
* @param dst_plates
19+
*/
20+
void SobelLocate::locate(Mat src, vector<Mat> &dst_plates) {
21+
//1.高斯模糊
22+
Mat blur;
23+
//Ksize:both must be positive and odd,正数的奇数
24+
//半径越大越模糊
25+
GaussianBlur(src, blur, Size(5, 5), 0);
26+
imshow("origin", src);
27+
// imshow("Gauss", blur);
28+
//2.灰度化
29+
Mat gray;
30+
cvtColor(blur, gray, COLOR_BGR2GRAY);
31+
// imshow("gray", gray);
32+
33+
//3.sobel运算
34+
Mat sobel_16;
35+
//输入
36+
//Sobel函数求导后,导数可能得值会大于255,或小于0
37+
Sobel(gray, sobel_16, CV_16S, 1, 0);
38+
// imshow("sobel_16",sobel_16);//无法显示
39+
//转回8位
40+
Mat sobel;
41+
42+
convertScaleAbs(sobel_16, sobel);
43+
// imshow("sobel", sobel);//无法显示,需要进行转换
44+
45+
//4.二值化(非黑即白)
46+
Mat shold;
47+
threshold(sobel, shold, 0, 255, THRESH_OTSU + THRESH_BINARY);
48+
// imshow("shold", shold);
49+
50+
//5.形态学操作:闭操作
51+
Mat close;
52+
// Mat element = getStructuringElement(MORPH_RECT, Size(80, 30));//car4
53+
Mat element = getStructuringElement(MORPH_RECT, Size(19, 3));
54+
morphologyEx(shold, close, MORPH_CLOSE, element);
55+
imshow("close", close);
56+
57+
//6.找轮廓
58+
vector<vector<Point>> contours;
59+
findContours(close, contours,
60+
RETR_EXTERNAL,//外轮廓
61+
CHAIN_APPROX_NONE //轮廓上所有的像素点
62+
);
63+
RotatedRect rotatedRect;
64+
vector<RotatedRect> vec_sobel_rects;
65+
66+
//7.遍历判断矩形
67+
for (vector<Point> points :contours) {
68+
rotatedRect = minAreaRect(points);//带角度的矩形
69+
rectangle(src, rotatedRect.boundingRect(), Scalar(0, 0, 255));
70+
if (verifySizes(rotatedRect)) {
71+
vec_sobel_rects.push_back(rotatedRect);
72+
}
73+
}
74+
75+
for (RotatedRect rect :vec_sobel_rects) {
76+
rectangle(src, rect.boundingRect(), Scalar(0, 255, 0));
77+
}
78+
// imshow("轮廓图", src);
79+
//8.矩形矫正
80+
//角度判断,旋转,调整大小
81+
82+
tortuosity(src, vec_sobel_rects, dst_plates);
83+
for (Mat m :dst_plates) {
84+
imshow("定位候选车牌", m);
85+
waitKey();
86+
}
87+
88+
89+
blur.release();
90+
gray.release();
91+
sobel_16.release();
92+
sobel.release();
93+
shold.release();
94+
close.release();
95+
}

0 commit comments

Comments
 (0)