当前位置:   article > 正文

OpenCV实战5 车牌号识别_opencv 车牌号识别c#

opencv 车牌号识别c#

原文在这里,参考这个进行了改进

感觉学到了很多东西,便在这里作下笔记。

效果:

目录

一、知识点学习:

1. fstream

2. 形态学开操作与形态闭操作

2.1 第一个角度:消除较小的联通区域 vs 弥合较小的联通区域

2.2 第二个角度:消除背景噪音 vs 消除前景噪音

3、approPolyDp函数

4、冒泡排序

5、匹配目标

6、putText函数打印中文

 7、文字文件、标签文件

7.1 文字文件

7.2 标签文件

二、车牌识别代码

三、项目总结


一、知识点学习:

1. fstream

作用:输入输出文件;

例子:

  1. fstream fin;
  2. fin.open(filename, ios::in);
  3. if (!fin.is_open())
  4. {
  5. cout << "can not open the file!" << endl;
  6. return false;
  7. }
  8. string s;
  9. while (std::getline(fin, s))
  10. {
  11. string str = s;
  12. data_name.push_back(str);
  13. }
  14. fin.close();

上面用到的open函数详细介绍:

  1. void open ( const char * filename,
  2. ios_base::openmode mode = ios_base::in | ios_base::out );

filename 操作文件名

mode 打开文件的方式,常用的有下面这两种

  1. ios::in:    //文件以输入方式打开(文件数据输入到内存)
  2. ios::out:    //文件以输出方式打开(内存数据输出到文件)

2. 形态学开操作与形态闭操作

这两个我一直不太懂,今天正好称这机会学习下。

2.1 第一个角度:消除较小的联通区域 vs 弥合较小的联通区域

形态学开运算的作用有以下这些:

  • 消除值高于邻近点的孤立点,达到去除图像中噪声的作用;
  • 消除较小的连通域,保留较大的连通域;
  • 断开较窄的狭颈,可以在两个物体纤细的连接处将它们分离
  • 不明显改变较大连通域的面积的情况下平滑连通域的连界、轮廓;

形态学闭运算的作用有以下这些:

  • 消除值低于邻近点的孤立点,达到去除图像中噪声的作用;
  • 连接两个邻近的连通域;
  • 弥合较窄的间断和细长的沟壑
  • 去除连通域内的小型空洞
  • 和开运算一样也能够平滑物体的轮廓;

2.2 第二个角度:消除背景噪音 vs 消除前景噪音

开操作:消除背景噪音

 闭操作:填充前景物体中的小洞,或者前景物体上的小黑点

3、approPolyDp函数

函数的作用:对图像轮廓点进行多边形拟合

函数的的调用形式:

  1. void approxPolyDP( InputArray curve,
  2. OutputArray approxCurve,
  3. double epsilon,
  4. bool closed );

 参数详解:

InputArray curve:一般是由图像的轮廓点组成的点集

OutputArray approxCurve:表示输出的多边形点集

 double epsilon:主要表示输出的精度,就是另个轮廓点之间最大距离数,5,6,7,,8,,,,,

bool closed:表示输出的多边形是否封闭

4、冒泡排序

这里有直观的动图展示:动图

这里用到冒泡排序对车牌字符的Rect进行排序:

  1. for (size_t i =0; i< Character_ROI.size(); i++)
  2. {
  3. for (size_t j=0; j< Character_ROI.size() -1 -i; j++)
  4. {
  5. if (Character_ROI[j].rect.x > Character_ROI[j+1].rect.x)
  6. {
  7. License temp = Character_ROI[j];
  8. Character_ROI[j] = Character_ROI[j+1];
  9. Character_ROI[j+1] = temp;
  10. }
  11. }
  12. }

 假设有5个字符,它们的Rect的X坐标是  4 1 3 0 2, 现在用冒泡排序进行排序:

5、匹配目标

这里使用OpenCV absdiff函数计算两张图像的像素差,以此来判断图像的相似程度。

其他方法除了模板匹配,基于Hu矩轮廓匹配,基于篇幅原因就在另外博客再学习。

6、putText函数打印中文

