static Color get_plate_color(const char* image) { cv::Mat img = cv::imread(image); _ASSERT(!img.empty()); return getPlateType(img, true); }
int CCharsRecognise::charsRecognise(CPlate& plate, std::string& plateLicense) { std::vector<Mat> matChars; Mat plateMat = plate.getPlateMat(); // 判断车牌颜色以此确认threshold方法 Color color; if (plate.getPlateLocateType() == CMSER) { color = plate.getPlateColor(); } else { int w = plateMat.cols; int h = plateMat.rows; Mat tmpMat = plateMat(Rect_<double>(w * 0.1, h * 0.1, w * 0.8, h * 0.8)); color = getPlateType(tmpMat, true); } int result = m_charsSegment->charsSegment(plateMat, matChars, color); //std::cout << "charsSegment:" << result << std::endl; if (result == 0) { //for (auto block : matChars) { // auto character = CharsIdentify::instance()->identify(block); // plateLicense.append(character.second); //} int num = matChars.size(); for (int j = 0; j < num; j++) { Mat charMat = matChars.at(j); bool isChinses = false; //if (j == 0) // isChinses = true; //auto character = CharsIdentify::instance()->identify(charMat, isChinses); //plateLicense.append(character.second); std::pair<std::string, std::string> character; float maxVal; if (j == 0) { isChinses = true; bool judge = true; character = CharsIdentify::instance()->identifyChinese(charMat, maxVal, judge); plateLicense.append(character.second); } else { isChinses = false; character = CharsIdentify::instance()->identify(charMat, isChinses); plateLicense.append(character.second); } CCharacter charResult; charResult.setCharacterMat(charMat); charResult.setCharacterStr(character.second); plate.addReutCharacter(charResult); } if (plateLicense.size() < 7) { return -1; } } return result; }
//! 字符分割与排序 int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec) { if( !input.data ) { return -3; } //判断车牌颜色以此确认threshold方法 int plateType = getPlateType(input); cvtColor(input, input, CV_RGB2GRAY); //Threshold input image Mat img_threshold; if (1 == plateType) threshold(input, img_threshold, 10, 255, CV_THRESH_OTSU+CV_THRESH_BINARY); else threshold(input, img_threshold, 10, 255, CV_THRESH_OTSU+CV_THRESH_BINARY_INV); if(m_debug) { stringstream ss(stringstream::in | stringstream::out); ss << "image/tmp/debug_char_threshold" << ".jpg"; imwrite(ss.str(), img_threshold); } //去除车牌上方的柳钉以及下方的横线等干扰 clearLiuDing(img_threshold); if(m_debug) { stringstream ss(stringstream::in | stringstream::out); ss << "image/tmp/debug_char_clearLiuDing" << ".jpg"; imwrite(ss.str(), img_threshold); } Mat img_contours; img_threshold.copyTo(img_contours); vector< vector< Point> > contours; findContours(img_contours, contours, // a vector of contours CV_RETR_EXTERNAL, // retrieve the external contours CV_CHAIN_APPROX_NONE); // all pixels of each contours //Start to iterate to each contour founded vector<vector<Point> >::iterator itc= contours.begin(); vector<Rect> vecRect; //Remove patch that are no inside limits of aspect ratio and area. //将不符合特定尺寸的图块排除出去 while (itc != contours.end()) { Rect mr = boundingRect(Mat(*itc)); Mat auxRoi(img_threshold, mr); if (verifySizes(auxRoi)) { vecRect.push_back(mr); } ++itc; } if (vecRect.size() == 0) return -3; vector<Rect> sortedRect; //对符合尺寸的图块按照从左到右进行排序 SortRect(vecRect, sortedRect); int specIndex = 0; //获得指示城市的特定Rect,如苏A的"A" specIndex = GetSpecificRect(sortedRect); if(m_debug) { if (specIndex < sortedRect.size()) { Mat specMat(img_threshold, sortedRect[specIndex]); stringstream ss(stringstream::in | stringstream::out); ss << "image/tmp/debug_specMat" <<".jpg"; imwrite(ss.str(), specMat); } } //根据特定Rect向左反推出中文字符 //这样做的主要原因是根据findContours方法很难捕捉到中文字符的准确Rect,因此仅能 //退过特定算法来指定 Rect chineseRect; if (specIndex < sortedRect.size()) chineseRect = GetChineseRect(sortedRect[specIndex]); else return -3; if(m_debug) { Mat chineseMat(img_threshold, chineseRect); stringstream ss(stringstream::in | stringstream::out); ss << "image/tmp/debug_chineseMat" <<".jpg"; imwrite(ss.str(), chineseMat); } //新建一个全新的排序Rect //将中文字符Rect第一个加进来,因为它肯定是最左边的 //其余的Rect只按照顺序去6个,车牌只可能是7个字符!这样可以避免阴影导致的“1”字符 vector<Rect> newSortedRect; newSortedRect.push_back(chineseRect); RebuildRect(sortedRect, newSortedRect, specIndex); if (newSortedRect.size() == 0) return -3; for (int i = 0; i < newSortedRect.size(); i++) { Rect mr = newSortedRect[i]; Mat auxRoi(img_threshold, mr); if (1) { auxRoi = preprocessChar(auxRoi); if(m_debug) { stringstream ss(stringstream::in | stringstream::out); ss << "image/tmp/debug_char_auxRoi_" << i <<".jpg"; imwrite(ss.str(), auxRoi); } resultVec.push_back(auxRoi); } } return 0; }
//! 字符分割与排序 int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec) { // 输入图片无数据,返回ErrorCode=0x01 if (!input.data) return 0x01; int w = input.cols; int h = input.rows; Mat tmpMat = input(Rect(w*0.1,h*0.1,w*0.8,h*0.8)); //判断车牌颜色以此确认threshold方法 Color plateType = getPlateType(tmpMat, true); Mat input_grey; cvtColor(input, input_grey, CV_BGR2GRAY); Mat img_threshold ; if (BLUE == plateType) { //cout << "BLUE" << endl; img_threshold = input_grey.clone(); int w = input_grey.cols; int h = input_grey.rows; Mat tmp = input_grey(Rect(w*0.1,h*0.1,w*0.8,h*0.8)); int threadHoldV = ThresholdOtsu(tmp); imwrite("E:/img_inputgray2.jpg",input_grey); threshold(input_grey, img_threshold, threadHoldV, 255, CV_THRESH_BINARY); imwrite("E:/img_threshold.jpg",img_threshold); //threshold(input_grey, img_threshold, 5, 255, CV_THRESH_OTSU + CV_THRESH_BINARY); } else if (YELLOW == plateType) { //cout << "YELLOW" << endl; img_threshold = input_grey.clone(); int w = input_grey.cols; int h = input_grey.rows; Mat tmp = input_grey(Rect(w*0.1,h*0.1,w*0.8,h*0.8)); int threadHoldV = ThresholdOtsu(tmp); imwrite("./image/tmp/inputgray2.jpg",input_grey); threshold(input_grey, img_threshold, threadHoldV, 255, CV_THRESH_BINARY_INV); //threshold(input_grey, img_threshold, 10, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV); } else if (WHITE == plateType) { //cout << "WHITE" << endl; /*img_threshold = input_grey.clone(); int w = input_grey.cols; int h = input_grey.rows; Mat tmp = input_grey(Rect(w*0.1, h*0.1, w*0.8, h*0.8)); int threadHoldV = ThresholdOtsu(tmp); imwrite("./image/tmp/inputgray2.jpg", input_grey);*/ threshold(input_grey, img_threshold, 10, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV); } else { //cout << "UNKNOWN" << endl; threshold(input_grey, img_threshold, 10, 255, CV_THRESH_OTSU + CV_THRESH_BINARY); } if (0) { imshow("threshold", img_threshold); waitKey(0); destroyWindow("threshold"); } if (m_debug) { stringstream ss(stringstream::in | stringstream::out); ss << "image/tmp/debug_char_threshold" <<iTag<< ".jpg"; imwrite(ss.str(), img_threshold); } // 去除车牌上方的柳钉以及下方的横线等干扰 // 并且也判断了是否是车牌 // 并且在此对字符的跳变次数以及字符颜色所占的比重做了是否是车牌的判别条件 // 如果不是车牌,返回ErrorCode=0x02 if (!clearLiuDing(img_threshold)) return 0x02; if (m_debug) { stringstream ss(stringstream::in | stringstream::out); ss << "image/tmp/debug_char_clearLiuDing" <<iTag<< ".jpg"; imwrite(ss.str(), img_threshold); } iTag++; Mat img_contours; img_threshold.copyTo(img_contours); vector< vector< Point> > contours; findContours(img_contours, contours, // a vector of contours CV_RETR_EXTERNAL, // retrieve the external contours CV_CHAIN_APPROX_NONE); // all pixels of each contours vector<vector<Point> >::iterator itc = contours.begin(); vector<Rect> vecRect; // 将不符合特定尺寸的字符块排除出去 while (itc != contours.end()) { Rect mr = boundingRect(Mat(*itc)); Mat auxRoi(img_threshold, mr); if (verifyCharSizes(auxRoi)) vecRect.push_back(mr); ++itc; } // 如果找不到任何字符块,则返回ErrorCode=0x03 if (vecRect.size() == 0) return 0x03; // 对符合尺寸的图块按照从左到右进行排序; /*vector<Rect> sortedRect; SortRect(vecRect, sortedRect);*/ vector<Rect> sortedRect(vecRect); std::sort(sortedRect.begin(), sortedRect.end(), [](const Rect &r1, const Rect &r2) {return r1.x < r2.x;}); int specIndex = 0; //获得特殊字符对应的Rectt,如苏A的"A" specIndex = GetSpecificRect(sortedRect); if (m_debug) { if (specIndex < sortedRect.size()) { Mat specMat(img_threshold, sortedRect[specIndex]); stringstream ss(stringstream::in | stringstream::out); ss << "image/tmp/debug_specMat" << ".jpg"; imwrite(ss.str(), specMat); } } //根据特定Rect向左反推出中文字符 //这样做的主要原因是根据findContours方法很难捕捉到中文字符的准确Rect,因此仅能 //退过特定算法来指定 Rect chineseRect; if (specIndex < sortedRect.size()) chineseRect = GetChineseRect(sortedRect[specIndex]); else return -3; if (m_debug) { Mat chineseMat(img_threshold, chineseRect); stringstream ss(stringstream::in | stringstream::out); ss << "image/tmp/debug_chineseMat" << ".jpg"; imwrite(ss.str(), chineseMat); } //新建一个全新的排序Rect //将中文字符Rect第一个加进来,因为它肯定是最左边的 //其余的Rect只按照顺序去6个,车牌只可能是7个字符!这样可以避免阴影导致的“1”字符 vector<Rect> newSortedRect; newSortedRect.push_back(chineseRect); RebuildRect(sortedRect, newSortedRect, specIndex); if (newSortedRect.size() == 0) return -3; for (int i = 0; i < newSortedRect.size(); i++) { Rect mr = newSortedRect[i]; Mat auxRoi(img_threshold, mr); if (1) { auxRoi = preprocessChar(auxRoi); if (m_debug) { stringstream ss(stringstream::in | stringstream::out); ss << "image/tmp/debug_char_auxRoi_" << (i+staticIndex) << ".jpg"; imwrite(ss.str(), auxRoi); } resultVec.push_back(auxRoi); } } staticIndex+=newSortedRect.size(); return 0; }
int CPlateRecognize::plateRecognize(Mat src, std::vector<CPlate> &plateVecOut, int img_index) { std::vector<CPlate> plateVec; int resultPD = plateDetect(src, plateVec, img_index); if (resultPD == 0) { size_t num = plateVec.size(); int index = 0; for (size_t j = 0; j < num; j++) { CPlate item = plateVec.at(j); Mat plateMat = item.getPlateMat(); if (0) { imshow("plate", plateMat); waitKey(0); destroyWindow("plate"); } Color color = item.getPlateColor(); if (color == UNKNOWN) { color = getPlateType(plateMat, true); item.setPlateColor(color); } std::string plateColor = getPlateColor(color); if (0) { std::cout << "plateColor:" << plateColor << std::endl; } std::string plateIdentify = ""; int resultCR = charsRecognise(item, plateIdentify); if (resultCR == 0) { std::string license = plateColor + ":" + plateIdentify; item.setPlateStr(license); plateVecOut.push_back(item); } else { std::string license = plateColor; item.setPlateStr(license); plateVecOut.push_back(item); if (0) { std::cout << "resultCR:" << resultCR << std::endl; } } } if (getResultShow()) { Mat result; src.copyTo(result); for (size_t j = 0; j < num; j++) { CPlate item = plateVec[j]; Mat plateMat = item.getPlateMat(); int height = 36; int width = 136; if (height * index + height < result.rows) { Mat imageRoi = result(Rect(0, 0 + height * index, width, height)); addWeighted(imageRoi, 0, plateMat, 1, 0, imageRoi); } index++; RotatedRect minRect = item.getPlatePos(); Point2f rect_points[4]; minRect.points(rect_points); Scalar lineColor = Scalar(255, 255, 255); if (item.getPlateLocateType() == SOBEL) lineColor = Scalar(255, 0, 0); if (item.getPlateLocateType() == COLOR) lineColor = Scalar(0, 255, 0); if (item.getPlateLocateType() == CMSER) lineColor = Scalar(0, 0, 255); for (int j = 0; j < 4; j++) line(result, rect_points[j], rect_points[(j + 1) % 4], lineColor, 2, 8); } showResult(result); } } return resultPD; }
//! 字符分割与排序 int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec, int index) { if (!input.data) return 0x01; int w = input.cols; int h = input.rows; Mat tmpMat = input(Rect_<double>(w * 0.1, h * 0.1, w * 0.8, h * 0.8)); // 判断车牌颜色以此确认threshold方法 Color plateType = getPlateType(tmpMat, true); Mat input_grey; cvtColor(input, input_grey, CV_BGR2GRAY); Mat img_threshold; // 二值化 // 根据车牌的不同颜色使用不同的阈值判断方法 // TODO:使用MSER来提取这些轮廓 if (BLUE == plateType) { // cout << "BLUE" << endl; img_threshold = input_grey.clone(); int w = input_grey.cols; int h = input_grey.rows; Mat tmp = input_grey(Rect_<double>(w * 0.1, h * 0.1, w * 0.8, h * 0.8)); int threadHoldV = ThresholdOtsu(tmp); threshold(input_grey, img_threshold, threadHoldV, 255, CV_THRESH_BINARY); } else if (YELLOW == plateType) { // cout << "YELLOW" << endl; img_threshold = input_grey.clone(); int w = input_grey.cols; int h = input_grey.rows; Mat tmp = input_grey(Rect_<double>(w * 0.1, h * 0.1, w * 0.8, h * 0.8)); int threadHoldV = ThresholdOtsu(tmp); // utils::imwrite("resources/image/tmp/inputgray2.jpg", input_grey); threshold(input_grey, img_threshold, threadHoldV, 255, CV_THRESH_BINARY_INV); } else if (WHITE == plateType) { // cout << "WHITE" << endl; threshold(input_grey, img_threshold, 10, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV); } else { // cout << "UNKNOWN" << endl; threshold(input_grey, img_threshold, 10, 255, CV_THRESH_OTSU + CV_THRESH_BINARY); } if (0) { imshow("threshold", img_threshold); waitKey(0); destroyWindow("threshold"); } if (m_debug) { stringstream ss(stringstream::in | stringstream::out); ss << "resources/image/tmp/debug_char_threshold_" << index << ".jpg"; utils::imwrite(ss.str(), img_threshold); } // 去除车牌上方的柳钉以及下方的横线等干扰 // 并且也判断了是否是车牌 // 并且在此对字符的跳变次数以及字符颜色所占的比重做了是否是车牌的判别条件 // 如果不是车牌,返回ErrorCode=0x02 if (!clearLiuDing(img_threshold)) return 0x02; if (m_debug) { stringstream ss(stringstream::in | stringstream::out); ss << "resources/image/tmp/debug_char_clearLiuDing_" << index << ".jpg"; utils::imwrite(ss.str(), img_threshold); } // 在二值化图像中提取轮廓 Mat img_contours; img_threshold.copyTo(img_contours); vector<vector<Point> > contours; findContours(img_contours, contours, // a vector of contours CV_RETR_EXTERNAL, // retrieve the external contours CV_CHAIN_APPROX_NONE); // all pixels of each contours vector<vector<Point> >::iterator itc = contours.begin(); vector<Rect> vecRect; // 将不符合特定尺寸的字符块排除出去 while (itc != contours.end()) { Rect mr = boundingRect(Mat(*itc)); Mat auxRoi(img_threshold, mr); if (verifyCharSizes(auxRoi)) vecRect.push_back(mr); ++itc; } // 如果找不到任何字符块,则返回ErrorCode=0x03 if (vecRect.size() == 0) return 0x03; // 对符合尺寸的图块按照从左到右进行排序; // 直接使用stl的sort方法,更有效率 vector<Rect> sortedRect(vecRect); std::sort(sortedRect.begin(), sortedRect.end(),[](const Rect& r1, const Rect& r2) { return r1.x < r2.x; }); size_t specIndex = 0; // 获得特殊字符对应的Rectt,如苏A的"A" specIndex = GetSpecificRect(sortedRect); if (m_debug) { if (specIndex < sortedRect.size()) { Mat specMat(img_threshold, sortedRect[specIndex]); stringstream ss(stringstream::in | stringstream::out); ss << "resources/image/tmp/debug_specMat_" << index << ".jpg"; utils::imwrite(ss.str(), specMat); } } // 根据特定Rect向左反推出中文字符 // 这样做的主要原因是根据findContours方法很难捕捉到中文字符的准确Rect,因此仅能 // 退过特定算法来指定 Rect chineseRect; if (specIndex < sortedRect.size()) chineseRect = GetChineseRect(sortedRect[specIndex]); else return 0x04; if (m_debug) { Mat chineseMat(img_threshold, chineseRect); stringstream ss(stringstream::in | stringstream::out); ss << "resources/image/tmp/debug_chineseMat_" << index << ".jpg"; utils::imwrite(ss.str(), chineseMat); } //新建一个全新的排序Rect //将中文字符Rect第一个加进来,因为它肯定是最左边的 //其余的Rect只按照顺序去6个,车牌只可能是7个字符!这样可以避免阴影导致的“1”字符 vector<Rect> newSortedRect; newSortedRect.push_back(chineseRect); RebuildRect(sortedRect, newSortedRect, specIndex); if (newSortedRect.size() == 0) return 0x05; // 开始截取每个字符 for (size_t i = 0; i < newSortedRect.size(); i++) { Rect mr = newSortedRect[i]; //Mat auxRoi(img_threshold, mr); // 使用灰度图来截取图块,然后依次对每个图块进行大津阈值来二值化 Mat auxRoi(input_grey, mr); Mat newRoi; if (BLUE == plateType) { /* img_threshold = auxRoi.clone(); int w = input_grey.cols; int h = input_grey.rows; Mat tmp = input_grey(Rect_<double>(w * 0.1, h * 0.1, w * 0.8, h * 0.8)); int threadHoldV = ThresholdOtsu(tmp);*/ threshold(auxRoi, newRoi, 5, 255, CV_THRESH_BINARY + CV_THRESH_OTSU); } else if (YELLOW == plateType) { threshold(auxRoi, newRoi, 5, 255, CV_THRESH_BINARY_INV + CV_THRESH_OTSU); } else if (WHITE == plateType) { threshold(auxRoi, newRoi, 5, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV); } else { threshold(auxRoi, newRoi, 5, 255, CV_THRESH_OTSU + CV_THRESH_BINARY); } // 归一化大小 newRoi = preprocessChar(newRoi); // 假设我们要重新训练ANN模型,在这里需要把训练样板输出 if (i == 0) { stringstream ss(stringstream::in | stringstream::out); ss << "resources/image/tmp/debug_char_auxRoi_" << index << "_" << (i) << ".jpg"; utils::imwrite(ss.str(), newRoi); } // 每个字符图块输入到下面的步骤进行处理 resultVec.push_back(newRoi); } return 0; }