赞
踩
感觉学到了很多东西,便在这里作下笔记。
效果:
目录
2.1 第一个角度:消除较小的联通区域 vs 弥合较小的联通区域
作用:输入输出文件;
例子:
- fstream fin;
- fin.open(filename, ios::in);
- if (!fin.is_open())
- {
- cout << "can not open the file!" << endl;
- return false;
- }
-
- string s;
- while (std::getline(fin, s))
- {
- string str = s;
- data_name.push_back(str);
- }
- fin.close();
上面用到的open函数详细介绍:
- void open ( const char * filename,
- ios_base::openmode mode = ios_base::in | ios_base::out );
filename 操作文件名
mode 打开文件的方式,常用的有下面这两种
- ios::in: //文件以输入方式打开(文件数据输入到内存)
- ios::out: //文件以输出方式打开(内存数据输出到文件)
这两个我一直不太懂,今天正好称这机会学习下。
形态学开运算的作用有以下这些:
形态学闭运算的作用有以下这些:
开操作:消除背景噪音
闭操作:填充前景物体中的小洞,或者前景物体上的小黑点
函数的作用:对图像轮廓点进行多边形拟合
函数的的调用形式:
- void approxPolyDP( InputArray curve,
- OutputArray approxCurve,
- double epsilon,
- bool closed );
参数详解:
InputArray curve:一般是由图像的轮廓点组成的点集
OutputArray approxCurve:表示输出的多边形点集
double epsilon:主要表示输出的精度,就是另个轮廓点之间最大距离数,5,6,7,,8,,,,,
bool closed:表示输出的多边形是否封闭
这里有直观的动图展示:动图
这里用到冒泡排序对车牌字符的Rect进行排序:
- for (size_t i =0; i< Character_ROI.size(); i++)
- {
- for (size_t j=0; j< Character_ROI.size() -1 -i; j++)
- {
- if (Character_ROI[j].rect.x > Character_ROI[j+1].rect.x)
- {
- License temp = Character_ROI[j];
- Character_ROI[j] = Character_ROI[j+1];
- Character_ROI[j+1] = temp;
- }
- }
- }
假设有5个字符,它们的Rect的X坐标是 4 1 3 0 2, 现在用冒泡排序进行排序:
这里使用OpenCV absdiff函数计算两张图像的像素差,以此来判断图像的相似程度。
其他方法除了模板匹配,基于Hu矩轮廓匹配,基于篇幅原因就在另外博客再学习。
我用的是OpenCV4.5.5,Ubuntun20.04,直接引入头文件就好了。
字体文件路径(windows系统):
/Windows/Fonts/
然后复制到Ubuntu系统下某个目录就行了
示例:
- #include <iostream>
- #include <opencv2/freetype.hpp>
- #include <opencv2/opencv.hpp>
-
- using namespace std;
- using namespace cv;
-
-
- int main()
- {
- Mat src = imread("/home/jason/work/01-img/dog.png");
-
- string text = "中华田园犬";
-
- Ptr<cv::freetype::FreeType2> ft2;
- ft2 = cv::freetype::createFreeType2();
- ft2->loadFontData("/usr/share/fonts/winFonts/SIMYOU.TTF",0);
-
- ft2->putText(src, text, Point(300, 200), 30 , Scalar(0, 0,255), 2, 8, true);
-
- imshow("src", src);
- waitKey();
-
- return 0;
- }
向博主私信了,但是没有回复,那就自己做一个。
wps word导出的图片:
扣出字符来:
- #include <iostream>
- #include <opencv2/opencv.hpp>
-
- using namespace std;
- using namespace cv;
-
- void Get_character(Mat & src, Mat & result)
- {
- Mat gray;
- cvtColor(src, gray, COLOR_BGR2GRAY);
-
- // 黑色的点
- vector<Point> locations;
- for (int x=0; x< src.cols; x++)
- for (int y=0; y< src.rows; y++)
- {
-
- if(gray.at<uchar>(y, x) < 255)
- {
- locations.push_back(Point(x,y));
- }
- }
-
- // 字符左上角 右下角
- double xmin, ymin, xmax, ymax;
- vector<int> xs, ys;
- for(size_t i=0; i<locations.size(); i++)
- {
- xs.push_back(locations[i].x);
- ys.push_back(locations[i].y);
-
- }
- Mat tempX(xs);
- Mat tempY(ys);
- Point p1;
- minMaxLoc(tempX, &xmin, &xmax,0,0);
- minMaxLoc(tempY, &ymin, &ymax, 0,0);
-
-
- // 画框
- Rect roi;
- Mat temp = src.clone();
- roi.x = xmin - 30;
- roi.y = ymin - 30;
- roi.width = xmax - xmin + 60;
- roi.height = ymax - ymin + 60;
- rectangle(src,roi, Scalar(255, 0,0), 1, 8);
-
- // 扣出来
- Mat ROI = temp(roi);
- imshow("ROI", ROI);
- result = ROI.clone();
-
-
- imshow("src", src);
- waitKey(10);
- }
-
-
- int main()
- {
-
- string tail = ".png";
- string head;
- string path, outpath;
- string outpath_head = "/home/jason/work/01-img/car/car_roi/";
- for (int i=0; i<= 69; i++)
- {
- if (i<10)
- {
- head = "/home/jason/work/01-img/car/car/0";
- }
-
- else
- {
- head = "/home/jason/work/01-img/car/car/";
- }
- path = head + to_string(i) + tail;
- outpath = outpath_head + to_string(i) + tail;
-
- Mat src = imread(path);
- Mat result;
- Get_character(src, result);
- imwrite(outpath,result);
-
- }
-
- return 0;
- }
我在识别俄过程中,发现自己字母字体与车牌字体对应不上,就可能出现偏差。
怎么办?我干脆就把车牌字符扣下来保存为模板!
locate.hpp
- #include <iostream>
- #include <opencv2/opencv.hpp>
- #include<opencv2/freetype.hpp>
- #include <fstream>
- using namespace cv;
- using namespace std;
-
-
-
- using namespace cv;
- using namespace std;
-
- // 自定义车牌结构体
- struct License
- {
- Mat mat; // ROI图片
- Rect rect; // ROI所在矩形
- };
-
-
- class Locate
- {
- private:
- // 车牌字符模板图片
- vector<Mat> Dataset;
-
- // 车牌字符名
- vector<string> Data_name;
-
- // 字体文件路径
- string Font_Path;
-
- // 车牌字符扣出来另存路径
- string Character_Out_Path;
-
- bool Read_Data(string filename, vector<Mat>& dataset);
- bool Read_Data(string filename, vector<string>&data_name);
-
- void Image_Preprocessing(Mat& gray, Mat& result);
-
- void Morphological_Process(Mat& preprocess, Mat& result);
-
- void Character_ROI_Preprocessing(vector<License>& License_ROI);
-
- void Get_License_ROI(Mat &morpho, Mat &src,
- vector<License>& License_ROI);
- void Remove_vertial_Border(Mat& car_bord, Mat& result);
- void Remove_Horizon_Border(Mat& car_bord, Mat & result);
-
-
- public:
- bool Set_Input(string label_Path, string template_Path,
- string font_Path, string character_out_path);
-
- void Get_License_ROI(Mat& src, vector<License>& License_ROI);
-
- void Get_Character_ROI(vector<License>& License_ROI,
- vector<vector<License>>&Character_ROI,
- Mat &src, bool character_save);
-
- int pixCount(Mat image);
-
- void License_Recognition(vector<vector<License>>&Character_ROI,
- vector<vector<int>>&result_index);
-
- void Draw_Result(Mat &src,
- vector<License>& License_ROI,
- vector<vector<License>>&Character_ROI,
- vector<vector<int>>&result_index);
-
- };
locate.cpp
- #include "Locate_License.h"
-
-
- // 读取文件 图片
- bool Locate::Read_Data(string filename, vector<Mat>& dataset)
- {
- vector<String> imagePathList;
- glob(filename, imagePathList); // 遍历文件夹下所有文件
- if (imagePathList.empty()) return false;
-
- for (size_t i=0; i<imagePathList.size(); i++)
- {
- cout << imagePathList[i] << endl;
- Mat image = imread(imagePathList[i]);
- resize(image, image, Size(50, 100), 1, 1, INTER_LINEAR);
- cvtColor(image, image, COLOR_BGR2GRAY);
- threshold(image, image, 0, 255, THRESH_BINARY_INV|THRESH_OTSU); // 字符需要是白色
-
- Mat kernel = getStructuringElement(MORPH_RECT, Size(3,3));
- dilate(image, image,kernel,Point(-1,-1),1);
-
-
- // imshow(to_string(i), image);
- dataset.push_back(image);
-
-
- }
- this->Dataset = dataset;
- return true;
- }
-
- //读取文件 标签
- bool Locate::Read_Data(string filename, vector<string>&data_name)
- {
- fstream fin;
- fin.open(filename, ios::in);
- if(!fin.is_open())
- {
- cout << "can not open the file!" << endl;
- return false;
- }
-
- string s;
- while (getline(fin, s))
- {
- string str = s;
- data_name.push_back(str);
-
- }
- fin.close();
-
- this->Data_name = data_name;
- return true;
- }
-
- bool Locate::Set_Input(string label_Path,
- string template_Path,
- string font_Path="/usr/share/fonts/winFonts/SIMYOU.TTF",
- string character_out_path = "/home/jason/work/01-img/car/out")
- {
- this->Font_Path = font_Path;
- printf("字体路径设置为: %s, 请检查该目录是否正确\n",font_Path.c_str());
-
- this->Character_Out_Path = character_out_path;
- printf("车牌字符输出路径设置为: %s, 请检查该目录是否正确\n",character_out_path.c_str());
-
- if (Read_Data(label_Path, this->Data_name) &&
- Read_Data(template_Path, this->Dataset))
- {
- printf("***** 成功读取模板图片、标签数据\n");
- return true;
- }
- else
- {
- printf("***** err:读取模板图片、标签数据\n");
- return false;
- }
- }
-
- // 突出字符
- void Locate::Image_Preprocessing(Mat& gray, Mat& result)
- {
-
- // 开操作,平滑作用,断开较窄的狭颈和消除细的突出物
- Mat kernel = getStructuringElement(MORPH_RECT, Size(25,25));
- Mat gray_blur;
- morphologyEx(gray, gray_blur, MORPH_OPEN, kernel);
- imshow("open1", gray_blur);
-
- // 灰度图-开操作图,突显字符等部分
- Mat rst;
- subtract(gray, gray_blur, rst, Mat());
- imshow("rst", rst);
-
- // Canny算子进行边缘检测
- Mat canny_Image;
- Canny(rst, canny_Image, 400, 200, 3);
-
- imshow("canny_Image", canny_Image);
-
- result=canny_Image.clone();
- }
-
-
- // 通过膨胀连接相近的图像区域,
- // 利用腐蚀去除孤立细小的色块,从而将所有的车牌上所有的字符都连通起来
- void Locate::Morphological_Process(Mat& preprocess, Mat& result)
- {
- // 图片膨胀处理
- Mat dilate_image, erode_image;
-
- //自定义核:进行 x 方向的膨胀腐蚀
- Mat elementX = getStructuringElement(MORPH_RECT, Size(19, 1));
- Mat elementY = getStructuringElement(MORPH_RECT, Size(1, 19));
- Point point(-1, -1);
-
- dilate(preprocess, dilate_image, elementX, point, 2);
- imshow("dilate1", dilate_image);
-
-
- // // 闭操作,避免车牌与 其他区域联通在一起
- // Mat kernel = getStructuringElement(MORPH_RECT, Size(10, 10));
- // morphologyEx(dilate_image, dilate_image, MORPH_OPEN,
- // kernel, Point(-1,-1),2);
- // imshow("MORPH_OPEN", dilate_image);
-
- erode(dilate_image, erode_image, elementX, point, 3);
- imshow("erode1", erode_image);
-
- dilate(erode_image, dilate_image, elementX, point, 2);
- imshow("dialte2", dilate_image);
-
- //自定义核:进行 Y 方向的膨胀腐蚀
- erode(dilate_image, erode_image, elementY, point, 1);
- imshow("yerode", erode_image);
-
- dilate(erode_image, dilate_image, elementY, point, 2);
- imshow("Ydilate", erode_image);
-
- // 平滑处理
- Mat median_Image;
- medianBlur(dilate_image, median_Image, 15);
- imshow("median1",median_Image);
-
- medianBlur(median_Image, median_Image, 15);
- imshow("median2", median_Image);
- result = median_Image.clone();
- }
-
-
- // 扣出车牌
- void Locate::Get_License_ROI(Mat &morpho, Mat &src, vector<License>& License_ROI)
- {
- vector<vector<Point>> contours;
- findContours(morpho, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
-
- //
- Mat temp =src.clone();
- drawContours(temp, contours, -1, Scalar(255,0,0), 4);
-
- //
- double area;
- for (size_t i=0; i< contours.size(); i++)
- {
- // 轮廓 --》 rect
- Rect rect = boundingRect(contours[i]);
-
- // 车牌的宽高比大约为3.3
- double width_height = (double)rect.width/ (double)rect.height;
- printf("height_width:%.2f\n", width_height);
- if (width_height>2.5 && width_height < 4.0)
- {
- rectangle(temp, rect, Scalar(0,0, 255), 4, 8);
- License temp_license = {src(rect), rect};
- License_ROI.push_back(temp_license);
- }
-
- }
- imshow("标出车牌",temp);
-
- if (License_ROI.size() > 0)
- {
- printf("****** 共提取到 %d 块车牌\n",(int)License_ROI.size());
- for (size_t i = 0; i< License_ROI.size(); i++)
- {
- string tempName = "第" + to_string(i) + "块车牌";
- imshow(tempName, License_ROI[i].mat);
- }
- }
- else
- {
- printf("****** 没有发现车牌\n");
- }
-
-
-
- }
-
- // 从图片中扣出车牌
- void Locate::Get_License_ROI(Mat& src, vector<License>& License_ROI)
- {
-
- // 灰度图
- Mat gray;
- cvtColor(src, gray, COLOR_BGR2GRAY);
- imshow("gray", gray);
-
- // 均衡化
- equalizeHist(gray, gray);
-
-
- // 突出字符,并获得canny边缘
- Mat preprocess_result;
- Image_Preprocessing(gray, preprocess_result);
-
- // 将车牌字符形成一个整体
- Mat morpho_image;
- Morphological_Process(preprocess_result, morpho_image);
-
-
- // 扣出整块车牌
- Get_License_ROI(morpho_image, src, License_ROI);
-
- }
-
-
- void Locate::Remove_vertial_Border(Mat& car_bord, Mat & result)
- {
- Mat vline = getStructuringElement(MORPH_RECT, Size(1,car_bord.rows));
- Mat dst1, temp1;
-
- erode(car_bord, temp1, vline);
- // imshow("V-erode",temp1);
-
- dilate(temp1, dst1, vline);
- // imshow("V-dilate",dst1);
-
- subtract(car_bord, dst1, result, Mat());
- // imshow("V-result",result);
- }
-
-
-
-
- void Locate::Remove_Horizon_Border(Mat& car_bord, Mat & result)
- {
- Mat hline = getStructuringElement(MORPH_RECT, Size(car_bord.rows,1));
- Mat dst1, temp1;
-
- erode(car_bord, temp1, hline);
- // imshow("H-erode",temp1);
-
- dilate(temp1, dst1, hline);
- // imshow("H-dilate",dst1);
-
- subtract(car_bord, dst1, result, Mat());
- // imshow("H-result",result);
- }
-
-
- // 对整块车牌进行预处理,
- void Locate::Character_ROI_Preprocessing(vector<License>& License_ROI)
- {
- for (size_t i=0; i<License_ROI.size(); i++)
- {
- // 灰度化
- Mat gray;
- cvtColor(License_ROI[i].mat, gray, COLOR_BGR2GRAY);
- imshow("gray--", gray);
-
-
-
- // // 均衡化 这里不需要用,用了方而效果不好,因为车牌中车牌字符本身就很显眼,不需要用均衡
- // equalizeHist(gray, gray);
-
-
- // 大津阈值化
- Mat thresh;
- threshold(gray, thresh, 0, 255, THRESH_BINARY|THRESH_OTSU ); // 字是白色的的
- imshow("thres", thresh);
-
- Mat hori;
- Remove_Horizon_Border(thresh, hori);
-
- Mat vert;
- Remove_vertial_Border(hori,vert);
- imshow("H V", vert);
-
- Mat open;
- Mat kernel = getStructuringElement(MORPH_RECT, Size(2,2));
- morphologyEx(vert, open,MORPH_CLOSE, kernel, Point(-1,-1),1);
- imshow("连接汉字两边", open);
-
- License_ROI[i].mat = open.clone();
-
- }
-
- }
-
- //
- void Locate::Get_Character_ROI(vector<License>& License_ROI,
- vector<vector<License>>&Character_ROI,
- Mat &src,bool character_save=true)
- {
- Character_ROI_Preprocessing(License_ROI);
- Mat temp = src.clone();
-
- for (size_t j=0; j<License_ROI.size(); j++)
- {
- Mat temp_carbod = License_ROI[j].mat.clone();
- Character_ROI.push_back({}); // 必须先添加一个空项进去
-
-
- vector<vector<Point>> contours;
- findContours(License_ROI[j].mat, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
- drawContours(temp, contours, -1, Scalar(255,0,0), 2, 8);
- imshow("Get_Character_ROI", temp);
-
- for (size_t i = 0; i<contours.size(); i++)
- {
- double area = contourArea(contours[i]);
- //由于我们筛选出来的轮廓是无序的,故后续我们需要将字符重新排序
- if (area > 100)
- {
- Rect rect = boundingRect(contours[i]);
- // 计算外接矩形框高比
- double ratio = double(rect.height)/ double(rect.width);
- if (ratio > 1)
- {
- // 字符扣出来
- Mat roi = License_ROI[j].mat(rect);
- resize(roi, roi, Size(50, 100), 1, 1, INTER_LINEAR);
- Character_ROI[j].push_back({roi, rect}); // 前面不添加一个空项进去,这就就报错
-
- // 字符在原图画框
- rectangle(temp_carbod ,rect, Scalar(255, 0, 0), 2, 8);
- imshow("字符框",temp_carbod);
-
-
- // 字符另外为
- if (character_save)
- {
- threshold(roi,roi,0, 255, THRESH_BINARY_INV|THRESH_OTSU);
- string outpath = this->Character_Out_Path + "/" + to_string(i) + ".png";
- imwrite(outpath,roi);
-
- }
- }
- }
- }
-
- //将筛选出来的字符轮廓 按照其左上角点坐标从左到右依次顺序排列
- // 冒泡排序 ; 你查一下,用41302自己排下序就懂了
- for (size_t k =0; k<Character_ROI.size(); k++)
- {
- for (size_t ii =0; ii< Character_ROI[k].size(); ii++)
- {
- for (size_t jj=0; jj< Character_ROI[k].size() -1 -ii; jj++)
- {
- if (Character_ROI[k][jj].rect.x > Character_ROI[k][jj+1].rect.x)
- {
- License temp = Character_ROI[k][jj];
- Character_ROI[k][jj] = Character_ROI[k][jj+1];
- Character_ROI[k][jj+1] = temp;
- }
- }
- }
- }
-
- }
-
- if (Character_ROI.size() > 0)
- {
- for (size_t k =0; k<Character_ROI.size(); k++)
- {
- printf("******* 第 %d 块车牌共扣出: %d 个字符\n", (int)k,(int)Character_ROI[k].size());
- }
- }
- else
- {
- printf("***** err :第车牌没有扣出字符!\n");
-
- }
-
- }
-
-
-
- int Locate::pixCount(Mat image)
- {
- int count =0;
- if (image.channels() == 1)
- {
- for (int i=0; i<image.rows; i++)
- {
- for (int j=0; j<image.cols; j++)
- {
- if (image.at<uchar>(i, j) == 255) // 数的是白色像素
- {
- count++;
- }
- }
- }
- return count;
- }
- else
- {
- return -1;
- }
- }
-
- // 识别车牌字符
- // 使用OpenCV absdiff函数计算两张图像的像素差,以此来判断图像的相似程度
- // 进行字符匹配的方法还有:模板匹配,基于Hu矩轮廓匹配
- void Locate::License_Recognition(vector<vector<License>>&Character_ROI,
- vector<vector<int>>& result_inedx)
- {
-
- for (size_t k =0; k<Character_ROI.size(); k++)
- {
- result_inedx.push_back({});
-
- for (int i=0; i<Character_ROI[k].size(); i++)
- {
- // 车牌单个字符预处理
- Mat roi_thresh;
- threshold(Character_ROI[k][i].mat, roi_thresh, 0, 255, THRESH_BINARY_INV); // 车牌字符需是白色
- string car = "car" + to_string(i);
- imshow(car,roi_thresh);
-
-
- int minCount = 1000000000;
- int index = 0;
-
- for (int j=0; j < this->Dataset.size(); j++)
- {
-
- // 计算车牌字符与模板的像素差,以此判断两张图片是否相同
- Mat templa = this->Dataset[j];
- Mat dst;
- absdiff(roi_thresh, templa, dst);
-
-
- // 白字黑底,两图像素相减,白色像素越少,两图越接近
- int count = pixCount(dst);
- if (count< minCount)
- {
- minCount = count;
- index = j;
- }
- // imshow(to_string(j),dst);
- }
- string p = "templ" + to_string(i);
- imshow(p, this->Dataset[index]);
- result_inedx[k].push_back(index);
- }
- }
-
- printf("*****共对 %d 块车牌的字符完成字符匹配\n",(int)Character_ROI.size());
-
- }
-
-
- // 显示最终效果
- void Locate::Draw_Result(Mat &src, vector<License> &License_ROI,
- vector<vector<License>>&Character_ROI,
- vector<vector<int>>&result_index)
- {
- Ptr<cv::freetype::FreeType2> ft2;
- ft2 = cv::freetype::createFreeType2();
-
- ft2->loadFontData(this->Font_Path,0);
-
- for (size_t k=0; k<License_ROI.size(); k++)
- {
-
- // 原图上框出车牌
- rectangle(src, License_ROI[k].rect, Scalar(0, 255, 0), 2);
-
- // 在原图车牌框上方上打印车牌字符
- for (size_t i=0; i< Character_ROI[k].size(); i++)
- {
- // cout << data_name[result_index[i]] << " ";
- string str = this->Data_name[result_index[k][i]];
- ft2->putText(src, str,
- Point(License_ROI[k].rect.x + Character_ROI[k][i].rect.x,
- License_ROI[k].rect.y - Character_ROI[k][i].rect.y),
- 30,Scalar(255, 0, 0), 1, 8, true);
- }
- // cout << endl;
- }
-
- }
main.cpp
- #include "Locate_License.h"
-
-
-
- int main()
- {
-
- Mat src = imread("/home/jason/work/01-img/car.png");
- if (src.empty())
- {
- cout << "No image!" << endl;
- system("pause");
- return -1;
- }
-
- Locate locate;
- locate.Set_Input("/home/jason/work/01-img/car/car.txt",
- "/home/jason/work/01-img/car/template",
- "/usr/share/fonts/winFonts/SIMYOU.TTF",
- "/home/jason/work/01-img/car/out");
-
- vector<License> License_ROI;
- locate.Get_License_ROI(src, License_ROI);
-
- vector<vector<License>> Character_ROI;
- locate.Get_Character_ROI(License_ROI, Character_ROI,
- src, true);
-
- vector<vector<int>> result_index;
- locate.License_Recognition(Character_ROI,result_index);
-
- locate.Draw_Result(src, License_ROI,
- Character_ROI, result_index);
-
- imshow("车牌识别结果", src);
- waitKey();
-
-
-
-
-
-
- return 0;
- }
想要模板图片文件和标签文件可以在评论区留言或者私信我,上传在CSDN还得是VIP你们才能下载。
代码思路:
项目不足:
ps: 这个项目做了好几天,cpp文件干到了500行,原文才300行,增加近一半代码,短时间不想改了
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。