我用的是OpenCV4.5.5,Ubuntun20.04,直接引入头文件就好了。

字体文件路径(windows系统):

/Windows/Fonts/

然后复制到Ubuntu系统下某个目录就行了

示例:

  1. #include <iostream>
  2. #include <opencv2/freetype.hpp>
  3. #include <opencv2/opencv.hpp>
  4. using namespace std;
  5. using namespace cv;
  6. int main()
  7. {
  8. Mat src = imread("/home/jason/work/01-img/dog.png");
  9. string text = "中华田园犬";
  10. Ptr<cv::freetype::FreeType2> ft2;
  11. ft2 = cv::freetype::createFreeType2();
  12. ft2->loadFontData("/usr/share/fonts/winFonts/SIMYOU.TTF",0);
  13. ft2->putText(src, text, Point(300, 200), 30 , Scalar(0, 0,255), 2, 8, true);
  14. imshow("src", src);
  15. waitKey();
  16. return 0;
  17. }

 7、文字文件、标签文件

向博主私信了,但是没有回复,那就自己做一个。

7.1 文字文件

wps word导出的图片:

扣出字符来:

  1. #include <iostream>
  2. #include <opencv2/opencv.hpp>
  3. using namespace std;
  4. using namespace cv;
  5. void Get_character(Mat & src, Mat & result)
  6. {
  7. Mat gray;
  8. cvtColor(src, gray, COLOR_BGR2GRAY);
  9. // 黑色的点
  10. vector<Point> locations;
  11. for (int x=0; x< src.cols; x++)
  12. for (int y=0; y< src.rows; y++)
  13. {
  14. if(gray.at<uchar>(y, x) < 255)
  15. {
  16. locations.push_back(Point(x,y));
  17. }
  18. }
  19. // 字符左上角 右下角
  20. double xmin, ymin, xmax, ymax;
  21. vector<int> xs, ys;
  22. for(size_t i=0; i<locations.size(); i++)
  23. {
  24. xs.push_back(locations[i].x);
  25. ys.push_back(locations[i].y);
  26. }
  27. Mat tempX(xs);
  28. Mat tempY(ys);
  29. Point p1;
  30. minMaxLoc(tempX, &xmin, &xmax,0,0);
  31. minMaxLoc(tempY, &ymin, &ymax, 0,0);
  32. // 画框
  33. Rect roi;
  34. Mat temp = src.clone();
  35. roi.x = xmin - 30;
  36. roi.y = ymin - 30;
  37. roi.width = xmax - xmin + 60;
  38. roi.height = ymax - ymin + 60;
  39. rectangle(src,roi, Scalar(255, 0,0), 1, 8);
  40. // 扣出来
  41. Mat ROI = temp(roi);
  42. imshow("ROI", ROI);
  43. result = ROI.clone();
  44. imshow("src", src);
  45. waitKey(10);
  46. }
  47. int main()
  48. {
  49. string tail = ".png";
  50. string head;
  51. string path, outpath;
  52. string outpath_head = "/home/jason/work/01-img/car/car_roi/";
  53. for (int i=0; i<= 69; i++)
  54. {
  55. if (i<10)
  56. {
  57. head = "/home/jason/work/01-img/car/car/0";
  58. }
  59. else
  60. {
  61. head = "/home/jason/work/01-img/car/car/";
  62. }
  63. path = head + to_string(i) + tail;
  64. outpath = outpath_head + to_string(i) + tail;
  65. Mat src = imread(path);
  66. Mat result;
  67. Get_character(src, result);
  68. imwrite(outpath,result);
  69. }
  70. return 0;
  71. }

7.2 标签文件

二、车牌识别代码

我在识别俄过程中,发现自己字母字体与车牌字体对应不上,就可能出现偏差。

怎么办?我干脆就把车牌字符扣下来保存为模板!

