| @@ -0,0 +1,24 @@ | |||
| // | |||
| // Created by Jack Yu on 21/10/2017. | |||
| // | |||
| #ifndef HYPERPR_CNNRECOGNIZER_H | |||
| #define HYPERPR_CNNRECOGNIZER_H | |||
| #include "Recognizer.h" | |||
| namespace pr { | |||
| class CNNRecognizer : public GeneralRecognizer { | |||
| public: | |||
| const int CHAR_INPUT_W = 14; | |||
| const int CHAR_INPUT_H = 30; | |||
| CNNRecognizer(std::string prototxt, std::string caffemodel); | |||
| label recognizeCharacter(cv::Mat character); | |||
| private: | |||
| cv::dnn::Net net; | |||
| }; | |||
| } // namespace pr | |||
| #endif // HYPERPR_CNNRECOGNIZER_H | |||
| @@ -0,0 +1,17 @@ | |||
| // | |||
| // Created by Jack Yu on 22/09/2017. | |||
| // | |||
| #ifndef HYPERPR_FASTDESKEW_H | |||
| #define HYPERPR_FASTDESKEW_H | |||
| #include <math.h> | |||
| #include <opencv2/opencv.hpp> | |||
| namespace pr { | |||
| cv::Mat fastdeskew(cv::Mat skewImage, int blockSize); | |||
| // cv::Mat spatialTransformer(cv::Mat skewImage); | |||
| } // namespace pr | |||
| #endif // HYPERPR_FASTDESKEW_H | |||
| @@ -0,0 +1,29 @@ | |||
| // | |||
| // Created by Jack Yu on 22/09/2017. | |||
| // | |||
| #ifndef HYPERPR_FINEMAPPING_H | |||
| #define HYPERPR_FINEMAPPING_H | |||
| #include <opencv2/dnn.hpp> | |||
| #include <opencv2/opencv.hpp> | |||
| #include <string> | |||
| namespace pr { | |||
| class FineMapping { | |||
| public: | |||
| FineMapping(); | |||
| FineMapping(std::string prototxt, std::string caffemodel); | |||
| static cv::Mat FineMappingVertical(cv::Mat InputProposal, int sliceNum = 15, | |||
| int upper = 0, int lower = -50, | |||
| int windows_size = 17); | |||
| cv::Mat FineMappingHorizon(cv::Mat FinedVertical, int leftPadding, | |||
| int rightPadding); | |||
| private: | |||
| cv::dnn::Net net; | |||
| }; | |||
| } // namespace pr | |||
| #endif // HYPERPR_FINEMAPPING_H | |||
| @@ -0,0 +1,54 @@ | |||
| // | |||
| // Created by Jack Yu on 22/10/2017. | |||
| // | |||
| #ifndef HYPERPR_PIPLINE_H | |||
| #define HYPERPR_PIPLINE_H | |||
| #include "CNNRecognizer.h" | |||
| #include "FastDeskew.h" | |||
| #include "FineMapping.h" | |||
| #include "PlateDetection.h" | |||
| #include "PlateInfo.h" | |||
| #include "PlateSegmentation.h" | |||
| #include "Recognizer.h" | |||
| #include "SegmentationFreeRecognizer.h" | |||
| namespace pr { | |||
| const std::vector<std::string> CH_PLATE_CODE{ | |||
| "京", "沪", "津", "渝", "冀", "晋", "蒙", "辽", "吉", "黑", "苏", "浙", | |||
| "皖", "闽", "赣", "鲁", "豫", "鄂", "湘", "粤", "桂", "琼", "川", "贵", | |||
| "云", "藏", "陕", "甘", "青", "宁", "新", "0", "1", "2", "3", "4", | |||
| "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", | |||
| "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", | |||
| "V", "W", "X", "Y", "Z", "港", "学", "使", "警", "澳", "挂", "军", | |||
| "北", "南", "广", "沈", "兰", "成", "济", "海", "民", "航", "空"}; | |||
| const int SEGMENTATION_FREE_METHOD = 0; | |||
| const int SEGMENTATION_BASED_METHOD = 1; | |||
| class PipelinePR { | |||
| public: | |||
| GeneralRecognizer *generalRecognizer; | |||
| PlateDetection *plateDetection; | |||
| PlateSegmentation *plateSegmentation; | |||
| FineMapping *fineMapping; | |||
| SegmentationFreeRecognizer *segmentationFreeRecognizer; | |||
| PipelinePR(std::string detector_filename, std::string finemapping_prototxt, | |||
| std::string finemapping_caffemodel, | |||
| std::string segmentation_prototxt, | |||
| std::string segmentation_caffemodel, | |||
| std::string charRecognization_proto, | |||
| std::string charRecognization_caffemodel, | |||
| std::string segmentationfree_proto, | |||
| std::string segmentationfree_caffemodel); | |||
| ~PipelinePR(); | |||
| std::vector<std::string> plateRes; | |||
| std::vector<PlateInfo> RunPiplineAsImage(cv::Mat plateImage, int method); | |||
| }; | |||
| } // namespace pr | |||
| #endif // HYPERPR_PIPLINE_H | |||
| @@ -0,0 +1,32 @@ | |||
| // | |||
| // Created by Jack Yu on 20/09/2017. | |||
| // | |||
| #ifndef HYPERPR_PLATEDETECTION_H | |||
| #define HYPERPR_PLATEDETECTION_H | |||
| #include <PlateInfo.h> | |||
| #include <opencv2/opencv.hpp> | |||
| #include <vector> | |||
| namespace pr { | |||
| class PlateDetection { | |||
| public: | |||
| PlateDetection(std::string filename_cascade); | |||
| PlateDetection(); | |||
| void LoadModel(std::string filename_cascade); | |||
| void plateDetectionRough(cv::Mat InputImage, | |||
| std::vector<pr::PlateInfo> &plateInfos, | |||
| int min_w = 36, int max_w = 800); | |||
| // std::vector<pr::PlateInfo> plateDetectionRough(cv::Mat | |||
| // InputImage,int min_w= 60,int max_h = 400); | |||
| // std::vector<pr::PlateInfo> | |||
| // plateDetectionRoughByMultiScaleEdge(cv::Mat InputImage); | |||
| private: | |||
| cv::CascadeClassifier cascade; | |||
| }; | |||
| } // namespace pr | |||
| #endif // HYPERPR_PLATEDETECTION_H | |||
| @@ -0,0 +1,94 @@ | |||
| // | |||
| // Created by Jack Yu on 20/09/2017. | |||
| // | |||
| #ifndef HYPERPR_PLATEINFO_H | |||
| #define HYPERPR_PLATEINFO_H | |||
| #include <opencv2/opencv.hpp> | |||
| namespace pr { | |||
| typedef std::vector<cv::Mat> Character; | |||
| enum PlateColor { BLUE, YELLOW, WHITE, GREEN, BLACK, UNKNOWN }; | |||
| enum CharType { CHINESE, LETTER, LETTER_NUMS, INVALID }; | |||
| class PlateInfo { | |||
| public: | |||
| std::vector<std::pair<CharType, cv::Mat>> plateChars; | |||
| std::vector<std::pair<CharType, cv::Mat>> plateCoding; | |||
| float confidence = 0; | |||
| PlateInfo(const cv::Mat &plateData, std::string plateName, cv::Rect plateRect, | |||
| PlateColor plateType) { | |||
| licensePlate = plateData; | |||
| name = plateName; | |||
| ROI = plateRect; | |||
| Type = plateType; | |||
| } | |||
| PlateInfo(const cv::Mat &plateData, cv::Rect plateRect, | |||
| PlateColor plateType) { | |||
| licensePlate = plateData; | |||
| ROI = plateRect; | |||
| Type = plateType; | |||
| } | |||
| PlateInfo(const cv::Mat &plateData, cv::Rect plateRect) { | |||
| licensePlate = plateData; | |||
| ROI = plateRect; | |||
| } | |||
| PlateInfo() {} | |||
| cv::Mat getPlateImage() { return licensePlate; } | |||
| void setPlateImage(cv::Mat plateImage) { licensePlate = plateImage; } | |||
| cv::Rect getPlateRect() { return ROI; } | |||
| void setPlateRect(cv::Rect plateRect) { ROI = plateRect; } | |||
| cv::String getPlateName() { return name; } | |||
| void setPlateName(cv::String plateName) { name = plateName; } | |||
| int getPlateType() { return Type; } | |||
| void appendPlateChar(const std::pair<CharType, cv::Mat> &plateChar) { | |||
| plateChars.push_back(plateChar); | |||
| } | |||
| void appendPlateCoding(const std::pair<CharType, cv::Mat> &charProb) { | |||
| plateCoding.push_back(charProb); | |||
| } | |||
| std::string decodePlateNormal(std::vector<std::string> mappingTable) { | |||
| std::string decode; | |||
| for (auto plate : plateCoding) { | |||
| float *prob = (float *)plate.second.data; | |||
| if (plate.first == CHINESE) { | |||
| decode += mappingTable[std::max_element(prob, prob + 31) - prob]; | |||
| confidence += *std::max_element(prob, prob + 31); | |||
| } | |||
| else if (plate.first == LETTER) { | |||
| decode += mappingTable[std::max_element(prob + 41, prob + 65) - prob]; | |||
| confidence += *std::max_element(prob + 41, prob + 65); | |||
| } | |||
| else if (plate.first == LETTER_NUMS) { | |||
| decode += mappingTable[std::max_element(prob + 31, prob + 65) - prob]; | |||
| confidence += *std::max_element(prob + 31, prob + 65); | |||
| } else if (plate.first == INVALID) { | |||
| decode += '*'; | |||
| } | |||
| } | |||
| name = decode; | |||
| confidence /= 7; | |||
| return decode; | |||
| } | |||
| private: | |||
| cv::Mat licensePlate; | |||
| cv::Rect ROI; | |||
| std::string name; | |||
| PlateColor Type; | |||
| }; | |||
| } // namespace pr | |||
| #endif // HYPERPR_PLATEINFO_H | |||
| @@ -0,0 +1,35 @@ | |||
| #ifndef HYPERPR_PLATESEGMENTATION_H | |||
| #define HYPERPR_PLATESEGMENTATION_H | |||
| #include "PlateInfo.h" | |||
| #include "opencv2/opencv.hpp" | |||
| #include <opencv2/dnn.hpp> | |||
| namespace pr { | |||
| class PlateSegmentation { | |||
| public: | |||
| const int PLATE_NORMAL = 6; | |||
| const int PLATE_NORMAL_GREEN = 7; | |||
| const int DEFAULT_WIDTH = 20; | |||
| PlateSegmentation(std::string phototxt, std::string caffemodel); | |||
| PlateSegmentation() {} | |||
| void segmentPlatePipline(PlateInfo &plateInfo, int stride, | |||
| std::vector<cv::Rect> &Char_rects); | |||
| void segmentPlateBySlidingWindows(cv::Mat &plateImage, int windowsWidth, | |||
| int stride, cv::Mat &respones); | |||
| void templateMatchFinding(const cv::Mat &respones, int windowsWidth, | |||
| std::pair<float, std::vector<int>> &candidatePts); | |||
| void refineRegion(cv::Mat &plateImage, const std::vector<int> &candidatePts, | |||
| const int padding, std::vector<cv::Rect> &rects); | |||
| void ExtractRegions(PlateInfo &plateInfo, std::vector<cv::Rect> &rects); | |||
| cv::Mat classifyResponse(const cv::Mat &cropped); | |||
| private: | |||
| cv::dnn::Net net; | |||
| }; | |||
| } // namespace pr | |||
| #endif // HYPERPR_PLATESEGMENTATION_H | |||
| @@ -0,0 +1,22 @@ | |||
| // | |||
| // Created by Jack Yu on 20/10/2017. | |||
| // | |||
| #ifndef HYPERPR_RECOGNIZER_H | |||
| #define HYPERPR_RECOGNIZER_H | |||
| #include "PlateInfo.h" | |||
| #include "opencv2/dnn.hpp" | |||
| namespace pr { | |||
| typedef cv::Mat label; | |||
| class GeneralRecognizer { | |||
| public: | |||
| virtual label recognizeCharacter(cv::Mat character) = 0; | |||
| // virtual cv::Mat SegmentationFreeForSinglePlate(cv::Mat plate) = | |||
| // 0; | |||
| void SegmentBasedSequenceRecognition(PlateInfo &plateinfo); | |||
| void SegmentationFreeSequenceRecognition(PlateInfo &plateInfo); | |||
| }; | |||
| } // namespace pr | |||
| #endif // HYPERPR_RECOGNIZER_H | |||
| @@ -0,0 +1,27 @@ | |||
| // | |||
| // Created by Jack Yu on 28/11/2017. | |||
| // | |||
| #ifndef HYPERPR_SEGMENTATIONFREERECOGNIZER_H | |||
| #define HYPERPR_SEGMENTATIONFREERECOGNIZER_H | |||
| #include "Recognizer.h" | |||
| namespace pr { | |||
| class SegmentationFreeRecognizer { | |||
| public: | |||
| const int CHAR_INPUT_W = 14; | |||
| const int CHAR_INPUT_H = 30; | |||
| const int CHAR_LEN = 84; | |||
| SegmentationFreeRecognizer(std::string prototxt, std::string caffemodel); | |||
| std::pair<std::string, float> | |||
| SegmentationFreeForSinglePlate(cv::Mat plate, | |||
| std::vector<std::string> mapping_table); | |||
| private: | |||
| cv::dnn::Net net; | |||
| }; | |||
| } // namespace pr | |||
| #endif // HYPERPR_SEGMENTATIONFREERECOGNIZER_H | |||
| @@ -0,0 +1,105 @@ | |||
| // | |||
| // Created by Jack Yu on 26/10/2017. | |||
| // | |||
| #ifndef HYPERPR_NIBLACKTHRESHOLD_H | |||
| #define HYPERPR_NIBLACKTHRESHOLD_H | |||
| #include <opencv2/opencv.hpp> | |||
| using namespace cv; | |||
| enum LocalBinarizationMethods { | |||
| BINARIZATION_NIBLACK = | |||
| 0, //!< Classic Niblack binarization. See @cite Niblack1985 . | |||
| BINARIZATION_SAUVOLA = 1, //!< Sauvola's technique. See @cite Sauvola1997 . | |||
| BINARIZATION_WOLF = 2, //!< Wolf's technique. See @cite Wolf2004 . | |||
| BINARIZATION_NICK = 3 //!< NICK technique. See @cite Khurshid2009 . | |||
| }; | |||
| void niBlackThreshold(InputArray _src, OutputArray _dst, double maxValue, | |||
| int type, int blockSize, double k, | |||
| int binarizationMethod) { | |||
| // Input grayscale image | |||
| Mat src = _src.getMat(); | |||
| CV_Assert(src.channels() == 1); | |||
| CV_Assert(blockSize % 2 == 1 && blockSize > 1); | |||
| if (binarizationMethod == BINARIZATION_SAUVOLA) { | |||
| CV_Assert(src.depth() == CV_8U); | |||
| } | |||
| type &= THRESH_MASK; | |||
| // Compute local threshold (T = mean + k * stddev) | |||
| // using mean and standard deviation in the neighborhood of each pixel | |||
| // (intermediate calculations are done with floating-point precision) | |||
| Mat test; | |||
| Mat thresh; | |||
| { | |||
| // note that: Var[X] = E[X^2] - E[X]^2 | |||
| Mat mean, sqmean, variance, stddev, sqrtVarianceMeanSum; | |||
| double srcMin, stddevMax; | |||
| boxFilter(src, mean, CV_32F, Size(blockSize, blockSize), Point(-1, -1), | |||
| true, BORDER_REPLICATE); | |||
| sqrBoxFilter(src, sqmean, CV_32F, Size(blockSize, blockSize), Point(-1, -1), | |||
| true, BORDER_REPLICATE); | |||
| variance = sqmean - mean.mul(mean); | |||
| sqrt(variance, stddev); | |||
| switch (binarizationMethod) { | |||
| case BINARIZATION_NIBLACK: | |||
| thresh = mean + stddev * static_cast<float>(k); | |||
| break; | |||
| case BINARIZATION_SAUVOLA: | |||
| thresh = mean.mul(1. + static_cast<float>(k) * (stddev / 128.0 - 1.)); | |||
| break; | |||
| case BINARIZATION_WOLF: | |||
| minMaxIdx(src, &srcMin, NULL); | |||
| minMaxIdx(stddev, NULL, &stddevMax); | |||
| thresh = | |||
| mean - static_cast<float>(k) * | |||
| (mean - srcMin - stddev.mul(mean - srcMin) / stddevMax); | |||
| break; | |||
| case BINARIZATION_NICK: | |||
| sqrt(variance + sqmean, sqrtVarianceMeanSum); | |||
| thresh = mean + static_cast<float>(k) * sqrtVarianceMeanSum; | |||
| break; | |||
| default: | |||
| CV_Error(CV_StsBadArg, "Unknown binarization method"); | |||
| break; | |||
| } | |||
| thresh.convertTo(thresh, src.depth()); | |||
| thresh.convertTo(test, src.depth()); | |||
| // | |||
| // cv::imshow("imagex",test); | |||
| // cv::waitKey(0); | |||
| } | |||
| // Prepare output image | |||
| _dst.create(src.size(), src.type()); | |||
| Mat dst = _dst.getMat(); | |||
| CV_Assert(src.data != dst.data); // no inplace processing | |||
| // Apply thresholding: ( pixel > threshold ) ? foreground : background | |||
| Mat mask; | |||
| switch (type) { | |||
| case THRESH_BINARY: // dst = (src > thresh) ? maxval : 0 | |||
| case THRESH_BINARY_INV: // dst = (src > thresh) ? 0 : maxval | |||
| compare(src, thresh, mask, (type == THRESH_BINARY ? CMP_GT : CMP_LE)); | |||
| dst.setTo(0); | |||
| dst.setTo(maxValue, mask); | |||
| break; | |||
| case THRESH_TRUNC: // dst = (src > thresh) ? thresh : src | |||
| compare(src, thresh, mask, CMP_GT); | |||
| src.copyTo(dst); | |||
| thresh.copyTo(dst, mask); | |||
| break; | |||
| case THRESH_TOZERO: // dst = (src > thresh) ? src : 0 | |||
| case THRESH_TOZERO_INV: // dst = (src > thresh) ? 0 : src | |||
| compare(src, thresh, mask, (type == THRESH_TOZERO ? CMP_GT : CMP_LE)); | |||
| dst.setTo(0); | |||
| src.copyTo(dst, mask); | |||
| break; | |||
| default: | |||
| CV_Error(CV_StsBadArg, "Unknown threshold type"); | |||
| break; | |||
| } | |||
| } | |||
| #endif // HYPERPR_NIBLACKTHRESHOLD_H | |||