学习目标:

  • 目标识别

学习内容:

车牌OCR


学习时间:

  • 2022年7月26日到2022年7月27日

学习产出:

因为之前用FPGA和基于python的cv做过车牌识别(大创),感觉战队C++用的多,这次用C++实现。

之前做的代码我已经开源、上传到我的仓库了,欢迎大家交流,不过没有完善好,会逐步推进的。

py链接,FPGA的还需要完善,目前对0和8的识别不是太行。

下面是战队的任务,就不过多讲解了。

头文件:Global.h

#ifdef GLOBALextern int flag_1;
extern bool flag;
extern bool specialFlag;
extern int captureRead
extern string carPlate;
extern char test[10];extern struct stu1
{char number;Mat image;double matchDegree;
};
extern struct  stu
{Mat image;double matchDegree;
};#endif

CPP文件也是唯一的一个:

#include <opencv2/opencv.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
#include"Global.h"
#include <windows.h>
#include <string>using namespace std;
using namespace cv;void fillHole(const Mat srcBw, Mat &dstBw);           //填补算法
Mat cutOne(Mat cutImage);         //边框切割算法
void CharCut(Mat srcImage);            //单个字符切割算法
Mat Location(Mat srcImage);            //图像识别算法
void SingleCharCut(Mat doubleImage, int k1, int k2);
void ShowChar();
void MatchProvince();
void MatchNumber();
void readProvince();
void readNumber();
void VideoShow(Mat videoImage);
void GetStringSize(HDC hDC, const char* str, int* w, int* h);
void putTextZH(Mat &dst, const char* str, Point org, Scalar color, int fontSize, const char* fn, bool italic, bool underline);int flag_1;          //判断是否倾斜,需不需要二次定位车牌
bool flag;       //判断提取是否成功
bool specialFlag = false;    //针对嵌套车牌
int captureRead = 0;
int videoFlag = 0;
string carPlateProvince = " ";
string carPlate = " ";
char test[10];
vector<Mat>  singleChar;         //字符图片容器int main(int argc, char *argv[])
{//计时开始double time0 = static_cast<double>(getTickCount());//视频操作VideoCapture capture("1.mp4");Mat srcImage;Mat theFirst;int singleCharLength;//读取字符图片readProvince();readNumber();while (1) {capture >> srcImage;try {if (!srcImage.data) { printf("视频识别结束    \n"); return 0; }if (srcImage.rows >= srcImage.cols){resize(srcImage, srcImage, Size(640, 640 * srcImage.rows / srcImage.cols));}else{resize(srcImage, srcImage, Size(400 * srcImage.cols / srcImage.rows, 400));}//车牌定位theFirst = Location(srcImage);if (flag){if (flag_1 == 1)                      //旋转后要再次定位去上下杂边{theFirst = Location(theFirst);flag_1 = 0;}}if (flag){//车牌切割(切割上下边,去除干扰)theFirst = cutOne(theFirst);//单个字符切割CharCut(theFirst);singleCharLength = singleChar.size();printf("采取字符轮廓数                               %d\n", singleCharLength);ShowChar();if (singleCharLength >= 7){MatchProvince();MatchNumber();}singleChar.clear();}}catch (Exception e) {cout << "Standard ecxeption : " << e.what() << " \n" << endl;}if (waitKey(30) >= 0)break;//延时30ms}//imwrite("match\\xxxxxx.bmp", singleChar[2]);time0 = ((double)getTickCount() - time0) / getTickFrequency();cout << "运行时间" << time0 << "秒" << endl;waitKey(0);
}void fillHole(const Mat srcBw, Mat &dstBw)
{Size imageSize = srcBw.size();Mat Temp = Mat::zeros(imageSize.height + 2, imageSize.width + 2, srcBw.type());//延展图像srcBw.copyTo(Temp(Range(1, imageSize.height + 1), Range(1, imageSize.width + 1)));cv::floodFill(Temp, Point(0, 0), Scalar(255));Mat cutImg;//裁剪延展的图像Temp(Range(1, imageSize.height + 1), Range(1, imageSize.width + 1)).copyTo(cutImg);dstBw = srcBw | (~cutImg);
}Mat Location(Mat srcImage)
{//判断变量重赋值flag = false;//用于旋转车牌int imageWidth, imageHeight;            //输入图像的长和宽imageWidth = srcImage.rows;                 //获取图片的宽imageHeight = srcImage.cols;                 //获取图像的长//!!!!!!!!!!!!!!!!!!!Mat blueROI = srcImage.clone();cvtColor(blueROI, blueROI, CV_BGR2HSV);//namedWindow("hsv图");//imshow("hsv图", blueROI);//中值滤波操作medianBlur(blueROI, blueROI, 3);//namedWindow("medianBlur图");//imshow("medianBlur图", blueROI);//将蓝色区域二值化inRange(blueROI, Scalar(100, 130, 50), Scalar(124, 255, 255), blueROI);//namedWindow("blue图");//imshow("blue图", blueROI);Mat element1 = getStructuringElement(MORPH_RECT, Size(2, 2));     //size()对速度有影响morphologyEx(blueROI, blueROI, MORPH_OPEN, element1);//namedWindow("0次K运算后图像");//imshow("0次K运算后图像", blueROI);Mat element0 = getStructuringElement(MORPH_ELLIPSE, Size(10, 10));     //size()对速度有影响morphologyEx(blueROI, blueROI, MORPH_CLOSE, element0);//namedWindow("0次闭运算后图像");//imshow("0次闭运算后图像", blueROI);vector<vector<Point>> contours;findContours(blueROI, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);int cnt = contours.size();cout << "number of contours   " << cnt << endl;  //打印轮廓个数if (cnt == 0){if (!flag)        //在视频中显示{cout << "图中无车牌       " << endl;//namedWindow("提取车牌结果图");//imshow("提取车牌结果图", srcImage);    //显示最终结果图VideoShow(srcImage);return  srcImage;}}double area;double longside, temp, shortside, long2short;float  angle = 0;Rect rect;RotatedRect box;    //可旋转的矩形盒子Point2f vertex[4];        //四个顶点Mat image = srcImage.clone();        //为后来显示做准备Mat  rgbCutImg;                       //车牌裁剪图//box.points(vertex);            //获取矩形四个顶点坐标//length=arcLength(contour[i]);                        //获取轮廓周长//area=contourArea(contour[i]);                        //获取轮廓面积//angle=box.angle;           //得到车牌倾斜角度for (int i = 0; i < cnt; i++){area = contourArea(contours[i]);              //获取轮廓面积if (area > 600 && area < 15000)     //矩形区域面积大小判断{rect = boundingRect(contours[i]);    //计算矩形边界box = minAreaRect(contours[i]);      //获取轮廓的矩形box.points(vertex);                  //获取矩形四个顶点坐标angle = box.angle;                   //得到车牌倾斜角度longside = sqrt(pow(vertex[1].x - vertex[0].x, 2) + pow(vertex[1].y - vertex[0].y, 2));shortside = sqrt(pow(vertex[2].x - vertex[1].x, 2) + pow(vertex[2].y - vertex[1].y, 2));if (shortside > longside)   //短轴大于长轴,交换数据{temp = longside;longside = shortside;shortside = temp;cout << "交换" << endl;}elseangle += 90;long2short = longside / shortside;if (long2short > 1.5 && long2short < 4.5){flag = true;for (int i = 0; i < 4; ++i)       //划线框出车牌区域line(image, vertex[i], vertex[((i + 1) % 4) ? (i + 1) : 0], Scalar(0, 255, 0), 1, CV_AA);if (!flag_1)        //在视频中显示{printf("提取成功\n");/*namedWindow("提取车牌结果图");imshow("提取车牌结果图", image);  */  //显示最终结果图VideoShow(image);}rgbCutImg = srcImage(rect);//namedWindow("车牌图");//imshow("车牌图", rgbCutImg);//裁剪出车牌    break;              //退出循环,以免容器中变量变换}}}cout << "倾斜角度:" << angle << endl;if (flag  &&  fabs(angle) > 0.8)        {flag_1 = 1;Mat RotractImg(imageWidth, imageHeight, CV_8UC1, Scalar(0, 0, 0));       Point2f center = box.center;           //获取车牌中心坐标Mat M2 = getRotationMatrix2D(center, angle, 1);       //计算旋转加缩放的变换矩阵 warpAffine(srcImage, RotractImg, M2, srcImage.size(), 1, 0, Scalar(0));     //namedWindow("倾斜矫正后图片",0);//imshow("倾斜矫正后图片", RotractImg);rgbCutImg = RotractImg(rect);      //截取车牌彩色照片//namedWindow("矫正后车牌照");//imshow("矫正后车牌照", rgbCutImg);/*cout << "矩形中心:" << box.center.x << "," << box.center.y << endl;*/return  rgbCutImg;}if (flag == false) {printf("提取失败\n");                      //后期加边缘检测法识别if (!flag_1)        //在视频中显示{/*namedWindow("提取车牌结果图");imshow("提取车牌结果图", image); */   //显示最终结果图VideoShow(image);}}return rgbCutImg;
}