locate.hpp

  1. #include <iostream>
  2. #include <opencv2/opencv.hpp>
  3. #include<opencv2/freetype.hpp>
  4. #include <fstream>
  5. using namespace cv;
  6. using namespace std;
  7. using namespace cv;
  8. using namespace std;
  9. // 自定义车牌结构体
  10. struct License
  11. {
  12. Mat mat; // ROI图片
  13. Rect rect; // ROI所在矩形
  14. };
  15. class Locate
  16. {
  17. private:
  18. // 车牌字符模板图片
  19. vector<Mat> Dataset;
  20. // 车牌字符名
  21. vector<string> Data_name;
  22. // 字体文件路径
  23. string Font_Path;
  24. // 车牌字符扣出来另存路径
  25. string Character_Out_Path;
  26. bool Read_Data(string filename, vector<Mat>& dataset);
  27. bool Read_Data(string filename, vector<string>&data_name);
  28. void Image_Preprocessing(Mat& gray, Mat& result);
  29. void Morphological_Process(Mat& preprocess, Mat& result);
  30. void Character_ROI_Preprocessing(vector<License>& License_ROI);
  31. void Get_License_ROI(Mat &morpho, Mat &src,
  32. vector<License>& License_ROI);
  33. void Remove_vertial_Border(Mat& car_bord, Mat& result);
  34. void Remove_Horizon_Border(Mat& car_bord, Mat & result);
  35. public:
  36. bool Set_Input(string label_Path, string template_Path,
  37. string font_Path, string character_out_path);
  38. void Get_License_ROI(Mat& src, vector<License>& License_ROI);
  39. void Get_Character_ROI(vector<License>& License_ROI,
  40. vector<vector<License>>&Character_ROI,
  41. Mat &src, bool character_save);
  42. int pixCount(Mat image);
  43. void License_Recognition(vector<vector<License>>&Character_ROI,
  44. vector<vector<int>>&result_index);
  45. void Draw_Result(Mat &src,
  46. vector<License>& License_ROI,
  47. vector<vector<License>>&Character_ROI,
  48. vector<vector<int>>&result_index);
  49. };

