diff --git a/Prj-PHP/CMakeLists.txt b/Prj-PHP/CMakeLists.txt new file mode 100644 index 0000000..7116b24 --- /dev/null +++ b/Prj-PHP/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 3.6) +project(platescan) + +set(OpenCV_DIR /usr/platescan/opencv-3.3.0/release/) +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -g -O2 -std=c++11 -fpic -o") +#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + +set(CMAKE_CXX_STANDARD 11) + +set(PHP_CONFIG_DIR /etc/php.d/) +set(PHP_CONFIG php-config) +set(POST_COMPILE_COMMAND systemctl restart php-fpm) + +find_package(OpenCV 3.3.0 REQUIRED) +include_directories( ${OpenCV_INCLUDE_DIRS}) +include_directories(include) + + +add_library( # Sets the name of the library. + platescan + + # Sets the library as a shared library. + SHARED + + # Provides a relative path to your source file(s). + main.cpp + + lpr/CNNRecognizer.cpp + lpr/FastDeskew.cpp + lpr/FineMapping.cpp + lpr/Pipeline.cpp + lpr/PlateDetection.cpp + lpr/PlateSegmentation.cpp + lpr/Recognizer.cpp + lpr/SegmentationFreeRecognizer.cpp +) + + +target_link_libraries( + platescan + phpcpp + ${OpenCV_LIBS} +) + +execute_process(COMMAND ${PHP_CONFIG} --extension-dir + OUTPUT_VARIABLE LIBRARY_DIR) + +message("Test ${LIBRARY_DIR} failed.") + +add_custom_command(TARGET platescan + POST_BUILD + COMMAND mv ./libplatescan.so ./platescan.so + COMMAND cp -f ./platescan.so ${LIBRARY_DIR} + COMMAND ${POST_COMPILE_COMMAND} + ) diff --git a/Prj-PHP/README.MD b/Prj-PHP/README.MD new file mode 100644 index 0000000..6583dae --- /dev/null +++ b/Prj-PHP/README.MD @@ -0,0 +1,16 @@ +# Prj-PHP +HyperLPR 在PHP扩展程序中的实现,核心代码拷贝了 Prj-Linux 中庾金科大牛的代码。我做的这部分工作主要是配置编译成PHP扩展程序。 + +```php +$path = realpath("demo.png"); +$res = platescan($path); +var_dump($res); // string(9) "苏ED0N19" + +``` + +最终实现上边这样的PHP调用 + +![image](./tests/demo.png) + + +#### 感谢各位大牛! \ No newline at end of file diff --git a/Prj-PHP/include/CNNRecognizer.h b/Prj-PHP/include/CNNRecognizer.h new file mode 100644 index 0000000..ad491a0 --- /dev/null +++ b/Prj-PHP/include/CNNRecognizer.h @@ -0,0 +1,24 @@ +// +// Created by 庾金科 on 21/10/2017. +// + +#ifndef SWIFTPR_CNNRECOGNIZER_H +#define SWIFTPR_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; + + }; + +} + +#endif //SWIFTPR_CNNRECOGNIZER_H diff --git a/Prj-PHP/include/FastDeskew.h b/Prj-PHP/include/FastDeskew.h new file mode 100644 index 0000000..08359e5 --- /dev/null +++ b/Prj-PHP/include/FastDeskew.h @@ -0,0 +1,18 @@ +// +// Created by 庾金科 on 22/09/2017. +// + +#ifndef SWIFTPR_FASTDESKEW_H +#define SWIFTPR_FASTDESKEW_H + +#include +#include +namespace pr{ + + cv::Mat fastdeskew(cv::Mat skewImage,int blockSize); +// cv::Mat spatialTransformer(cv::Mat skewImage); + +}//namepace pr + + +#endif //SWIFTPR_FASTDESKEW_H diff --git a/Prj-PHP/include/FineMapping.h b/Prj-PHP/include/FineMapping.h new file mode 100644 index 0000000..352202e --- /dev/null +++ b/Prj-PHP/include/FineMapping.h @@ -0,0 +1,32 @@ +// +// Created by 庾金科 on 22/09/2017. +// + +#ifndef SWIFTPR_FINEMAPPING_H +#define SWIFTPR_FINEMAPPING_H + +#include +#include + +#include +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; + + }; + + + + +} +#endif //SWIFTPR_FINEMAPPING_H diff --git a/Prj-PHP/include/Pipeline.h b/Prj-PHP/include/Pipeline.h new file mode 100644 index 0000000..4e82955 --- /dev/null +++ b/Prj-PHP/include/Pipeline.h @@ -0,0 +1,60 @@ +// +// Created by 庾金科 on 22/10/2017. +// + +#ifndef SWIFTPR_PIPLINE_H +#define SWIFTPR_PIPLINE_H + +#include "PlateDetection.h" +#include "PlateSegmentation.h" +#include "CNNRecognizer.h" +#include "PlateInfo.h" +#include "FastDeskew.h" +#include "FineMapping.h" +#include "Recognizer.h" +#include "SegmentationFreeRecognizer.h" + +namespace pr{ + + const std::vector 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 plateRes; + std::vector RunPiplineAsImage(cv::Mat plateImage,int method); + + + + + + + + }; + + +} +#endif //SWIFTPR_PIPLINE_H diff --git a/Prj-PHP/include/PlateDetection.h b/Prj-PHP/include/PlateDetection.h new file mode 100644 index 0000000..71ad9af --- /dev/null +++ b/Prj-PHP/include/PlateDetection.h @@ -0,0 +1,33 @@ +// +// Created by 庾金科 on 20/09/2017. +// + +#ifndef SWIFTPR_PLATEDETECTION_H +#define SWIFTPR_PLATEDETECTION_H + +#include +#include +#include +namespace pr{ + class PlateDetection{ + public: + PlateDetection(std::string filename_cascade); + PlateDetection(); + void LoadModel(std::string filename_cascade); + void plateDetectionRough(cv::Mat InputImage,std::vector &plateInfos,int min_w=36,int max_w=800); +// std::vector plateDetectionRough(cv::Mat InputImage,int min_w= 60,int max_h = 400); + + +// std::vector plateDetectionRoughByMultiScaleEdge(cv::Mat InputImage); + + + + private: + cv::CascadeClassifier cascade; + + + }; + +}// namespace pr + +#endif //SWIFTPR_PLATEDETECTION_H diff --git a/Prj-PHP/include/PlateInfo.h b/Prj-PHP/include/PlateInfo.h new file mode 100644 index 0000000..f500bb5 --- /dev/null +++ b/Prj-PHP/include/PlateInfo.h @@ -0,0 +1,126 @@ +// +// Created by 庾金科 on 20/09/2017. +// + +#ifndef SWIFTPR_PLATEINFO_H +#define SWIFTPR_PLATEINFO_H +#include +namespace pr { + + typedef std::vector Character; + + enum PlateColor { BLUE, YELLOW, WHITE, GREEN, BLACK,UNKNOWN}; + enum CharType {CHINESE,LETTER,LETTER_NUMS,INVALID}; + + + class PlateInfo { + public: + std::vector> plateChars; + std::vector> 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 &plateChar) + { + plateChars.push_back(plateChar); + } + + void appendPlateCoding(const std::pair &charProb){ + plateCoding.push_back(charProb); + } + + // cv::Mat getPlateChars(int id) { + // if(id 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); + + +// std::cout<<*std::max_element(prob,prob+31)< +#include "PlateInfo.h" + +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 &Char_rects); + + void segmentPlateBySlidingWindows(cv::Mat &plateImage,int windowsWidth,int stride,cv::Mat &respones); + void templateMatchFinding(const cv::Mat &respones,int windowsWidth,std::pair> &candidatePts); + void refineRegion(cv::Mat &plateImage,const std::vector &candidatePts,const int padding,std::vector &rects); + void ExtractRegions(PlateInfo &plateInfo,std::vector &rects); + cv::Mat classifyResponse(const cv::Mat &cropped); + private: + cv::dnn::Net net; + + +// RefineRegion() + + }; + +}//namespace pr + +#endif //SWIFTPR_PLATESEGMENTATION_H diff --git a/Prj-PHP/include/Recognizer.h b/Prj-PHP/include/Recognizer.h new file mode 100644 index 0000000..556e42e --- /dev/null +++ b/Prj-PHP/include/Recognizer.h @@ -0,0 +1,23 @@ +// +// Created by 庾金科 on 20/10/2017. +// + + +#ifndef SWIFTPR_RECOGNIZER_H +#define SWIFTPR_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); + + }; + +} +#endif //SWIFTPR_RECOGNIZER_H diff --git a/Prj-PHP/include/SegmentationFreeRecognizer.h b/Prj-PHP/include/SegmentationFreeRecognizer.h new file mode 100644 index 0000000..583899e --- /dev/null +++ b/Prj-PHP/include/SegmentationFreeRecognizer.h @@ -0,0 +1,28 @@ +// +// Created by 庾金科 on 28/11/2017. +// + +#ifndef SWIFTPR_SEGMENTATIONFREERECOGNIZER_H +#define SWIFTPR_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 SegmentationFreeForSinglePlate(cv::Mat plate,std::vector mapping_table); + + + private: + cv::dnn::Net net; + + }; + +} +#endif //SWIFTPR_SEGMENTATIONFREERECOGNIZER_H diff --git a/Prj-PHP/include/niBlackThreshold.h b/Prj-PHP/include/niBlackThreshold.h new file mode 100644 index 0000000..5ad7e14 --- /dev/null +++ b/Prj-PHP/include/niBlackThreshold.h @@ -0,0 +1,107 @@ +// +// Created by 庾金科 on 26/10/2017. +// + +#ifndef SWIFTPR_NIBLACKTHRESHOLD_H +#define SWIFTPR_NIBLACKTHRESHOLD_H + + +#include +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(k); + + break; + case BINARIZATION_SAUVOLA: + thresh = mean.mul(1. + static_cast(k) * (stddev / 128.0 - 1.)); + break; + case BINARIZATION_WOLF: + minMaxIdx(src, &srcMin,NULL); + minMaxIdx(stddev, NULL, &stddevMax); + thresh = mean - static_cast(k) * (mean - srcMin - stddev.mul(mean - srcMin) / stddevMax); + break; + case BINARIZATION_NICK: + sqrt(variance + sqmean, sqrtVarianceMeanSum); + thresh = mean + static_cast(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 //SWIFTPR_NIBLACKTHRESHOLD_H diff --git a/Prj-PHP/lpr/CNNRecognizer.cpp b/Prj-PHP/lpr/CNNRecognizer.cpp new file mode 100644 index 0000000..ff0991e --- /dev/null +++ b/Prj-PHP/lpr/CNNRecognizer.cpp @@ -0,0 +1,19 @@ +// +// Created by 庾金科 on 21/10/2017. +// + +#include "CNNRecognizer.h" + +namespace pr{ + CNNRecognizer::CNNRecognizer(std::string prototxt,std::string caffemodel){ + net = cv::dnn::readNetFromCaffe(prototxt, caffemodel); + } + + label CNNRecognizer::recognizeCharacter(cv::Mat charImage){ + if(charImage.channels()== 3) + cv::cvtColor(charImage,charImage,cv::COLOR_BGR2GRAY); + cv::Mat inputBlob = cv::dnn::blobFromImage(charImage, 1/255.0, cv::Size(CHAR_INPUT_W,CHAR_INPUT_H), cv::Scalar(0,0,0),false); + net.setInput(inputBlob,"data"); + return net.forward(); + } +} \ No newline at end of file diff --git a/Prj-PHP/lpr/FastDeskew.cpp b/Prj-PHP/lpr/FastDeskew.cpp new file mode 100644 index 0000000..5da3644 --- /dev/null +++ b/Prj-PHP/lpr/FastDeskew.cpp @@ -0,0 +1,108 @@ +// +// Created by Jack Yu on 02/10/2017. +// + + + +#include "FastDeskew.h" + +namespace pr{ + const int ANGLE_MIN = 30 ; + const int ANGLE_MAX = 150 ; + const int PLATE_H = 36; + const int PLATE_W = 136; + + int angle(float x,float y) + { + return atan2(x,y)*180/3.1415; + } + + std::vector avgfilter(std::vector angle_list,int windowsSize) { + std::vector angle_list_filtered(angle_list.size() - windowsSize + 1); + for (int i = 0; i < angle_list.size() - windowsSize + 1; i++) { + float avg = 0.00f; + for (int j = 0; j < windowsSize; j++) { + avg += angle_list[i + j]; + } + avg = avg / windowsSize; + angle_list_filtered[i] = avg; + } + + return angle_list_filtered; + } + + + void drawHist(std::vector seq){ + cv::Mat image(300,seq.size(),CV_8U); + image.setTo(0); + + for(int i = 0;i(skewPlate.rows*tan(cv::abs(angle)/180* 3.14) ); + cv::Size size(skewPlate.cols + extend_padding ,skewPlate.rows); + float interval = abs(sin((angle /180) * 3.14)* skewPlate.rows); + cv::Point2f pts1[4] = {cv::Point2f(0,0),cv::Point2f(0,size_o.height),cv::Point2f(size_o.width,0),cv::Point2f(size_o.width,size_o.height)}; + if(angle>0) { + cv::Point2f pts2[4] = {cv::Point2f(interval, 0), cv::Point2f(0, size_o.height), + cv::Point2f(size_o.width, 0), cv::Point2f(size_o.width - interval, size_o.height)}; + cv::Mat M = cv::getPerspectiveTransform(pts1,pts2); + cv::warpPerspective(skewPlate,dst,M,size); + } + else { + cv::Point2f pts2[4] = {cv::Point2f(0, 0), cv::Point2f(interval, size_o.height), cv::Point2f(size_o.width-interval, 0), + cv::Point2f(size_o.width, size_o.height)}; + cv::Mat M = cv::getPerspectiveTransform(pts1,pts2); + cv::warpPerspective(skewPlate,dst,M,size,cv::INTER_CUBIC); + } + return dst; + } + cv::Mat fastdeskew(cv::Mat skewImage,int blockSize){ + const int FILTER_WINDOWS_SIZE = 5; + std::vector angle_list(180); + memset(angle_list.data(),0,angle_list.size()*sizeof(int)); + cv::Mat bak; + skewImage.copyTo(bak); + if(skewImage.channels() == 3) + cv::cvtColor(skewImage,skewImage,cv::COLOR_RGB2GRAY); + if(skewImage.channels() == 1) + { + cv::Mat eigen; + cv::cornerEigenValsAndVecs(skewImage,eigen,blockSize,5); + for( int j = 0; j < skewImage.rows; j+=blockSize ) + { for( int i = 0; i < skewImage.cols; i+=blockSize ) + { + float x2 = eigen.at(j, i)[4]; + float y2 = eigen.at(j, i)[5]; + int angle_cell = angle(x2,y2); + angle_list[(angle_cell + 180)%180]+=1.0; + } + } + } + std::vector filtered = avgfilter(angle_list,5); + int maxPos = std::max_element(filtered.begin(),filtered.end()) - filtered.begin() + FILTER_WINDOWS_SIZE/2; + if(maxPos>ANGLE_MAX) + maxPos = (-maxPos+90+180)%180; + if(maxPos(maxPos),60.0f); + return deskewed; + } + + + +}//namespace pr diff --git a/Prj-PHP/lpr/FineMapping.cpp b/Prj-PHP/lpr/FineMapping.cpp new file mode 100644 index 0000000..2032b15 --- /dev/null +++ b/Prj-PHP/lpr/FineMapping.cpp @@ -0,0 +1,170 @@ +#include "FineMapping.h" +namespace pr{ + + const int FINEMAPPING_H = 60 ; + const int FINEMAPPING_W = 140; + const int PADDING_UP_DOWN = 30; + void drawRect(cv::Mat image,cv::Rect rect) + { + cv::Point p1(rect.x,rect.y); + cv::Point p2(rect.x+rect.width,rect.y+rect.height); + cv::rectangle(image,p1,p2,cv::Scalar(0,255,0),1); + } + + + FineMapping::FineMapping(std::string prototxt,std::string caffemodel) { + net = cv::dnn::readNetFromCaffe(prototxt, caffemodel); + + } + + cv::Mat FineMapping::FineMappingHorizon(cv::Mat FinedVertical,int leftPadding,int rightPadding) + { + +// if(FinedVertical.channels()==1) +// cv::cvtColor(FinedVertical,FinedVertical,cv::COLOR_GRAY2BGR); + cv::Mat inputBlob = cv::dnn::blobFromImage(FinedVertical, 1/255.0, cv::Size(66,16), + cv::Scalar(0,0,0),false); + + net.setInput(inputBlob,"data"); + cv::Mat prob = net.forward(); + int front = static_cast(prob.at(0,0)*FinedVertical.cols); + int back = static_cast(prob.at(0,1)*FinedVertical.cols); + front -= leftPadding ; + if(front<0) front = 0; + back +=rightPadding; + if(back>FinedVertical.cols-1) back=FinedVertical.cols - 1; + cv::Mat cropped = FinedVertical.colRange(front,back).clone(); + return cropped; + + + } + std::pair FitLineRansac(std::vector pts,int zeroadd = 0 ) + { + std::pair res; + if(pts.size()>2) + { + cv::Vec4f line; + cv::fitLine(pts,line,cv::DIST_HUBER,0,0.01,0.01); + float vx = line[0]; + float vy = line[1]; + float x = line[2]; + float y = line[3]; + int lefty = static_cast((-x * vy / vx) + y); + int righty = static_cast(((136- x) * vy / vx) + y); + res.first = lefty+PADDING_UP_DOWN+zeroadd; + res.second = righty+PADDING_UP_DOWN+zeroadd; + return res; + } + res.first = zeroadd; + res.second = zeroadd; + return res; + } + + cv::Mat FineMapping::FineMappingVertical(cv::Mat InputProposal,int sliceNum,int upper,int lower,int windows_size){ + cv::Mat PreInputProposal; + cv::Mat proposal; + cv::resize(InputProposal,PreInputProposal,cv::Size(FINEMAPPING_W,FINEMAPPING_H)); + if(InputProposal.channels() == 3) + cv::cvtColor(PreInputProposal,proposal,cv::COLOR_BGR2GRAY); + else + PreInputProposal.copyTo(proposal); + // this will improve some sen + cv::Mat kernal = cv::getStructuringElement(cv::MORPH_ELLIPSE,cv::Size(1,3)); + float diff = static_cast(upper-lower); + diff/=static_cast(sliceNum-1); + cv::Mat binary_adaptive; + std::vector line_upper; + std::vector line_lower; + int contours_nums=0; + for(int i = 0 ; i < sliceNum ; i++) + { + std::vector > contours; + float k =lower + i*diff; + cv::adaptiveThreshold(proposal,binary_adaptive,255,cv::ADAPTIVE_THRESH_MEAN_C,cv::THRESH_BINARY,windows_size,k); + cv::Mat draw; + binary_adaptive.copyTo(draw); + cv::findContours(binary_adaptive,contours,cv::RETR_EXTERNAL,cv::CHAIN_APPROX_SIMPLE); + for(auto contour: contours) + { + cv::Rect bdbox =cv::boundingRect(contour); + float lwRatio = bdbox.height/static_cast(bdbox.width); + int bdboxAera = bdbox.width*bdbox.height; + if (( lwRatio>0.7&&bdbox.width*bdbox.height>100 && bdboxAera<300) + || (lwRatio>3.0 && bdboxAera<100 && bdboxAera>10)) + { + cv::Point p1(bdbox.x, bdbox.y); + cv::Point p2(bdbox.x + bdbox.width, bdbox.y + bdbox.height); + line_upper.push_back(p1); + line_lower.push_back(p2); + contours_nums+=1; + } + } + } + if(contours_nums<41) + { + cv::bitwise_not(InputProposal,InputProposal); + cv::Mat kernal = cv::getStructuringElement(cv::MORPH_ELLIPSE,cv::Size(1,5)); + cv::Mat bak; + cv::resize(InputProposal,bak,cv::Size(FINEMAPPING_W,FINEMAPPING_H)); + cv::erode(bak,bak,kernal); + if(InputProposal.channels() == 3) + cv::cvtColor(bak,proposal,cv::COLOR_BGR2GRAY); + else + proposal = bak; + int contours_nums=0; + for(int i = 0 ; i < sliceNum ; i++) + { + std::vector > contours; + float k =lower + i*diff; + cv::adaptiveThreshold(proposal,binary_adaptive,255,cv::ADAPTIVE_THRESH_MEAN_C,cv::THRESH_BINARY,windows_size,k); + cv::Mat draw; + binary_adaptive.copyTo(draw); + cv::findContours(binary_adaptive,contours,cv::RETR_EXTERNAL,cv::CHAIN_APPROX_SIMPLE); + for(auto contour: contours) + { + cv::Rect bdbox =cv::boundingRect(contour); + float lwRatio = bdbox.height/static_cast(bdbox.width); + int bdboxAera = bdbox.width*bdbox.height; + if (( lwRatio>0.7&&bdbox.width*bdbox.height>120 && bdboxAera<300) + || (lwRatio>3.0 && bdboxAera<100 && bdboxAera>10)) + { + + cv::Point p1(bdbox.x, bdbox.y); + cv::Point p2(bdbox.x + bdbox.width, bdbox.y + bdbox.height); + line_upper.push_back(p1); + line_lower.push_back(p2); + contours_nums+=1; + } + } + } + } + cv::Mat rgb; + cv::copyMakeBorder(PreInputProposal, rgb, PADDING_UP_DOWN, PADDING_UP_DOWN, 0, 0, cv::BORDER_REPLICATE); + std::pair A; + std::pair B; + A = FitLineRansac(line_upper, -1); + B = FitLineRansac(line_lower, 1); + int leftyB = A.first; + int rightyB = A.second; + int leftyA = B.first; + int rightyA = B.second; + int cols = rgb.cols; + int rows = rgb.rows; + std::vector corners(4); + corners[0] = cv::Point2f(cols - 1, rightyA); + corners[1] = cv::Point2f(0, leftyA); + corners[2] = cv::Point2f(cols - 1, rightyB); + corners[3] = cv::Point2f(0, leftyB); + std::vector corners_trans(4); + corners_trans[0] = cv::Point2f(136, 36); + corners_trans[1] = cv::Point2f(0, 36); + corners_trans[2] = cv::Point2f(136, 0); + corners_trans[3] = cv::Point2f(0, 0); + cv::Mat transform = cv::getPerspectiveTransform(corners, corners_trans); + cv::Mat quad = cv::Mat::zeros(36, 136, CV_8UC3); + cv::warpPerspective(rgb, quad, transform, quad.size()); + return quad; + } +} + + diff --git a/Prj-PHP/lpr/Pipeline.cpp b/Prj-PHP/lpr/Pipeline.cpp new file mode 100644 index 0000000..f4eb8b2 --- /dev/null +++ b/Prj-PHP/lpr/Pipeline.cpp @@ -0,0 +1,101 @@ +// +// Created by 庾金科 on 23/10/2017. +// + +#include "Pipeline.h" + + +namespace pr { + + + + const int HorizontalPadding = 4; + PipelinePR::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) { + plateDetection = new PlateDetection(detector_filename); + fineMapping = new FineMapping(finemapping_prototxt, finemapping_caffemodel); + plateSegmentation = new PlateSegmentation(segmentation_prototxt, segmentation_caffemodel); + generalRecognizer = new CNNRecognizer(charRecognization_proto, charRecognization_caffemodel); + segmentationFreeRecognizer = new SegmentationFreeRecognizer(segmentationfree_proto,segmentationfree_caffemodel); + + } + + PipelinePR::~PipelinePR() { + + delete plateDetection; + delete fineMapping; + delete plateSegmentation; + delete generalRecognizer; + delete segmentationFreeRecognizer; + + + } + + std::vector PipelinePR:: RunPiplineAsImage(cv::Mat plateImage,int method) { + std::vector results; + std::vector plates; + plateDetection->plateDetectionRough(plateImage,plates,36,700); + + for (pr::PlateInfo plateinfo:plates) { + + cv::Mat image_finemapping = plateinfo.getPlateImage(); + image_finemapping = fineMapping->FineMappingVertical(image_finemapping); + image_finemapping = pr::fastdeskew(image_finemapping, 5); + + + + //Segmentation-based + + if(method==SEGMENTATION_BASED_METHOD) + { + image_finemapping = fineMapping->FineMappingHorizon(image_finemapping, 2, HorizontalPadding); + cv::resize(image_finemapping, image_finemapping, cv::Size(136+HorizontalPadding, 36)); +// cv::imshow("image_finemapping",image_finemapping); +// cv::waitKey(0); + plateinfo.setPlateImage(image_finemapping); + std::vector rects; + + plateSegmentation->segmentPlatePipline(plateinfo, 1, rects); + plateSegmentation->ExtractRegions(plateinfo, rects); + cv::copyMakeBorder(image_finemapping, image_finemapping, 0, 0, 0, 20, cv::BORDER_REPLICATE); + plateinfo.setPlateImage(image_finemapping); + generalRecognizer->SegmentBasedSequenceRecognition(plateinfo); + plateinfo.decodePlateNormal(pr::CH_PLATE_CODE); + + } + //Segmentation-free + else if(method==SEGMENTATION_FREE_METHOD) + { + + image_finemapping = fineMapping->FineMappingHorizon(image_finemapping, 4, HorizontalPadding+3); + + cv::resize(image_finemapping, image_finemapping, cv::Size(136+HorizontalPadding, 36)); +// cv::imwrite("./test.png",image_finemapping); +// cv::imshow("image_finemapping",image_finemapping); +// cv::waitKey(0); + plateinfo.setPlateImage(image_finemapping); +// std::vector rects; + + std::pair res = segmentationFreeRecognizer->SegmentationFreeForSinglePlate(plateinfo.getPlateImage(),pr::CH_PLATE_CODE); + plateinfo.confidence = res.second; + plateinfo.setPlateName(res.first); + } + + + + results.push_back(plateinfo); + } + +// for (auto str:results) { +// std::cout << str << std::endl; +// } + return results; + + }//namespace pr + + + +} \ No newline at end of file diff --git a/Prj-PHP/lpr/PlateDetection.cpp b/Prj-PHP/lpr/PlateDetection.cpp new file mode 100644 index 0000000..cb3eeef --- /dev/null +++ b/Prj-PHP/lpr/PlateDetection.cpp @@ -0,0 +1,32 @@ +#include "PlateDetection.h" +#include "util.h" +namespace pr{ + PlateDetection::PlateDetection(std::string filename_cascade){ + cascade.load(filename_cascade); + + }; + void PlateDetection::plateDetectionRough(cv::Mat InputImage,std::vector &plateInfos,int min_w,int max_w){ + cv::Mat processImage; + cv::cvtColor(InputImage,processImage,cv::COLOR_BGR2GRAY); + std::vector platesRegions; + cv::Size minSize(min_w,min_w/4); + cv::Size maxSize(max_w,max_w/4); + cascade.detectMultiScale( processImage, platesRegions, + 1.1, 3, cv::CASCADE_SCALE_IMAGE,minSize,maxSize); + for(auto plate:platesRegions) + { + int zeroadd_w = static_cast(plate.width*0.30); + int zeroadd_h = static_cast(plate.height*2); + int zeroadd_x = static_cast(plate.width*0.15); + int zeroadd_y = static_cast(plate.height*1); + plate.x-=zeroadd_x; + plate.y-=zeroadd_y; + plate.height += zeroadd_h; + plate.width += zeroadd_w; + cv::Mat plateImage = util::cropFromImage(InputImage,plate); + PlateInfo plateInfo(plateImage,plate); + plateInfos.push_back(plateInfo); + + } + } +}//namespace pr diff --git a/Prj-PHP/lpr/PlateSegmentation.cpp b/Prj-PHP/lpr/PlateSegmentation.cpp new file mode 100644 index 0000000..69e1aeb --- /dev/null +++ b/Prj-PHP/lpr/PlateSegmentation.cpp @@ -0,0 +1,404 @@ +// +// Created by 庾金科 on 16/10/2017. +// + +#include "PlateSegmentation.h" +#include "niBlackThreshold.h" + + +//#define DEBUG +namespace pr{ + + PlateSegmentation::PlateSegmentation(std::string prototxt,std::string caffemodel) { + net = cv::dnn::readNetFromCaffe(prototxt, caffemodel); + } + cv::Mat PlateSegmentation::classifyResponse(const cv::Mat &cropped){ + cv::Mat inputBlob = cv::dnn::blobFromImage(cropped, 1/255.0, cv::Size(22,22), cv::Scalar(0,0,0),false); + net.setInput(inputBlob,"data"); + return net.forward(); + } + + void drawHist(float* seq,int size,const char* name){ + cv::Mat image(300,size,CV_8U); + image.setTo(0); + float* start =seq; + float* end = seq+size; + float l = *std::max_element(start,end); + for(int i = 0;i>1),rect.y + (rect.height>>1)); + int rebuildLeft = (rect.width>>1 )+ left; + int rebuildRight = (rect.width>>1 )+ right; + int rebuildTop = (rect.height>>1 )+ top; + int rebuildBottom = (rect.height>>1 )+ bottom; + return boxFromCenter(center,rebuildLeft,rebuildRight,rebuildTop,rebuildBottom,bdSize); + + } + + + + void PlateSegmentation:: refineRegion(cv::Mat &plateImage,const std::vector &candidatePts,const int padding,std::vector &rects){ + int w = candidatePts[5] - candidatePts[4]; + int cols = plateImage.cols; + int rows = plateImage.rows; + for(int i = 0 ; i < candidatePts.size() ; i++) + { + int left = 0; + int right = 0 ; + + if(i == 0 ){ + left= candidatePts[i]; + right = left+w+padding; + } + else { + left = candidatePts[i] - padding; + right = left + w + padding * 2; + } + + computeSafeMargin(right,cols); + computeSafeMargin(left,cols); + cv::Rect roi(left,0,right - left,rows-1); + cv::Mat roiImage; + plateImage(roi).copyTo(roiImage); + + if (i>=1) + { + + cv::Mat roi_thres; +// cv::threshold(roiImage,roi_thres,0,255,cv::THRESH_OTSU|cv::THRESH_BINARY); + + niBlackThreshold(roiImage,roi_thres,255,cv::THRESH_BINARY,15,0.27,BINARIZATION_NIBLACK); + + std::vector> contours; + cv::findContours(roi_thres,contours,cv::RETR_LIST,cv::CHAIN_APPROX_SIMPLE); + cv::Point boxCenter(roiImage.cols>>1,roiImage.rows>>1); + + cv::Rect final_bdbox; + cv::Point final_center; + int final_dist = INT_MAX; + + + for(auto contour:contours) + { + cv::Rect bdbox = cv::boundingRect(contour); + cv::Point center(bdbox.x+(bdbox.width>>1),bdbox.y + (bdbox.height>>1)); + int dist = (center.x - boxCenter.x)*(center.x - boxCenter.x); + if(dist rows>>1) + { final_dist =dist; + final_center = center; + final_bdbox = bdbox; + } + } + + //rebuild box + if(final_bdbox.height/ static_cast(final_bdbox.width) > 3.5 && final_bdbox.width*final_bdbox.height<10) + final_bdbox = boxFromCenter(final_center,8,8,(rows>>1)-3 , (rows>>1) - 2,roiImage.size()); + else { + if(i == candidatePts.size()-1) + final_bdbox = boxPadding(final_bdbox, padding/2, padding, padding/2, padding/2, roiImage.size()); + else + final_bdbox = boxPadding(final_bdbox, padding, padding, padding, padding, roiImage.size()); + + +// std::cout<0&&i+j+r> &candidatePts){ + int rows = respones.rows; + int cols = respones.cols; + + + + float *data = (float*)respones.data; + float *engNum_prob = data; + float *false_prob = data+cols; + float *ch_prob = data+cols*2; + + avgfilter(engNum_prob,cols,5); + avgfilter(false_prob,cols,5); +// avgfilter(ch_prob,cols,5); + std::vector candidate_pts(7); +#ifdef DEBUG + drawHist(engNum_prob,cols,"engNum_prob"); + drawHist(false_prob,cols,"false_prob"); + drawHist(ch_prob,cols,"ch_prob"); + cv::waitKey(0); +#endif + + + + + int cp_list[7]; + float loss_selected = -10; + + for(int start = 0 ; start < 20 ; start+=2) + for(int width = windowsWidth-5; width < windowsWidth+5 ; width++ ){ + for(int interval = windowsWidth/2; interval < windowsWidth; interval++) + { + int cp1_ch = start; + int cp2_p0 = cp1_ch+ width; + int cp3_p1 = cp2_p0+ width + interval; + int cp4_p2 = cp3_p1 + width; + int cp5_p3 = cp4_p2 + width+1; + int cp6_p4 = cp5_p3 + width+2; + int cp7_p5= cp6_p4+ width+2; + + int md1 = (cp1_ch+cp2_p0)>>1; + int md2 = (cp2_p0+cp3_p1)>>1; + int md3 = (cp3_p1+cp4_p2)>>1; + int md4 = (cp4_p2+cp5_p3)>>1; + int md5 = (cp5_p3+cp6_p4)>>1; + int md6 = (cp6_p4+cp7_p5)>>1; + + + + + if(cp7_p5>=cols) + continue; +// float loss = ch_prob[cp1_ch]+ +// engNum_prob[cp2_p0] +engNum_prob[cp3_p1]+engNum_prob[cp4_p2]+engNum_prob[cp5_p3]+engNum_prob[cp6_p4] +engNum_prob[cp7_p5] +// + (false_prob[md2]+false_prob[md3]+false_prob[md4]+false_prob[md5]+false_prob[md5] + false_prob[md6]); + float loss = ch_prob[cp1_ch]*3 -(false_prob[cp3_p1]+false_prob[cp4_p2]+false_prob[cp5_p3]+false_prob[cp6_p4]+false_prob[cp7_p5]); + + if(loss>loss_selected) + { + loss_selected = loss; + cp_list[0]= cp1_ch; + cp_list[1]= cp2_p0; + cp_list[2]= cp3_p1; + cp_list[3]= cp4_p2; + cp_list[4]= cp5_p3; + cp_list[5]= cp6_p4; + cp_list[6]= cp7_p5; + } + } + } + candidate_pts[0] = cp_list[0]; + candidate_pts[1] = cp_list[1]; + candidate_pts[2] = cp_list[2]; + candidate_pts[3] = cp_list[3]; + candidate_pts[4] = cp_list[4]; + candidate_pts[5] = cp_list[5]; + candidate_pts[6] = cp_list[6]; + + candidatePts.first = loss_selected; + candidatePts.second = candidate_pts; + + }; + + + void PlateSegmentation::segmentPlateBySlidingWindows(cv::Mat &plateImage,int windowsWidth,int stride,cv::Mat &respones){ + + +// cv::resize(plateImage,plateImage,cv::Size(136,36)); + + cv::Mat plateImageGray; + cv::cvtColor(plateImage,plateImageGray,cv::COLOR_BGR2GRAY); + int padding = plateImage.cols-136 ; +// int padding = 0 ; + int height = plateImage.rows - 1; + int width = plateImage.cols - 1 - padding; + for(int i = 0 ; i < width - windowsWidth +1 ; i +=stride) + { + cv::Rect roi(i,0,windowsWidth,height); + cv::Mat roiImage = plateImageGray(roi); + cv::Mat response = classifyResponse(roiImage); + respones.push_back(response); + } + + + + + respones = respones.t(); +// std::pair> images ; +// +// +// std::cout< &Char_rects){ + cv::Mat plateImage = plateInfo.getPlateImage(); // get src image . + cv::Mat plateImageGray; + cv::cvtColor(plateImage,plateImageGray,cv::COLOR_BGR2GRAY); + //do binarzation + // + std::pair> sections ; // segment points variables . + + cv::Mat respones; //three response of every sub region from origin image . + segmentPlateBySlidingWindows(plateImage,DEFAULT_WIDTH,1,respones); + templateMatchFinding(respones,DEFAULT_WIDTH/stride,sections); + for(int i = 0; i < sections.second.size() ; i++) + { + sections.second[i]*=stride; + + } + +// std::cout< &rects){ + cv::Mat plateImage = plateInfo.getPlateImage(); + for(int i = 0 ; i < rects.size() ; i++){ + cv::Mat charImage; + plateImage(rects[i]).copyTo(charImage); + if(charImage.channels()) + cv::cvtColor(charImage,charImage,cv::COLOR_BGR2GRAY); +// cv::imshow("image",charImage); +// cv::waitKey(0); + cv::equalizeHist(charImage,charImage); +// + +// + + + std::pair char_instance; + if(i == 0 ){ + + char_instance.first = CHINESE; + + + } else if(i == 1){ + char_instance.first = LETTER; + } + else{ + char_instance.first = LETTER_NUMS; + } + char_instance.second = charImage; + plateInfo.appendPlateChar(char_instance); + + } + + } + +}//namespace pr diff --git a/Prj-PHP/lpr/Recognizer.cpp b/Prj-PHP/lpr/Recognizer.cpp new file mode 100644 index 0000000..3bfafef --- /dev/null +++ b/Prj-PHP/lpr/Recognizer.cpp @@ -0,0 +1,23 @@ +// +// Created by Jack Yu on 22/10/2017. +// + +#include "Recognizer.h" + +namespace pr{ + void GeneralRecognizer::SegmentBasedSequenceRecognition(PlateInfo &plateinfo){ + for(auto char_instance:plateinfo.plateChars) + { + std::pair res; + if(char_instance.second.rows*char_instance.second.cols>40) { + label code_table = recognizeCharacter(char_instance.second); + res.first = char_instance.first; + code_table.copyTo(res.second); + plateinfo.appendPlateCoding(res); + } else{ + res.first = INVALID; + plateinfo.appendPlateCoding(res); + } + } + } +} diff --git a/Prj-PHP/lpr/SegmentationFreeRecognizer.cpp b/Prj-PHP/lpr/SegmentationFreeRecognizer.cpp new file mode 100644 index 0000000..ec0f7e9 --- /dev/null +++ b/Prj-PHP/lpr/SegmentationFreeRecognizer.cpp @@ -0,0 +1,89 @@ +// +// Created by Jack Yu on 28/11/2017. +// +#include "SegmentationFreeRecognizer.h" + +namespace pr { + SegmentationFreeRecognizer::SegmentationFreeRecognizer(std::string prototxt, std::string caffemodel) { + net = cv::dnn::readNetFromCaffe(prototxt, caffemodel); + } + inline int judgeCharRange(int id) + {return id<31 || id>63; + } + std::pair decodeResults(cv::Mat code_table,std::vector mapping_table,float thres) + { + cv::MatSize mtsize = code_table.size; + int sequencelength = mtsize[2]; + int labellength = mtsize[1]; + cv::transpose(code_table.reshape(1,1).reshape(1,labellength),code_table); + std::string name = ""; + std::vector seq(sequencelength); + std::vector> seq_decode_res; + for(int i = 0 ; i < sequencelength; i++) { + float *fstart = ((float *) (code_table.data) + i * labellength ); + int id = std::max_element(fstart,fstart+labellength) - fstart; + seq[i] =id; + } + + float sum_confidence = 0; + int plate_lenghth = 0 ; + for(int i = 0 ; i< sequencelength ; i++) + { + if(seq[i]!=labellength-1 && (i==0 || seq[i]!=seq[i-1])) + { + float *fstart = ((float *) (code_table.data) + i * labellength ); + float confidence = *(fstart+seq[i]); + std::pair pair_(seq[i],confidence); + seq_decode_res.push_back(pair_); + } + } + int i = 0; + if (seq_decode_res.size()>1 && judgeCharRange(seq_decode_res[0].first) && judgeCharRange(seq_decode_res[1].first)) + { + i=2; + int c = seq_decode_res[0].second res; + res.second = sum_confidence/plate_lenghth; + res.first = name; + return res; + + } + std::string decodeResults(cv::Mat code_table,std::vector mapping_table) + { + cv::MatSize mtsize = code_table.size; + int sequencelength = mtsize[2]; + int labellength = mtsize[1]; + cv::transpose(code_table.reshape(1,1).reshape(1,labellength),code_table); + std::string name = ""; + std::vector seq(sequencelength); + for(int i = 0 ; i < sequencelength; i++) { + float *fstart = ((float *) (code_table.data) + i * labellength ); + int id = std::max_element(fstart,fstart+labellength) - fstart; + seq[i] =id; + } + for(int i = 0 ; i< sequencelength ; i++) + { + if(seq[i]!=labellength-1 && (i==0 || seq[i]!=seq[i-1])) + name+=mapping_table[seq[i]]; + } + return name; + } + std::pair SegmentationFreeRecognizer::SegmentationFreeForSinglePlate(cv::Mat Image,std::vector mapping_table) { + cv::transpose(Image,Image); + cv::Mat inputBlob = cv::dnn::blobFromImage(Image, 1 / 255.0, cv::Size(40,160)); + net.setInput(inputBlob, "data"); + cv::Mat char_prob_mat = net.forward(); + return decodeResults(char_prob_mat,mapping_table,0.00); + } +} diff --git a/Prj-PHP/lpr/util.h b/Prj-PHP/lpr/util.h new file mode 100644 index 0000000..3f9f52c --- /dev/null +++ b/Prj-PHP/lpr/util.h @@ -0,0 +1,67 @@ +// +// Created by Jack Yu on 04/04/2017. +// + +#include +namespace util{ + template void swap ( T& a, T& b ) + { + T c(a); a=b; b=c; + } + template T min(T& a,T& b ) + { + return a>b?b:a; + } + + cv::Mat cropFromImage(const cv::Mat &image,cv::Rect rect){ + int w = image.cols-1; + int h = image.rows-1; + rect.x = std::max(rect.x,0); + rect.y = std::max(rect.y,0); + rect.height = std::min(rect.height,h-rect.y); + rect.width = std::min(rect.width,w-rect.x); + cv::Mat temp(rect.size(), image.type()); + cv::Mat cropped; + temp = image(rect); + temp.copyTo(cropped); + return cropped; + + } + + cv::Mat cropBox2dFromImage(const cv::Mat &image,cv::RotatedRect rect) + { + cv::Mat M, rotated, cropped; + float angle = rect.angle; + cv::Size rect_size(rect.size.width,rect.size.height); + if (rect.angle < -45.) { + angle += 90.0; + swap(rect_size.width, rect_size.height); + } + M = cv::getRotationMatrix2D(rect.center, angle, 1.0); + cv::warpAffine(image, rotated, M, image.size(), cv::INTER_CUBIC); + cv::getRectSubPix(rotated, rect_size, rect.center, cropped); + return cropped; + } + + cv::Mat calcHist(const cv::Mat &image) + { + cv::Mat hsv; + std::vector hsv_planes; + cv::cvtColor(image,hsv,cv::COLOR_BGR2HSV); + cv::split(hsv,hsv_planes); + cv::Mat hist; + int histSize = 256; + float range[] = {0,255}; + const float* histRange = {range}; + cv::calcHist( &hsv_planes[0], 1, 0, cv::Mat(), hist, 1, &histSize, &histRange,true, true); + return hist; + } + + float computeSimilir(const cv::Mat &A,const cv::Mat &B) + { + cv::Mat histA,histB; + histA = calcHist(A); + histB = calcHist(B); + return cv::compareHist(histA,histB,CV_COMP_CORREL); + } +}//namespace util diff --git a/Prj-PHP/main.cpp b/Prj-PHP/main.cpp new file mode 100644 index 0000000..c690685 --- /dev/null +++ b/Prj-PHP/main.cpp @@ -0,0 +1,121 @@ +// +// Created by Coleflowers on 20/06/2018. +// +#include +#include + +#include "PlateSegmentation.h" +#include "CNNRecognizer.h" +#include "Recognizer.h" +#include "PlateDetection.h" +#include "FastDeskew.h" +#include "FineMapping.h" + + +std::vector chars{"京","沪","津","渝","冀","晋","蒙","辽","吉","黑","苏","浙","皖","闽","赣","鲁","豫","鄂","湘","粤","桂","琼","川","贵","云","藏","陕","甘","青","宁","新","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"}; + +/** + * 车牌图片识别 + * @param imgpath 图片的绝对路径 + * @return 车牌号 图片路径的图片不存在的话 返回空值 + */ +cv::String scan(const char *imgpath){ + // 读取图片获取车牌的粗略部分 + cv::Mat image = cv::imread(imgpath); + if(!image.data) { + printf("No img data!\n"); + return ""; + } + pr::PlateDetection plateDetection("/usr/platescan/src/lpr/model/cascade.xml"); + std::vector plates; + plateDetection.plateDetectionRough(image,plates); + // todo 处理发现的每一个车牌信息 + // 目前只处理了一组 + cv::Mat img; + for(pr::PlateInfo platex:plates) { + // 暂时不保存了 + //cv::imwrite("res/mmm.png",platex.getPlateImage()); + img = platex.getPlateImage(); + } + + // 车牌部分 + cv::Mat image_finemapping = pr::FineMapping::FineMappingVertical(img); + pr::FineMapping finemapper = pr::FineMapping("/usr/platescan/src/lpr/model/HorizonalFinemapping.prototxt","/usr/platescan/src/lpr/model/HorizonalFinemapping.caffemodel"); + + // + image_finemapping = pr::fastdeskew(image_finemapping, 5); + + // image_finemapping = finemapper.FineMappingHorizon(image_finemapping,0,-3); + // Android 代码里这个是 + // 改了之后部分图片数据精度发生变化 ,比如 字母精度有了,但是汉字又错了 + image_finemapping = finemapper.FineMappingHorizon(image_finemapping,2,5); + // 暂时不保存了 + //cv::imwrite("res/m222222222.png",image_finemapping); + + // 设置尺寸 识别 + cv::Mat demo = image_finemapping; + cv::resize(demo,demo,cv::Size(136,36)); + //cv::imwrite("res/m333333.png",demo); + + cv::Mat respones; + pr::PlateSegmentation plateSegmentation("/usr/platescan/src/lpr/model/Segmentation.prototxt","/usr/platescan/src/lpr/model/Segmentation.caffemodel"); + pr::PlateInfo plate; + plate.setPlateImage(demo); + std::vector rects; + plateSegmentation.segmentPlatePipline(plate,1,rects); + plateSegmentation.ExtractRegions(plate,rects); + + pr::GeneralRecognizer *recognizer = new pr::CNNRecognizer("/usr/platescan/src/lpr/model/CharacterRecognization.prototxt","/usr/platescan/src/lpr/model/CharacterRecognization.caffemodel"); + recognizer->SegmentBasedSequenceRecognition(plate); + //std::cout<("platescan", {Php::ByRef("string", Php::Type::String)}); + + // return the extension + return extension; + } +} diff --git a/Prj-PHP/platescan.ini b/Prj-PHP/platescan.ini new file mode 100644 index 0000000..aca617e --- /dev/null +++ b/Prj-PHP/platescan.ini @@ -0,0 +1,2 @@ +extension=platescan.so + diff --git a/Prj-PHP/tests/demo.png b/Prj-PHP/tests/demo.png new file mode 100644 index 0000000..e1dd608 Binary files /dev/null and b/Prj-PHP/tests/demo.png differ diff --git a/Prj-PHP/tests/platescan.php b/Prj-PHP/tests/platescan.php new file mode 100644 index 0000000..3d3a3e7 --- /dev/null +++ b/Prj-PHP/tests/platescan.php @@ -0,0 +1,13 @@ +