/注意嵌套车牌/emmmm这个模型需要优化一下

Mat cutOne(Mat cutImage)
{//打印车牌长宽try {/*cout << "           cutImage.rows  :   " << cutImage.rows << endl;cout << "           cutImage.cols  :   " << cutImage.cols << endl;*/if(cutImage.rows >= cutImage.cols)resize(cutImage, cutImage, Size(320, 320 * cutImage.rows / cutImage.cols));}catch (Exception e){resize(cutImage, cutImage, Size(320, 100));}/*namedWindow("Resize车牌图");imshow("Resize车牌图", cutImage);*/int height = cutImage.rows;cout << "\tHeight:" << height << "\tWidth:" << 320 << endl;if (height < 86){printf("嵌套车牌\n");specialFlag = true;}Mat whiteROI = cutImage.clone();if (specialFlag){cvtColor(whiteROI, whiteROI, CV_BGR2HSV);//将白色区域二值化//inRange(whiteROI, Scalar(0, 0, 0), Scalar(130, 50, 245), whiteROI);      //增大 S 即饱和度可以使hsv白色检测范围更大inRange(whiteROI, Scalar(0, 0, 0), Scalar(180, 100, 245), whiteROI);//namedWindow("specialFlagwhiteROI图");//imshow("specialFlagwhiteROI图", whiteROI);}else{GaussianBlur(whiteROI, whiteROI, Size(3, 3), 0, 0);/*namedWindow("GaussianBlur车牌图");imshow("GaussianBlur车牌图", whiteROI);*/cvtColor(whiteROI, whiteROI, CV_BGR2HSV);//medianBlur(whiteROI, whiteROI, 3);//namedWindow("Src_medianBlur图");//imshow("Src_medianBlur图", whiteROI);//将白色区域二值化//inRange(whiteROI, Scalar(0, 0, 10), Scalar(180, 30, 255), whiteROI);      //增大 S 即饱和度可以使hsv白色检测范围更大inRange(whiteROI, Scalar(0, 0, 10), Scalar(180, 120, 255), whiteROI);//namedWindow("whiteROI图");//imshow("whiteROI图", whiteROI);}/*Mat element0 = getStructuringElement(MORPH_ELLIPSE, Size(4, 4));     //size()对速度有影响morphologyEx(whiteROI, whiteROI, MORPH_OPEN, element0);namedWindow("OPEN图");imshow("OPEN图", whiteROI);*/Mat dstImage = cutImage.clone();vector<vector<Point>> contours;findContours(whiteROI, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);drawContours(dstImage, contours, -1, Scalar(0, 0, 255), 1);//namedWindow("疑似字符轮廓识别图");//imshow("疑似字符轮廓识别图", dstImage);inRange(dstImage, Scalar(0, 0, 255), Scalar(0, 0, 255), dstImage);//namedWindow("字符大轮廓图");//imshow("字符大轮廓图", dstImage);/*fillHole(dstImage, dstImage);namedWindow("填补轮廓后图");imshow("填补轮廓后图", dstImage);*/int row1 = 2;int row2 = dstImage.rows;int rowMax = dstImage.rows - 1;            //开区间,防止越界int colMax = dstImage.cols - 1;            //开区间,防止越界int addFirst = 10;int addFirst0 = 0;int addFirst1 = 0;int addFirst2 = 0;//测中间像素//dstImage.at<uchar>(rowMax-1, colMax-1);//cout << "Width:" << j << endl;int addFirstTemp = addFirst;          //第一次用时已经改变数值,容易忽略!!!!!uchar* data;//裁剪上下边。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。//上边for (int i = 2; i < rowMax / 3; i++, addFirst1 = 0)                           //   6     刚刚好{data = dstImage.ptr<uchar>(i);for (int j = 2; j < colMax; j++){if (data[j] == 255){addFirst1++;}}if (addFirst1 < addFirst)                       //筛选最小值所在行{row1 = i;addFirst = addFirst1 + 3;//cout << "行头" << row1 << endl;//flag_x = 1;}}//下边for (int i = rowMax - 2; i > rowMax - rowMax / 4; i--, addFirst2 = 0)                //   6     刚刚好{data = dstImage.ptr<uchar>(i);for (int j = 2; j < colMax; j++){if (data[j] == 255){addFirst2++;}}if (addFirst2 < addFirstTemp)                       //筛选最小值所在行{row2 = i;addFirstTemp = addFirst2 + 3;//cout << "行底" << row2 << endl;//flag_y = 1;}}int orow;orow = row2 - row1;Mat w_image;Mat  rgb_w_image;w_image = dstImage(Rect(0, row1, colMax, orow));rgb_w_image = cutImage(Rect(0, row1, colMax, orow));//namedWindow("裁剪上下图");//imshow("裁剪上下图", w_image);int rowMax_ALT = w_image.rows - 1;            //开区间,防止越界(注意,裁剪完上下后要重新写行和宽,因为行和宽已经改变)int colMax_ALT = w_image.cols - 1;            //开区间,防止越界(注意,裁剪完上下后要重新写行和宽,因为行和宽已经改变)int col_1 = 2;int col_2 = w_image.cols;int add = 2;int add1 = 0;int add2 = 0;int addTemp = add;        //第一次用时已经改变数值,容易忽略!!!!!//裁剪左右边。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。//左边//for (int i = 0; i < colMax_ALT / 18; i++, add1 = 0)                           //       刚刚好//{// for (int j = 2; j < rowMax_ALT; j++)//    {//     data = dstImage.ptr<uchar>(j);//     if (data[i] == 255)//     {//         add1++;//     }// }// if (add1 < add)                       //筛选最小值所在列//   {//     col_1 = i;//       add = add1 + 1;// }//}//右边if (specialFlag){for (int i = colMax_ALT; i > colMax_ALT - colMax_ALT / 18; i--, add2 = 0)                //        刚刚好{for (int j = 2; j < rowMax_ALT; j++){data = dstImage.ptr<uchar>(j);if (data[i] == 255){add2++;}}if (add2 < addTemp)                       //筛选最小值所在列{col_2 = i;addTemp = add2 + 1;//cout << "行底" << row2 << endl;}}}int o_col;o_col = col_2 - col_1;Mat H_image;H_image = w_image(Rect(col_1, 0, o_col, rowMax_ALT));rgb_w_image = rgb_w_image(Rect(col_1, 0, o_col, rowMax_ALT));//namedWindow("再裁剪左右图");//imshow("再裁剪左右图", H_image);//namedWindow("裁剪后彩图");//imshow("裁剪后彩图", rgb_w_image);return rgb_w_image;
}void CharCut(Mat srcImage)
{resize(srcImage, srcImage, Size(320, 320 * srcImage.rows / srcImage.cols));//namedWindow("Resize车牌图");//imshow("Resize车牌图", srcImage);GaussianBlur(srcImage, srcImage, Size(3, 3), 0, 0);/*namedWindow("GaussianBlur车牌图");imshow("GaussianBlur车牌图", srcImage);
*/medianBlur(srcImage, srcImage, 3);//namedWindow("Src_medianBlur图");//imshow("Src_medianBlur图", srcImage);cvtColor(srcImage, srcImage, CV_BGR2HSV);//将白色区域二值化Mat doubleImage;//inRange(srcImage, Scalar(0, 0, 10), Scalar(180, 75, 255), doubleImage);      //增大 S 即饱和度可以使hsv白色检测范围更大inRange(srcImage, Scalar(0, 0, 0), Scalar(180, 125, 245), doubleImage);namedWindow("doubleImage图");imshow("doubleImage图", doubleImage);int colTemp = 0;int rowMax = doubleImage.rows;int colMax = doubleImage.cols;int addFirst = 0;int add = 0;int k1 = 0;int k2;int kTemp = 0;int times = 0;int oneCutEnd = 0;float t = 1.0;uchar* data;cout << "Test:            " << specialFlag << endl;//针对嵌套车牌处理if (specialFlag){for (int i = 2; i < colMax; i++, addFirst = 0, add = 0){for (int j = rowMax / 10.8; j < rowMax - rowMax / (10.8*t); j++){data = doubleImage.ptr<uchar>(j);if (data[i - 1] == 255){addFirst++;                             //统计前一列}}for (int j = rowMax / 10.8; j < rowMax - rowMax / (10.8*t); j++){data = doubleImage.ptr<uchar>(j);if (data[i] == 255){add++;                                  //统计后一列}}//省份字符分开切割if (!times){if (!oneCutEnd && (!addFirst  &&  add))k1 = i - 1;if (addFirst && !add){k2 = i;oneCutEnd = 1;if (k2 - k1 > colMax / 11.25){times = 1;if (k2 - k1 < colMax / 5.625)SingleCharCut(doubleImage, k1, k2);elsei = 2;}}}         //切割其他字符else {if (!addFirst  &&  add)k1 = i - 1;if (addFirst && !add){k2 = i;if (k2 - k1 > colMax / 32){if (k2 - k1 < colMax / 5.625)SingleCharCut(doubleImage, k1, k2);else                                   //针对嵌套车牌下部连接过靠上{i = k1;t -= 0.1;}}else{   //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!处理中间分割点与‘ 1 ’!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!for (int a = k1; a <= k2; a++){data = doubleImage.ptr<uchar>(rowMax / 5);if (data[a] == 255)kTemp++;}if (kTemp > 0)SingleCharCut(doubleImage, k1, k2);kTemp = 0;}}}}k2 = colMax;if (k2 - k1 > colMax / 32)SingleCharCut(doubleImage, k1, k2);specialFlag = false;}else {for (int i = 2; i < colMax; i++, addFirst = 0, add = 0){for (int j = rowMax / 12.8; j < rowMax - rowMax / 12.8; j++){data = doubleImage.ptr<uchar>(j);if (data[i - 1] == 255){addFirst++;}}for (int j = rowMax / 12.8; j < rowMax - rowMax / 12.8; j++){data = doubleImage.ptr<uchar>(j);if (data[i] == 255){add++;}}if (!times){if (!oneCutEnd && (!addFirst  &&  add))k1 = i - 1;if (addFirst && !add){k2 = i;oneCutEnd = 1;if (k2 - k1 > colMax / 11.25){times = 1;if (k2 - k1 < colMax / 5.625)SingleCharCut(doubleImage, k1, k2);elsei = 2;}}}else {if (!addFirst  &&  add)k1 = i - 1;if (addFirst && !add){k2 = i;if (k2 - k1 > colMax / 32)SingleCharCut(doubleImage, k1, k2);else{   //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!处理中间分割点与‘ 1 ’!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!for (int a = k1; a <= k2; a++){data = doubleImage.ptr<uchar>(rowMax / 5);if (data[a] == 255)kTemp++;}if (kTemp > 0)SingleCharCut(doubleImage, k1, k2);kTemp = 0;}}}}}
}void SingleCharCut(Mat doubleImage, int k1, int k2)
{//printf("k1 = %d ,k2 = %d\n", k1, k2);int rowMax = doubleImage.rows;Mat image;image = doubleImage(Rect(k1, 0, k2 - k1, rowMax));int row1 = 0;int row2 = image.rows;rowMax = image.rows - 1;            //开区间,防止越界int colMax = image.cols;            //开区间,防止越界int addFirst = 2;int addFirst1 = 0;int addFirst2 = 0;uchar* data;//测中间像素//image.at<uchar>(rowMax-1, colMax-1);//cout << "Width:" << j << endl;int addFirstTemp = addFirst;          //第一次用时已经改变数值,容易忽略!!!!!//裁剪上下边。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。//上边for (int i = 0; i < rowMax / 4; i++, addFirst1 = 0)                           //   6     刚刚好{data = image.ptr<uchar>(i);for (int j = 0; j < colMax; j++){if (data[j] == 255){addFirst1++;}}if (addFirst1 < addFirst)                       //筛选最小值所在行{row1 = i;addFirst = addFirst1 + 1;}}//下边for (int i = rowMax; i > rowMax - rowMax / 4; i--, addFirst2 = 0)                //   6     刚刚好{data = image.ptr<uchar>(i);for (int j = 2; j < colMax; j++){if (data[j] == 255){addFirst2++;}}if (addFirst2 < addFirstTemp)                       //筛选最小值所在行{row2 = i;addFirstTemp = addFirst2 + 1;}}int orow;orow = row2 - row1;Mat w_image;w_image = image(Rect(0, row1, colMax, orow));singleChar.push_back(w_image);}void ShowChar()
{int length = singleChar.size();for (int i = 0; i < length; i++) {resize(singleChar[i], singleChar[i], Size(20, 40));           //字符图像归一化//namedWindow(to_string(i) + "图");//imshow(to_string(i) + "图", singleChar[i]);}
}//读取省份模板
struct  stu
{Mat image;double matchDegree;
};
struct  stu first[35];
void readProvince()
{int i = 0;//读取字符{first[i].image = imread("match\\zw1.bmp", 0);i++;first[i].image = imread("match\\zw2.bmp", 0);i++;first[i].image = imread("match\\zw3.bmp", 0);i++;first[i].image = imread("match\\zw4.bmp", 0);i++;first[i].image = imread("match\\zw5.bmp", 0);i++;first[i].image = imread("match\\zw6.bmp", 0);i++;first[i].image = imread("match\\zw7.bmp", 0);i++;first[i].image = imread("match\\zw8.bmp", 0);i++;first[i].image = imread("match\\zw9.bmp", 0);i++;first[i].image = imread("match\\zw10.bmp", 0);i++;first[i].image = imread("match\\zw11.bmp", 0);i++;first[i].image = imread("match\\zw12.bmp", 0);i++;first[i].image = imread("match\\zw13.bmp", 0);i++;first[i].image = imread("match\\zw14.bmp", 0);i++;first[i].image = imread("match\\zw15.bmp", 0);i++;first[i].image = imread("match\\zw16.bmp", 0);i++;first[i].image = imread("match\\zw17.bmp", 0);i++;first[i].image = imread("match\\zw18.bmp", 0);i++;first[i].image = imread("match\\zw19.bmp", 0);i++;first[i].image = imread("match\\zw20.bmp", 0);i++;first[i].image = imread("match\\zw21.bmp", 0);i++;first[i].image = imread("match\\zw22.bmp", 0);i++;first[i].image = imread("match\\zw23.bmp", 0);i++;first[i].image = imread("match\\zw24.bmp", 0);i++;first[i].image = imread("match\\zw25.bmp", 0);i++;first[i].image = imread("match\\zw26.bmp", 0);i++;first[i].image = imread("match\\zw27.bmp", 0);i++;first[i].image = imread("match\\zw28.bmp", 0);i++;first[i].image = imread("match\\zw29.bmp", 0);i++;first[i].image = imread("match\\zw30.bmp", 0);i++;first[i].image = imread("match\\zw31.bmp", 0);i++;first[i].image = imread("match\\zw32.bmp", 0);i++;first[i].image = imread("match\\zw33.bmp", 0);i++;first[i].image = imread("match\\zw34.bmp", 0);i++;first[i].image = imread("match\\zw35.bmp", 0);}}//识别省份字符
void MatchProvince()
{int rowMax = 40;int colMax = 20;int add = 0;int addTemp = 0;Mat absCutImage;double temp;int index = 0;uchar* data;for (int i = 0; i < rowMax; i++){data = singleChar[0].ptr<uchar>(i);for (int j = 0; j < colMax; j++){if (data[j] == 255){add++;}}}int firstLength = end(first) - begin(first);//printf("数组长度1         %d\n",firstLength);for (int x = 0; x < firstLength; x++, addTemp = 0){absCutImage = abs(first[x].image - singleChar[0]);for (int i = 0; i < rowMax; i++){data = absCutImage.ptr<uchar>(i);for (int j = 0; j < colMax; j++){if (data[j] == 255){addTemp++;}}}temp = 1.0 - 1.0*addTemp / add;if (temp <= 1)first[x].matchDegree = temp;elsefirst[x].matchDegree = 0;if (x > 0 && first[x].matchDegree > first[index].matchDegree)index = x;}/*absCutImage = abs(singleChar[0] - first[index].image);namedWindow("省份图片相减后图" + to_string(0));imshow("省份图片相减后图" + to_string(0), absCutImage);*/printf("省份字符最大匹配度:  %lf\n", first[index].matchDegree);switch (index) {case 0:printf("藏");carPlateProvince += "藏";break;case 1:printf("川");carPlateProvince += "川";break;case 2:printf("鄂");carPlateProvince += "鄂";break;case 3:printf("甘");carPlateProvince += "甘";break;case 4:printf("赣");carPlateProvince += "赣";break;case 5:printf("贵");carPlateProvince += "贵";break;case 6:printf("桂");carPlateProvince += "桂";break;case 7:printf("黑");carPlateProvince += "黑";break;case 8:printf("泸");carPlateProvince += "泸";break;case 9:printf("吉");carPlateProvince += "吉";break;case 10:printf("翼");carPlateProvince += "翼";break;case 11:printf("津");carPlateProvince += "津";break;case 12:printf("晋");carPlateProvince += "晋";break;case 13:printf("京");carPlateProvince += "京";break;case 14:printf("辽");carPlateProvince += "辽";break;case 15:printf("鲁");carPlateProvince += "鲁";break;case 16:printf("蒙");carPlateProvince += "蒙";break;case 17:printf("闽");carPlateProvince += "闽";break;case 18:printf("宁");carPlateProvince += "宁";break;case 19:printf("青");carPlateProvince += "青";break;case 20:printf("琼");carPlateProvince += "琼";break;case 21:printf("陕");carPlateProvince += "陕";break;case 22:printf("苏");carPlateProvince += "苏";break;case 23:printf("皖");carPlateProvince += "皖";break;case 24:printf("湘");carPlateProvince += "湘";break;case 25:printf("新");carPlateProvince += "新";break;case 26:printf("渝");carPlateProvince += "渝";break;case 27:printf("豫");carPlateProvince += "豫";break;case 28:printf("粤");carPlateProvince += "粤";break;case 29:printf("云");carPlateProvince += "云";break;case 30:printf("浙");carPlateProvince += "浙";break;case 31:printf("湘");carPlateProvince += "湘";break;case 32:printf("湘");carPlateProvince += "湘";break;case 33:printf("鲁");carPlateProvince += "鲁";break;case 34:printf("粤");carPlateProvince += "粤";break;}printf("\n");
}

读取模板文件


struct stu1
{char number;Mat image;double matchDegree;
};
struct stu1 second[49];
void readNumber()
{for (int i = 0; i < 10; i++) {second[i].image = imread("match\\" + to_string(i) + ".bmp", 0);second[i].number = 48 + i;}//读取字符{int i = 10;second[i].image = imread("match\\6a.bmp", 0);second[i].number = '6';i++;second[i].image = imread("match\\3a.bmp", 0);second[i].number = '3';i++;second[i].image = imread("match\\P1.bmp", 0);second[i].number = 'P';i++;second[i].image = imread("match\\8b.bmp", 0);second[i].number = '8';i++;second[i].image = imread("match\\K1.bmp", 0);second[i].number = 'K';i++;second[i].image = imread("match\\9a.bmp", 0);second[i].number = '9';i++;second[i].image = imread("match\\B2.bmp", 0);second[i].number = 'B';i++;second[i].image = imread("match\\G1.bmp", 0);second[i].number = 'G';i++;second[i].image = imread("match\\T1.bmp", 0);second[i].number = 'T';i++;second[i].image = imread("match\\B1.bmp", 0);second[i].number = 'B';i++;second[i].image = imread("match\\8a.bmp", 0);second[i].number = '8';i++;second[i].image = imread("match\\1a.bmp", 0);second[i].number = '1';i++;second[i].image = imread("match\\7a.bmp", 0);second[i].number = '7';i++;second[i].image = imread("match\\D1.bmp", 0);second[i].number = 'D';i++;second[i].image = imread("match\\0a.bmp", 0);second[i].number = '0';i++;second[i].image = imread("match\\A.bmp", 0);second[i].number = 'A';i++;second[i].image = imread("match\\B.bmp", 0);second[i].number = 'B';i++;second[i].image = imread("match\\C.bmp", 0);second[i].number = 'C';i++;second[i].image = imread("match\\D.bmp", 0);second[i].number = 'D';i++;second[i].image = imread("match\\E.bmp", 0);second[i].number = 'E';i++;second[i].image = imread("match\\F.bmp", 0);second[i].number = 'F';i++;second[i].image = imread("match\\G.bmp", 0);second[i].number = 'G';i++;second[i].image = imread("match\\H.bmp", 0);second[i].number = 'H';i++;second[i].image = imread("match\\J.bmp", 0);second[i].number = 'J';i++;second[i].image = imread("match\\K.bmp", 0);second[i].number = 'K';i++;second[i].image = imread("match\\L.bmp", 0);second[i].number = 'L';i++;second[i].image = imread("match\\M.bmp", 0);second[i].number = 'M';i++;second[i].image = imread("match\\N.bmp", 0);second[i].number = 'N';i++;second[i].image = imread("match\\P.bmp", 0);second[i].number = 'P';i++;second[i].image = imread("match\\Q.bmp", 0);second[i].number = 'Q';i++;second[i].image = imread("match\\R.bmp", 0);second[i].number = 'R';i++;second[i].image = imread("match\\S.bmp", 0);second[i].number = 'S';i++;second[i].image = imread("match\\T.bmp", 0);second[i].number = 'T';i++;second[i].image = imread("match\\U.bmp", 0);second[i].number = 'U';i++;second[i].image = imread("match\\V.bmp", 0);second[i].number = 'V';i++;second[i].image = imread("match\\W.bmp", 0);second[i].number = 'W';i++;second[i].image = imread("match\\X.bmp", 0);second[i].number = 'X';i++;second[i].image = imread("match\\Y.bmp", 0);second[i].number = 'Y';i++;second[i].image = imread("match\\Z.bmp", 0);second[i].number = 'Z';}}//识别其他字符
void MatchNumber()
{int rowMax = 40;int colMax = 20;int add = 0;int addTemp = 0;Mat absCutImage;double temp;int index = 0;int length = singleChar.size();int secondLength = end(second) - begin(second);//printf("数组长度2         %d   \n", secondLength);uchar* data;int q = 0;for (int y = 1; y < length; y++, add = 0, index = 0){if (y > 6)                //防止多读break;//统计要读取字符的白色像素总值for (int i = 0; i < rowMax; i++){data = singleChar[y].ptr<uchar>(i);for (int j = 0; j < colMax; j++){if (data[j] == 255){add++;}}}//逐个字符识别for (int x = 0; x < secondLength; x++, addTemp = 0){absCutImage = abs(singleChar[y] - second[x].image);//统计相减之后的图像白色像素总值for (int i = 0; i < rowMax; i++){data = absCutImage.ptr<uchar>(i);for (int j = 0; j < colMax; j++){if (data[j] == 255){addTemp++;}}}temp = 1.0 - 1.0*addTemp / add;if (temp <= 1 && temp > 0)second[x].matchDegree = temp;elsesecond[x].matchDegree = 0;//获取最大匹配度对应索引indexif (x > 0 && second[x].matchDegree > second[index].matchDegree)index = x;}absCutImage = abs(singleChar[y] - second[index].image);/*  namedWindow("图片相减后图"+to_string(y));imshow("图片相减后图" + to_string(y), absCutImage);*/printf("最大匹配度:  %lf\n", second[index].matchDegree);printf("对应字符:    %c\n", second[index].number);test[q] = second[index].number;//printf("\ntest11111           %c\n", test[q]);q++;}test[q] = '\0';//printf("\ntest22222           %c\n", test[q-1]);//cout<<  "xxxxxxxxxxxxxx"<<carPlate<<endl;
}void VideoShow(Mat videoImage)
{/*if(videoFlag==0)*///carPlate = "京A J9846";carPlate += test;carPlateProvince += carPlate;/*carPlate.copy(test,strlen(test));*/cout <<  carPlateProvince << endl;cout <<  carPlateProvince.length() << endl;if(carPlateProvince.length()<10)putTextZH(videoImage, "Not Plate!", Point(490, 20), Scalar(0, 0, 255), 30, "Arial", false, false);elseputTextZH(videoImage, carPlateProvince.c_str(), Point(490, 20), Scalar(0, 0, 255), 30, "Arial", false, false);/*else if(videoFlag==1)*/namedWindow("提取车牌结果图");imshow("提取车牌结果图", videoImage);carPlateProvince = " ";carPlate = " ";
}void GetStringSize(HDC hDC, const char* str, int* w, int* h)
{SIZE size;GetTextExtentPoint32A(hDC, str, strlen(str), &size);if (w != 0) *w = size.cx;if (h != 0) *h = size.cy;
}void putTextZH(Mat &dst, const char* str, Point org, Scalar color, int fontSize, const char* fn, bool italic, bool underline)
{CV_Assert(dst.data != 0 && (dst.channels() == 1 || dst.channels() == 3));int x, y, r, b;if (org.x > dst.cols || org.y > dst.rows) return;x = org.x < 0 ? -org.x : 0;y = org.y < 0 ? -org.y : 0;LOGFONTA lf;lf.lfHeight = -fontSize;lf.lfWidth = 0;lf.lfEscapement = 0;lf.lfOrientation = 0;lf.lfWeight = 5;lf.lfItalic = italic;   //斜体lf.lfUnderline = underline; //下划线lf.lfStrikeOut = 0;lf.lfCharSet = DEFAULT_CHARSET;lf.lfOutPrecision = 0;lf.lfClipPrecision = 0;lf.lfQuality = PROOF_QUALITY;lf.lfPitchAndFamily = 0;strcpy_s(lf.lfFaceName, fn);HFONT hf = CreateFontIndirectA(&lf);HDC hDC = CreateCompatibleDC(0);HFONT hOldFont = (HFONT)SelectObject(hDC, hf);int strBaseW = 0, strBaseH = 0;int singleRow = 0;char buf[1 << 12];strcpy_s(buf, str);char *bufT[1 << 12];  // 这个用于分隔字符串后剩余的字符,可能会超出。//处理多行{int nnh = 0;int cw, ch;const char* ln = strtok_s(buf, "\n", bufT);while (ln != 0){GetStringSize(hDC, ln, &cw, &ch);strBaseW = max(strBaseW, cw);strBaseH = max(strBaseH, ch);ln = strtok_s(0, "\n", bufT);nnh++;}singleRow = strBaseH;strBaseH *= nnh;}if (org.x + strBaseW < 0 || org.y + strBaseH < 0){SelectObject(hDC, hOldFont);DeleteObject(hf);DeleteObject(hDC);return;}r = org.x + strBaseW > dst.cols ? dst.cols - org.x - 1 : strBaseW - 1;b = org.y + strBaseH > dst.rows ? dst.rows - org.y - 1 : strBaseH - 1;org.x = org.x < 0 ? 0 : org.x;org.y = org.y < 0 ? 0 : org.y;BITMAPINFO bmp = { 0 };BITMAPINFOHEADER& bih = bmp.bmiHeader;int strDrawLineStep = strBaseW * 3 % 4 == 0 ? strBaseW * 3 : (strBaseW * 3 + 4 - ((strBaseW * 3) % 4));bih.biSize = sizeof(BITMAPINFOHEADER);bih.biWidth = strBaseW;bih.biHeight = strBaseH;bih.biPlanes = 1;bih.biBitCount = 24;bih.biCompression = BI_RGB;bih.biSizeImage = strBaseH * strDrawLineStep;bih.biClrUsed = 0;bih.biClrImportant = 0;void* pDibData = 0;HBITMAP hBmp = CreateDIBSection(hDC, &bmp, DIB_RGB_COLORS, &pDibData, 0, 0);CV_Assert(pDibData != 0);HBITMAP hOldBmp = (HBITMAP)SelectObject(hDC, hBmp);//color.val[2], color.val[1], color.val[0]SetTextColor(hDC, RGB(255, 255, 255));SetBkColor(hDC, 0);//SetStretchBltMode(hDC, COLORONCOLOR);strcpy_s(buf, str);const char* ln = strtok_s(buf, "\n", bufT);int outTextY = 0;while (ln != 0){TextOutA(hDC, 0, outTextY, ln, strlen(ln));outTextY += singleRow;ln = strtok_s(0, "\n", bufT);}uchar* dstData = (uchar*)dst.data;int dstStep = dst.step / sizeof(dstData[0]);unsigned char* pImg = (unsigned char*)dst.data + org.x * dst.channels() + org.y * dstStep;unsigned char* pStr = (unsigned char*)pDibData + x * 3;for (int tty = y; tty <= b; ++tty){unsigned char* subImg = pImg + (tty - y) * dstStep;unsigned char* subStr = pStr + (strBaseH - tty - 1) * strDrawLineStep;for (int ttx = x; ttx <= r; ++ttx){for (int n = 0; n < dst.channels(); ++n) {double vtxt = subStr[n] / 255.0;int cvv = vtxt * color.val[n] + (1 - vtxt) * subImg[n];subImg[n] = cvv > 255 ? 255 : (cvv < 0 ? 0 : cvv);}subStr += 3;subImg += dst.channels();}}SelectObject(hDC, hOldBmp);SelectObject(hDC, hOldFont);DeleteObject(hf);DeleteObject(hBmp);DeleteDC(hDC);
}

最后是一个帧率显示:

我看到了一个opencv播放视频实时显示帧速(FPS),然后用他的方法进行函数封装。

void ShowFPS_Method_One(Mat &dstImage) {char str[20];    //存放字符串化的帧率double fps;      //帧率double t_front, t_now;                  //用于中间计算t_front = (double)getTickCount();      //返回从操作系统启动到当前所经过的毫秒数t_now = ((double)getTickCount() - t_front) / getTickFrequency();  //getTickFrequency返回每秒的计时周期数fps = 1.0 / t_now;string FPSstring("FPS:");sprintf_s(str, "%.2f", fps);FPSstring += str;//在帧上显示"FPS:XXXX"putText(dstImage, FPSstring, Point(5, 20),CV_FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 255, 255));
}

代码还需要优化,字符切割用的指针遍历像素加限制条件切割,感觉思想可以用到FPGA上(大概吧),我接触计算机视觉不久而且主要是深度学习那方面,这个完整工程文件是我和我同学做的所以在询问他之后可能会上传。

合工大苍穹战队视觉组培训Day8——视觉,目标识别相关推荐

  1. 合工大苍穹战队视觉组培训Day9——相机标定

    目录 学习目标: 学习内容: 学习时间: 学习产出: 一.前期准备 二.采集相关数据 三.进行标定 四.用cv::undistort方法去除畸变 五.效果 六.心得 学习目标: pnp解算 相机标定 ...

  2. 第十七届全国大学生智能车竞赛智能视觉组培训第一弹

    简 介: 第十七届全国大学生智能车竞赛竞速比赛中,由NXP赞助的智能视觉组的比赛细则已经公布的.为了帮助同学们能够更好地制作比赛作品,在在此过程中得到更好地锻炼,NXP公司近期举行智能视觉组培训.欢迎 ...

  3. 智能车大赛AI视觉组培训第一弹——基础篇

    智能车大赛AI视觉组培训第一弹: ▌基础篇 1.培训简介 ■时间安排: 2021年03月18日 14:00 ■内容简介: 由恩智浦赞助的AI视觉组,今年由于把人工智能应用在智能车竞赛中,使得难度提升了 ...

  4. AI视觉组培训第二弹——入门篇

    ▌入门篇   由恩智浦赞助的AI视觉组,今年把人工智能应用在智能车竞赛中的难度由提高了一步.这里面最重要的部分就是,用人工智能的神经网络进行图片识别.   上一次培训,我们已经介绍了人工智能和神经网络 ...

  5. 2023年智能车大赛智能视觉组培训第一弹——拆题

    1.内容简介 讲座时间: 2023年02月10日 14:00   第十八届全国大学生智能汽车竞赛已拉开帷幕,恩智浦继续为大赛提供赞助和技术支持.   今年是智能视觉组的第三年,经过前两年的探索和迭代, ...

  6. 2021苍穹战队视觉组寒假学习计划--环境配置

    2021苍穹战队视觉组寒假学习计划–环境配置 前言 本文为环境配置文章,寒假学习使用平台为Python++Opencv+Pytorch,推荐使用软件为Anaconda+Pytorch+Opencv+P ...

  7. 第16届智能车智能视觉组-上海交通大学AuTop战队算法分享

    简 介: 参加了第十六届智能车竞赛室内视觉AI组别同学留下的对于智能车竞赛参赛感悟与建议.并对于自己参赛过程中学习研究的算法进行开源并给出了详细的介绍. 关键词: 智能车竞赛,SJTU-AuTop,视 ...

  8. RoboMaster机甲大师——视觉组——总结、未来期望与比赛经验分享

    RoboMaster机甲大师--视觉组--总结与未来期望 FOR THE SIGMA FOR THE GTINDER FOR THE ROBOMASTER 入坑指南 各兵种需要的视觉: 步兵-每年必备 ...

  9. 第十六届全国大学生智能汽车竞赛总决赛 AI视觉组线上赛图片显示软件发布及线上赛注意事项

    简 介: 本文对于第十六届全国大学生智能车竞赛视觉AI组线上比赛的识别任务软件以及相关比赛流程注意事项进行总结. 关键词: 智能车竞赛,视觉AI组 §01 积分分值   根据 第十六届全国大学生智能车 ...

最新文章

  1. Java中System.getProperty()的参数
  2. 搭建Windows Server 2008故障转移群集
  3. FPGA之道(30)编写自己的vhdl库文件
  4. 集群的定义以及类别定义
  5. FUN ALV TOOLBAR 增加自定义按钮
  6. 前端js vue遇到的一些简单的数据处理-持续更新
  7. markdown pad激活
  8. Android Studio 自定义Gradle Plugin
  9. oracle中何时会用到join,Oracle中join用法的演示
  10. APICloud App定制平台的操作指南
  11. VMware Cloud Director 严重漏洞可使整个云基础设施遭接管
  12. Deep Learning论文翻译(Nature Deep Review)
  13. fedroa设置启动快捷键
  14. 手机 长按删除 html5,移动端长按删除
  15. HBase进化之从NoSQL到NewSQL,凤凰涅槃成就Phoenix
  16. 横滚角,俯仰角,航向角
  17. 十四、Debian 10 ldap
  18. android系统的市场占有率,安卓系统市场占有率竟然比苹果iOS高了这么多
  19. facebook 邀请好友
  20. bpl文件java_BPL_Motor Control Virtual Commissioning

热门文章

  1. 物流系统管理课程(二)
  2. TableauBDP,哪个才是最适合中国用户的数据可视化分析工具?
  3. python百位数千位数取整
  4. python入门和使用
  5. 旷视科技(Face++)面经
  6. 分层确定性钱包(HD Wallets)
  7. 2018上海科技大学991数据结构与算法
  8. 我的世界网易绘梦师国服 物品材料介绍
  9. 国内H5页面制作工具评测,iH5排第一
  10. git 使用过程遇到的问题