locate.cpp

  1. #include "Locate_License.h"
  2. // 读取文件 图片
  3. bool Locate::Read_Data(string filename, vector<Mat>& dataset)
  4. {
  5. vector<String> imagePathList;
  6. glob(filename, imagePathList); // 遍历文件夹下所有文件
  7. if (imagePathList.empty()) return false;
  8. for (size_t i=0; i<imagePathList.size(); i++)
  9. {
  10. cout << imagePathList[i] << endl;
  11. Mat image = imread(imagePathList[i]);
  12. resize(image, image, Size(50, 100), 1, 1, INTER_LINEAR);
  13. cvtColor(image, image, COLOR_BGR2GRAY);
  14. threshold(image, image, 0, 255, THRESH_BINARY_INV|THRESH_OTSU); // 字符需要是白色
  15. Mat kernel = getStructuringElement(MORPH_RECT, Size(3,3));
  16. dilate(image, image,kernel,Point(-1,-1),1);
  17. // imshow(to_string(i), image);
  18. dataset.push_back(image);
  19. }
  20. this->Dataset = dataset;
  21. return true;
  22. }
  23. //读取文件 标签
  24. bool Locate::Read_Data(string filename, vector<string>&data_name)
  25. {
  26. fstream fin;
  27. fin.open(filename, ios::in);
  28. if(!fin.is_open())
  29. {
  30. cout << "can not open the file!" << endl;
  31. return false;
  32. }
  33. string s;
  34. while (getline(fin, s))
  35. {
  36. string str = s;
  37. data_name.push_back(str);
  38. }
  39. fin.close();
  40. this->Data_name = data_name;
  41. return true;
  42. }
  43. bool Locate::Set_Input(string label_Path,
  44. string template_Path,
  45. string font_Path="/usr/share/fonts/winFonts/SIMYOU.TTF",
  46. string character_out_path = "/home/jason/work/01-img/car/out")
  47. {
  48. this->Font_Path = font_Path;
  49. printf("字体路径设置为: %s, 请检查该目录是否正确\n",font_Path.c_str());
  50. this->Character_Out_Path = character_out_path;
  51. printf("车牌字符输出路径设置为: %s, 请检查该目录是否正确\n",character_out_path.c_str());
  52. if (Read_Data(label_Path, this->Data_name) &&
  53. Read_Data(template_Path, this->Dataset))
  54. {
  55. printf("***** 成功读取模板图片、标签数据\n");
  56. return true;
  57. }
  58. else
  59. {
  60. printf("***** err:读取模板图片、标签数据\n");
  61. return false;
  62. }
  63. }
  64. // 突出字符
  65. void Locate::Image_Preprocessing(Mat& gray, Mat& result)
  66. {
  67. // 开操作,平滑作用,断开较窄的狭颈和消除细的突出物
  68. Mat kernel = getStructuringElement(MORPH_RECT, Size(25,25));
  69. Mat gray_blur;
  70. morphologyEx(gray, gray_blur, MORPH_OPEN, kernel);
  71. imshow("open1", gray_blur);
  72. // 灰度图-开操作图,突显字符等部分
  73. Mat rst;
  74. subtract(gray, gray_blur, rst, Mat());
  75. imshow("rst", rst);
  76. // Canny算子进行边缘检测
  77. Mat canny_Image;
  78. Canny(rst, canny_Image, 400, 200, 3);
  79. imshow("canny_Image", canny_Image);
  80. result=canny_Image.clone();
  81. }
  82. // 通过膨胀连接相近的图像区域,
  83. // 利用腐蚀去除孤立细小的色块,从而将所有的车牌上所有的字符都连通起来
  84. void Locate::Morphological_Process(Mat& preprocess, Mat& result)
  85. {
  86. // 图片膨胀处理
  87. Mat dilate_image, erode_image;
  88. //自定义核:进行 x 方向的膨胀腐蚀
  89. Mat elementX = getStructuringElement(MORPH_RECT, Size(19, 1));
  90. Mat elementY = getStructuringElement(MORPH_RECT, Size(1, 19));
  91. Point point(-1, -1);
  92. dilate(preprocess, dilate_image, elementX, point, 2);
  93. imshow("dilate1", dilate_image);
  94. // // 闭操作,避免车牌与 其他区域联通在一起
  95. // Mat kernel = getStructuringElement(MORPH_RECT, Size(10, 10));
  96. // morphologyEx(dilate_image, dilate_image, MORPH_OPEN,
  97. // kernel, Point(-1,-1),2);
  98. // imshow("MORPH_OPEN", dilate_image);
  99. erode(dilate_image, erode_image, elementX, point, 3);
  100. imshow("erode1", erode_image);
  101. dilate(erode_image, dilate_image, elementX, point, 2);
  102. imshow("dialte2", dilate_image);
  103. //自定义核:进行 Y 方向的膨胀腐蚀
  104. erode(dilate_image, erode_image, elementY, point, 1);
  105. imshow("yerode", erode_image);
  106. dilate(erode_image, dilate_image, elementY, point, 2);
  107. imshow("Ydilate", erode_image);
  108. // 平滑处理
  109. Mat median_Image;
  110. medianBlur(dilate_image, median_Image, 15);
  111. imshow("median1",median_Image);
  112. medianBlur(median_Image, median_Image, 15);
  113. imshow("median2", median_Image);
  114. result = median_Image.clone();
  115. }
  116. // 扣出车牌
  117. void Locate::Get_License_ROI(Mat &morpho, Mat &src, vector<License>& License_ROI)
  118. {
  119. vector<vector<Point>> contours;
  120. findContours(morpho, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
  121. //
  122. Mat temp =src.clone();
  123. drawContours(temp, contours, -1, Scalar(255,0,0), 4);
  124. //
  125. double area;
  126. for (size_t i=0; i< contours.size(); i++)
  127. {
  128. // 轮廓 --》 rect
  129. Rect rect = boundingRect(contours[i]);
  130. // 车牌的宽高比大约为3.3
  131. double width_height = (double)rect.width/ (double)rect.height;
  132. printf("height_width:%.2f\n", width_height);
  133. if (width_height>2.5 && width_height < 4.0)
  134. {
  135. rectangle(temp, rect, Scalar(0,0, 255), 4, 8);
  136. License temp_license = {src(rect), rect};
  137. License_ROI.push_back(temp_license);
  138. }
  139. }
  140. imshow("标出车牌",temp);
  141. if (License_ROI.size() > 0)
  142. {
  143. printf("****** 共提取到 %d 块车牌\n",(int)License_ROI.size());
  144. for (size_t i = 0; i< License_ROI.size(); i++)
  145. {
  146. string tempName = "第" + to_string(i) + "块车牌";
  147. imshow(tempName, License_ROI[i].mat);
  148. }
  149. }
  150. else
  151. {
  152. printf("****** 没有发现车牌\n");
  153. }
  154. }
  155. // 从图片中扣出车牌
  156. void Locate::Get_License_ROI(Mat& src, vector<License>& License_ROI)
  157. {
  158. // 灰度图
  159. Mat gray;
  160. cvtColor(src, gray, COLOR_BGR2GRAY);
  161. imshow("gray", gray);
  162. // 均衡化
  163. equalizeHist(gray, gray);
  164. // 突出字符,并获得canny边缘
  165. Mat preprocess_result;
  166. Image_Preprocessing(gray, preprocess_result);
  167. // 将车牌字符形成一个整体
  168. Mat morpho_image;
  169. Morphological_Process(preprocess_result, morpho_image);
  170. // 扣出整块车牌
  171. Get_License_ROI(morpho_image, src, License_ROI);
  172. }
  173. void Locate::Remove_vertial_Border(Mat& car_bord, Mat & result)
  174. {
  175. Mat vline = getStructuringElement(MORPH_RECT, Size(1,car_bord.rows));
  176. Mat dst1, temp1;
  177. erode(car_bord, temp1, vline);
  178. // imshow("V-erode",temp1);
  179. dilate(temp1, dst1, vline);
  180. // imshow("V-dilate",dst1);
  181. subtract(car_bord, dst1, result, Mat());
  182. // imshow("V-result",result);
  183. }
  184. void Locate::Remove_Horizon_Border(Mat& car_bord, Mat & result)
  185. {
  186. Mat hline = getStructuringElement(MORPH_RECT, Size(car_bord.rows,1));
  187. Mat dst1, temp1;
  188. erode(car_bord, temp1, hline);
  189. // imshow("H-erode",temp1);
  190. dilate(temp1, dst1, hline);
  191. // imshow("H-dilate",dst1);
  192. subtract(car_bord, dst1, result, Mat());
  193. // imshow("H-result",result);
  194. }
  195. // 对整块车牌进行预处理,
  196. void Locate::Character_ROI_Preprocessing(vector<License>& License_ROI)
  197. {
  198. for (size_t i=0; i<License_ROI.size(); i++)
  199. {
  200. // 灰度化
  201. Mat gray;
  202. cvtColor(License_ROI[i].mat, gray, COLOR_BGR2GRAY);
  203. imshow("gray--", gray);
  204. // // 均衡化 这里不需要用,用了方而效果不好,因为车牌中车牌字符本身就很显眼,不需要用均衡
  205. // equalizeHist(gray, gray);
  206. // 大津阈值化
  207. Mat thresh;
  208. threshold(gray, thresh, 0, 255, THRESH_BINARY|THRESH_OTSU ); // 字是白色的的
  209. imshow("thres", thresh);
  210. Mat hori;
  211. Remove_Horizon_Border(thresh, hori);
  212. Mat vert;
  213. Remove_vertial_Border(hori,vert);
  214. imshow("H V", vert);
  215. Mat open;
  216. Mat kernel = getStructuringElement(MORPH_RECT, Size(2,2));
  217. morphologyEx(vert, open,MORPH_CLOSE, kernel, Point(-1,-1),1);
  218. imshow("连接汉字两边", open);
  219. License_ROI[i].mat = open.clone();
  220. }
  221. }
  222. //
  223. void Locate::Get_Character_ROI(vector<License>& License_ROI,
  224. vector<vector<License>>&Character_ROI,
  225. Mat &src,bool character_save=true)
  226. {
  227. Character_ROI_Preprocessing(License_ROI);
  228. Mat temp = src.clone();
  229. for (size_t j=0; j<License_ROI.size(); j++)
  230. {
  231. Mat temp_carbod = License_ROI[j].mat.clone();
  232. Character_ROI.push_back({}); // 必须先添加一个空项进去
  233. vector<vector<Point>> contours;
  234. findContours(License_ROI[j].mat, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
  235. drawContours(temp, contours, -1, Scalar(255,0,0), 2, 8);
  236. imshow("Get_Character_ROI", temp);
  237. for (size_t i = 0; i<contours.size(); i++)
  238. {
  239. double area = contourArea(contours[i]);
  240. //由于我们筛选出来的轮廓是无序的,故后续我们需要将字符重新排序
  241. if (area > 100)
  242. {
  243. Rect rect = boundingRect(contours[i]);
  244. // 计算外接矩形框高比
  245. double ratio = double(rect.height)/ double(rect.width);
  246. if (ratio > 1)
  247. {
  248. // 字符扣出来
  249. Mat roi = License_ROI[j].mat(rect);
  250. resize(roi, roi, Size(50, 100), 1, 1, INTER_LINEAR);
  251. Character_ROI[j].push_back({roi, rect}); // 前面不添加一个空项进去,这就就报错
  252. // 字符在原图画框
  253. rectangle(temp_carbod ,rect, Scalar(255, 0, 0), 2, 8);
  254. imshow("字符框",temp_carbod);
  255. // 字符另外为
  256. if (character_save)
  257. {
  258. threshold(roi,roi,0, 255, THRESH_BINARY_INV|THRESH_OTSU);
  259. string outpath = this->Character_Out_Path + "/" + to_string(i) + ".png";
  260. imwrite(outpath,roi);
  261. }
  262. }
  263. }
  264. }
  265. //将筛选出来的字符轮廓 按照其左上角点坐标从左到右依次顺序排列
  266. // 冒泡排序 ; 你查一下,用41302自己排下序就懂了
  267. for (size_t k =0; k<Character_ROI.size(); k++)
  268. {
  269. for (size_t ii =0; ii< Character_ROI[k].size(); ii++)
  270. {
  271. for (size_t jj=0; jj< Character_ROI[k].size() -1 -ii; jj++)
  272. {
  273. if (Character_ROI[k][jj].rect.x > Character_ROI[k][jj+1].rect.x)
  274. {
  275. License temp = Character_ROI[k][jj];
  276. Character_ROI[k][jj] = Character_ROI[k][jj+1];
  277. Character_ROI[k][jj+1] = temp;
  278. }
  279. }
  280. }
  281. }
  282. }
  283. if (Character_ROI.size() > 0)
  284. {
  285. for (size_t k =0; k<Character_ROI.size(); k++)
  286. {
  287. printf("******* 第 %d 块车牌共扣出: %d 个字符\n", (int)k,(int)Character_ROI[k].size());
  288. }
  289. }
  290. else
  291. {
  292. printf("***** err :第车牌没有扣出字符!\n");
  293. }
  294. }
  295. int Locate::pixCount(Mat image)
  296. {
  297. int count =0;
  298. if (image.channels() == 1)
  299. {
  300. for (int i=0; i<image.rows; i++)
  301. {
  302. for (int j=0; j<image.cols; j++)
  303. {
  304. if (image.at<uchar>(i, j) == 255) // 数的是白色像素
  305. {
  306. count++;
  307. }
  308. }
  309. }
  310. return count;
  311. }
  312. else
  313. {
  314. return -1;
  315. }
  316. }
  317. // 识别车牌字符
  318. // 使用OpenCV absdiff函数计算两张图像的像素差,以此来判断图像的相似程度
  319. // 进行字符匹配的方法还有:模板匹配,基于Hu矩轮廓匹配
  320. void Locate::License_Recognition(vector<vector<License>>&Character_ROI,
  321. vector<vector<int>>& result_inedx)
  322. {
  323. for (size_t k =0; k<Character_ROI.size(); k++)
  324. {
  325. result_inedx.push_back({});
  326. for (int i=0; i<Character_ROI[k].size(); i++)
  327. {
  328. // 车牌单个字符预处理
  329. Mat roi_thresh;
  330. threshold(Character_ROI[k][i].mat, roi_thresh, 0, 255, THRESH_BINARY_INV); // 车牌字符需是白色
  331. string car = "car" + to_string(i);
  332. imshow(car,roi_thresh);
  333. int minCount = 1000000000;
  334. int index = 0;
  335. for (int j=0; j < this->Dataset.size(); j++)
  336. {
  337. // 计算车牌字符与模板的像素差,以此判断两张图片是否相同
  338. Mat templa = this->Dataset[j];
  339. Mat dst;
  340. absdiff(roi_thresh, templa, dst);
  341. // 白字黑底,两图像素相减,白色像素越少,两图越接近
  342. int count = pixCount(dst);
  343. if (count< minCount)
  344. {
  345. minCount = count;
  346. index = j;
  347. }
  348. // imshow(to_string(j),dst);
  349. }
  350. string p = "templ" + to_string(i);
  351. imshow(p, this->Dataset[index]);
  352. result_inedx[k].push_back(index);
  353. }
  354. }
  355. printf("*****共对 %d 块车牌的字符完成字符匹配\n",(int)Character_ROI.size());
  356. }
  357. // 显示最终效果
  358. void Locate::Draw_Result(Mat &src, vector<License> &License_ROI,
  359. vector<vector<License>>&Character_ROI,
  360. vector<vector<int>>&result_index)
  361. {
  362. Ptr<cv::freetype::FreeType2> ft2;
  363. ft2 = cv::freetype::createFreeType2();
  364. ft2->loadFontData(this->Font_Path,0);
  365. for (size_t k=0; k<License_ROI.size(); k++)
  366. {
  367. // 原图上框出车牌
  368. rectangle(src, License_ROI[k].rect, Scalar(0, 255, 0), 2);
  369. // 在原图车牌框上方上打印车牌字符
  370. for (size_t i=0; i< Character_ROI[k].size(); i++)
  371. {
  372. // cout << data_name[result_index[i]] << " ";
  373. string str = this->Data_name[result_index[k][i]];
  374. ft2->putText(src, str,
  375. Point(License_ROI[k].rect.x + Character_ROI[k][i].rect.x,
  376. License_ROI[k].rect.y - Character_ROI[k][i].rect.y),
  377. 30,Scalar(255, 0, 0), 1, 8, true);
  378. }
  379. // cout << endl;
  380. }
  381. }

main.cpp

  1. #include "Locate_License.h"
  2. int main()
  3. {
  4. Mat src = imread("/home/jason/work/01-img/car.png");
  5. if (src.empty())
  6. {
  7. cout << "No image!" << endl;
  8. system("pause");
  9. return -1;
  10. }
  11. Locate locate;
  12. locate.Set_Input("/home/jason/work/01-img/car/car.txt",
  13. "/home/jason/work/01-img/car/template",
  14. "/usr/share/fonts/winFonts/SIMYOU.TTF",
  15. "/home/jason/work/01-img/car/out");
  16. vector<License> License_ROI;
  17. locate.Get_License_ROI(src, License_ROI);
  18. vector<vector<License>> Character_ROI;
  19. locate.Get_Character_ROI(License_ROI, Character_ROI,
  20. src, true);
  21. vector<vector<int>> result_index;
  22. locate.License_Recognition(Character_ROI,result_index);
  23. locate.Draw_Result(src, License_ROI,
  24. Character_ROI, result_index);
  25. imshow("车牌识别结果", src);
  26. waitKey();
  27. return 0;
  28. }

想要模板图片文件和标签文件可以在评论区留言或者私信我,上传在CSDN还得是VIP你们才能下载。

三、项目总结

代码思路:

  1. 获取整个车牌  (这里部分涉及预处理很有意思)
  2. 对车牌进行切割,获得7个字符
  3. 将获得的车牌字符与模板匹配

项目不足:

  1. 本项目仅仅对车牌字符为白色的车牌有用
  2. 未对车牌作旋转矫正【p1, p2】,透视矫正,这两个因素影响很大,后面有空再补上

ps: 这个项目做了好几天,cpp文件干到了500行,原文才300行,增加近一半代码,短时间不想改了

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/611902
推荐阅读
相关标签
  

闽ICP备14008679号