format
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
project(SwiftPR)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
find_package(OpenCV REQUIRED)
|
||||
|
||||
include_directories( ${OpenCV_INCLUDE_DIRS})
|
||||
include_directories(include)
|
||||
|
||||
set(SRC_DETECTION src/PlateDetection.cpp src/util.h include/PlateDetection.h)
|
||||
|
||||
set(SRC_FINEMAPPING src/FineMapping.cpp )
|
||||
|
||||
set(SRC_FASTDESKEW src/FastDeskew.cpp )
|
||||
|
||||
set(SRC_SEGMENTATION src/PlateSegmentation.cpp )
|
||||
|
||||
set(SRC_RECOGNIZE src/Recognizer.cpp src/CNNRecognizer.cpp)
|
||||
|
||||
set(SRC_PIPLINE src/Pipeline.cpp)
|
||||
|
||||
set(SRC_SEGMENTATIONFREE src/SegmentationFreeRecognizer.cpp )
|
||||
|
||||
#TEST_DETECTION
|
||||
add_executable(TEST_Detection ${SRC_DETECTION} demos/test_detection.cpp)
|
||||
target_link_libraries(TEST_Detection ${OpenCV_LIBS})
|
||||
|
||||
#TEST_FINEMAPPING
|
||||
add_executable(TEST_FINEMAPPING ${SRC_FINEMAPPING} demos/test_finemapping.cpp)
|
||||
target_link_libraries(TEST_FINEMAPPING ${OpenCV_LIBS})
|
||||
|
||||
#TEST_DESKEW
|
||||
|
||||
add_executable(TEST_FASTDESKEW ${SRC_FASTDESKEW} demos/test_fastdeskew.cpp)
|
||||
target_link_libraries(TEST_FASTDESKEW ${OpenCV_LIBS})
|
||||
|
||||
#TEST_SEGMENTATION
|
||||
|
||||
add_executable(TEST_SEGMENTATION ${SRC_SEGMENTATION} ${SRC_RECOGNIZE} demos/test_segmentation.cpp)
|
||||
target_link_libraries(TEST_SEGMENTATION ${OpenCV_LIBS})
|
||||
|
||||
#TEST_RECOGNIZATION
|
||||
|
||||
add_executable(TEST_RECOGNIZATION ${SRC_RECOGNIZE} demos/test_recognization.cpp)
|
||||
target_link_libraries(TEST_RECOGNIZATION ${OpenCV_LIBS})
|
||||
|
||||
#TEST_SEGMENTATIONFREE
|
||||
add_executable(TEST_SEGMENTATIONFREE ${SRC_SEGMENTATIONFREE} demos/test_segmentationFree.cpp)
|
||||
target_link_libraries(TEST_SEGMENTATIONFREE ${OpenCV_LIBS})
|
||||
|
||||
#TEST_PIPELINE
|
||||
|
||||
add_executable(TEST_PIPLINE ${SRC_DETECTION} ${SRC_FINEMAPPING} ${SRC_FASTDESKEW} ${SRC_SEGMENTATION} ${SRC_RECOGNIZE} ${SRC_PIPLINE} ${SRC_SEGMENTATIONFREE} demos/test_pipeline.cpp)
|
||||
target_link_libraries(TEST_PIPLINE ${OpenCV_LIBS})
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Created by 庾金科 on 20/09/2017.
|
||||
//
|
||||
|
||||
#include <../include/PlateDetection.h>
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
cv::Mat image = cv::imread("res/test1.jpg");
|
||||
pr::PlateDetection plateDetection("model/cascade.xml");
|
||||
std::vector<pr::PlateInfo> plates;
|
||||
plateDetection.plateDetectionRough(image,plates);
|
||||
for(pr::PlateInfo platex:plates)
|
||||
{
|
||||
drawRect(image,platex.getPlateRect());
|
||||
cv::imwrite("res/cache/test.png",platex.getPlateImage());
|
||||
cv::imshow("image",platex.getPlateImage());
|
||||
cv::waitKey(0);
|
||||
}
|
||||
cv::imshow("image",image);
|
||||
cv::waitKey(0);
|
||||
return 0 ;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Created by Jack Yu on 02/10/2017.
|
||||
//
|
||||
|
||||
|
||||
#include <../include/FastDeskew.h>
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
void TEST_DESKEW(){
|
||||
|
||||
cv::Mat image = cv::imread("res/3.png",cv::IMREAD_GRAYSCALE);
|
||||
// cv::resize(image,image,cv::Size(136*2,36*2));
|
||||
cv::Mat deskewed = pr::fastdeskew(image,12);
|
||||
// cv::imwrite("./res/4.png",deskewed);
|
||||
// cv::Mat deskewed2 = pr::fastdeskew(deskewed,12);
|
||||
//
|
||||
cv::imshow("image",deskewed);
|
||||
cv::waitKey(0);
|
||||
|
||||
}
|
||||
int main()
|
||||
{
|
||||
|
||||
TEST_DESKEW();
|
||||
return 0 ;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Created by Jack Yu on 24/09/2017.
|
||||
//
|
||||
|
||||
#include "FineMapping.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
cv::Mat image = cv::imread("res/cache/test.png");
|
||||
cv::Mat image_finemapping = pr::FineMapping::FineMappingVertical(image);
|
||||
pr::FineMapping finemapper = pr::FineMapping("model/HorizonalFinemapping.prototxt","model/HorizonalFinemapping.caffemodel");
|
||||
image_finemapping = finemapper.FineMappingHorizon(image_finemapping,0,-3);
|
||||
cv::imwrite("res/cache/finemappingres.png",image_finemapping);
|
||||
cv::imshow("image",image_finemapping);
|
||||
cv::waitKey(0);
|
||||
|
||||
|
||||
return 0 ;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
//
|
||||
// Created by Jack Yu on 23/10/2017.
|
||||
//
|
||||
|
||||
#include "../include/Pipeline.h"
|
||||
|
||||
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
template<class T>
|
||||
static unsigned int levenshtein_distance(const T &s1, const T &s2) {
|
||||
const size_t len1 = s1.size(), len2 = s2.size();
|
||||
std::vector<unsigned int> col(len2 + 1), prevCol(len2 + 1);
|
||||
|
||||
for (unsigned int i = 0; i < prevCol.size(); i++) prevCol[i] = i;
|
||||
for (unsigned int i = 0; i < len1; i++) {
|
||||
col[0] = i + 1;
|
||||
for (unsigned int j = 0; j < len2; j++)
|
||||
col[j + 1] = min(
|
||||
min(prevCol[1 + j] + 1, col[j] + 1),
|
||||
prevCol[j] + (s1[i] == s2[j] ? 0 : 1));
|
||||
col.swap(prevCol);
|
||||
}
|
||||
return prevCol[len2];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TEST_ACC(){
|
||||
|
||||
pr::PipelinePR prc("model/cascade.xml",
|
||||
"model/HorizonalFinemapping.prototxt","model/HorizonalFinemapping.caffemodel",
|
||||
"model/Segmentation.prototxt","model/Segmentation.caffemodel",
|
||||
"model/CharacterRecognization.prototxt","model/CharacterRecognization.caffemodel",
|
||||
"model/SegmenationFree-Inception.prototxt","model/SegmenationFree-Inception.caffemodel"
|
||||
);
|
||||
|
||||
ifstream file;
|
||||
string imagename;
|
||||
int n = 0,correct = 0,j = 0,sum = 0;
|
||||
char filename[] = "/Users/yujinke/Downloads/general_test/1.txt";
|
||||
string pathh = "/Users/yujinke/Downloads/general_test/";
|
||||
file.open(filename, ios::in);
|
||||
while (!file.eof())
|
||||
{
|
||||
file >> imagename;
|
||||
string imgpath = pathh + imagename;
|
||||
std::cout << "------------------------------------------------" << endl;
|
||||
cout << "图片名:" << imagename << endl;
|
||||
cv::Mat image = cv::imread(imgpath);
|
||||
// cv::imshow("image", image);
|
||||
// cv::waitKey(0);
|
||||
|
||||
std::vector<pr::PlateInfo> res = prc.RunPiplineAsImage(image,pr::SEGMENTATION_FREE_METHOD);
|
||||
|
||||
float conf = 0;
|
||||
vector<float> con ;
|
||||
vector<string> name;
|
||||
for (auto st : res) {
|
||||
if (st.confidence > 0.1) {
|
||||
//std::cout << st.getPlateName() << " " << st.confidence << std::endl;
|
||||
con.push_back(st.confidence);
|
||||
name.push_back(st.getPlateName());
|
||||
//conf += st.confidence;
|
||||
}
|
||||
else
|
||||
cout << "no string" << endl;
|
||||
}
|
||||
// std::cout << conf << std::endl;
|
||||
int num = con.size();
|
||||
float max = 0;
|
||||
string platestr, chpr, ch;
|
||||
int diff = 0,dif = 0;
|
||||
for (int i = 0; i < num; i++) {
|
||||
|
||||
if (con.at(i) > max)
|
||||
{
|
||||
max = con.at(i);
|
||||
platestr = name.at(i);
|
||||
}
|
||||
|
||||
}
|
||||
// cout << "max:"<<max << endl;
|
||||
cout << "string:" << platestr << endl;
|
||||
chpr = platestr.substr(0, 2);
|
||||
ch = imagename.substr(0, 2);
|
||||
diff = levenshtein_distance(imagename, platestr);
|
||||
dif = diff - 4;
|
||||
cout << "差距:" <<dif << endl;
|
||||
sum += dif;
|
||||
if (ch != chpr) n++;
|
||||
if (diff == 0) correct++;
|
||||
j++;
|
||||
}
|
||||
float cha = 1 - float(n) / float(j);
|
||||
std::cout << "------------------------------------------------" << endl;
|
||||
cout << "车牌总数:" << j << endl;
|
||||
cout << "汉字识别准确率:"<<cha << endl;
|
||||
float chaccuracy = 1 - float(sum - n * 2) /float(j * 8);
|
||||
cout << "字符识别准确率:" << chaccuracy << endl;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void TEST_PIPELINE(){
|
||||
|
||||
pr::PipelinePR prc("model/cascade.xml",
|
||||
"model/HorizonalFinemapping.prototxt","model/HorizonalFinemapping.caffemodel",
|
||||
"model/Segmentation.prototxt","model/Segmentation.caffemodel",
|
||||
"model/CharacterRecognization.prototxt","model/CharacterRecognization.caffemodel",
|
||||
"model/SegmenationFree-Inception.prototxt","model/SegmenationFree-Inception.caffemodel"
|
||||
);
|
||||
|
||||
cv::Mat image = cv::imread("/Users/yujinke/ClionProjects/cpp_ocr_demo/test.png");
|
||||
|
||||
|
||||
std::vector<pr::PlateInfo> res = prc.RunPiplineAsImage(image,pr::SEGMENTATION_FREE_METHOD);
|
||||
|
||||
for(auto st:res) {
|
||||
if(st.confidence>0.75) {
|
||||
std::cout << st.getPlateName() << " " << st.confidence << std::endl;
|
||||
cv::Rect region = st.getPlateRect();
|
||||
|
||||
cv::rectangle(image,cv::Point(region.x,region.y),cv::Point(region.x+region.width,region.y+region.height),cv::Scalar(255,255,0),2);
|
||||
}
|
||||
}
|
||||
|
||||
cv::imshow("image",image);
|
||||
cv::waitKey(0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void TEST_CAM()
|
||||
{
|
||||
|
||||
cv::VideoCapture capture("test1.mp4");
|
||||
cv::Mat frame;
|
||||
|
||||
pr::PipelinePR prc("model/cascade.xml",
|
||||
"model/HorizonalFinemapping.prototxt","model/HorizonalFinemapping.caffemodel",
|
||||
"model/Segmentation.prototxt","model/Segmentation.caffemodel",
|
||||
"model/CharacterRecognization.prototxt","model/CharacterRecognization.caffemodel",
|
||||
"model/SegmentationFree.prototxt","model/SegmentationFree.caffemodel"
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
while(1) {
|
||||
//读取下一帧
|
||||
if (!capture.read(frame)) {
|
||||
std::cout << "读取视频失败" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
//
|
||||
// cv::transpose(frame,frame);
|
||||
// cv::flip(frame,frame,2);
|
||||
|
||||
// cv::resize(frame,frame,cv::Size(frame.cols/2,frame.rows/2));
|
||||
|
||||
|
||||
|
||||
std::vector<pr::PlateInfo> res = prc.RunPiplineAsImage(frame,pr::SEGMENTATION_FREE_METHOD);
|
||||
|
||||
for(auto st:res) {
|
||||
if(st.confidence>0.75) {
|
||||
std::cout << st.getPlateName() << " " << st.confidence << std::endl;
|
||||
cv::Rect region = st.getPlateRect();
|
||||
|
||||
cv::rectangle(frame,cv::Point(region.x,region.y),cv::Point(region.x+region.width,region.y+region.height),cv::Scalar(255,255,0),2);
|
||||
}
|
||||
}
|
||||
|
||||
cv::imshow("image",frame);
|
||||
cv::waitKey(1);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
TEST_ACC();
|
||||
|
||||
// TEST_CAM();
|
||||
// TEST_PIPELINE();
|
||||
|
||||
return 0 ;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// Created by Jack Yu on 23/10/2017.
|
||||
//
|
||||
|
||||
#include "../include/CNNRecognizer.h"
|
||||
|
||||
std::vector<std::string> 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"};
|
||||
|
||||
#include <opencv2/dnn.hpp>
|
||||
using namespace cv::dnn;
|
||||
|
||||
|
||||
void getMaxClass(cv::Mat &probBlob, int *classId, double *classProb)
|
||||
{
|
||||
// cv::Mat probMat = probBlob.matRefConst().reshape(1, 1); //reshape the blob to 1x1000 matrix
|
||||
cv::Point classNumber;
|
||||
|
||||
cv::minMaxLoc(probBlob, NULL, classProb, NULL, &classNumber);
|
||||
|
||||
*classId = classNumber.x;
|
||||
}
|
||||
|
||||
void TEST_RECOGNIZATION(){
|
||||
// pr::CNNRecognizer instance("model/CharacterRecognization.prototxt","model/CharacterRecognization.caffemodel");
|
||||
Net net = cv::dnn::readNetFromCaffe("model/CharacterRecognization.prototxt","model/CharacterRecognization.caffemodel");
|
||||
cv::Mat image = cv::imread("res/char1.png",cv::IMREAD_GRAYSCALE);
|
||||
cv::resize(image,image,cv::Size(14,30));
|
||||
cv::equalizeHist(image,image);
|
||||
cv::Mat inputBlob = cv::dnn::blobFromImage(image, 1/255.0, cv::Size(14,30), false);
|
||||
|
||||
net.setInput(inputBlob,"data");
|
||||
|
||||
cv::Mat res = net.forward();
|
||||
std::cout<<res<<std::endl;
|
||||
float *p = (float*)res.data;
|
||||
int maxid= 0;
|
||||
double prob = 0;
|
||||
|
||||
getMaxClass(res,&maxid,&prob);
|
||||
|
||||
|
||||
|
||||
std::cout<<chars[maxid]<<std::endl;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
int main()
|
||||
{TEST_RECOGNIZATION();
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// Created by Jack Yu on 16/10/2017.
|
||||
//
|
||||
|
||||
|
||||
#include "../include/PlateSegmentation.h"
|
||||
#include "../include/CNNRecognizer.h"
|
||||
#include "../include/Recognizer.h"
|
||||
|
||||
|
||||
std::vector<std::string> 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"};
|
||||
|
||||
|
||||
void TEST_SLIDINGWINDOWS_EVAL(){
|
||||
cv::Mat demo = cv::imread("res/cache/finemappingres.png");
|
||||
cv::resize(demo,demo,cv::Size(136,36));
|
||||
|
||||
cv::Mat respones;
|
||||
pr::PlateSegmentation plateSegmentation("model/Segmentation.prototxt","model/Segmentation.caffemodel");
|
||||
pr::PlateInfo plate;
|
||||
plate.setPlateImage(demo);
|
||||
std::vector<cv::Rect> rects;
|
||||
plateSegmentation.segmentPlatePipline(plate,1,rects);
|
||||
plateSegmentation.ExtractRegions(plate,rects);
|
||||
|
||||
pr::GeneralRecognizer *recognizer = new pr::CNNRecognizer("model/CharacterRecognization.prototxt","model/CharacterRecognization.caffemodel");
|
||||
recognizer->SegmentBasedSequenceRecognition(plate);
|
||||
std::cout<<plate.decodePlateNormal(chars)<<std::endl;
|
||||
|
||||
|
||||
delete(recognizer);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
int main(){
|
||||
|
||||
TEST_SLIDINGWINDOWS_EVAL();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// Created by Jack Yu on 29/11/2017.
|
||||
//
|
||||
#include "../include/SegmentationFreeRecognizer.h"
|
||||
#include "../include/Pipeline.h"
|
||||
|
||||
#include "../include/PlateInfo.h"
|
||||
|
||||
|
||||
|
||||
std::string decodeResults(cv::Mat code_table,std::vector<std::string> 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<int> 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]];
|
||||
}
|
||||
std::cout<<name;
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
cv::Mat image = cv::imread("res/cache/chars_segment.jpg");
|
||||
// cv::transpose(image,image);
|
||||
|
||||
// cv::resize(image,image,cv::Size(160,40));
|
||||
cv::imshow("xxx",image);
|
||||
cv::waitKey(0);
|
||||
pr::SegmentationFreeRecognizer recognizr("model/SegmenationFree-Inception.prototxt","model/ISegmenationFree-Inception.caffemodel");
|
||||
std::pair<std::string,float> res = recognizr.SegmentationFreeForSinglePlate(image,pr::CH_PLATE_CODE);
|
||||
std::cout<<res.first<<" "
|
||||
<<res.second<<std::endl;
|
||||
|
||||
|
||||
// decodeResults(plate,pr::CH_PLATE_CODE);
|
||||
cv::imshow("image",image);
|
||||
cv::waitKey(0);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Created by Jack Yu on 21/10/2017.
|
||||
//
|
||||
|
||||
#include "../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();
|
||||
}
|
||||
} // namespace pr
|
||||
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// Created by Jack Yu on 02/10/2017.
|
||||
//
|
||||
|
||||
#include <../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<float> avgfilter(std::vector<float> angle_list, int windowsSize) {
|
||||
std::vector<float> 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<float> seq) {
|
||||
cv::Mat image(300, seq.size(), CV_8U);
|
||||
image.setTo(0);
|
||||
|
||||
for (int i = 0; i < seq.size(); i++) {
|
||||
float l = *std::max_element(seq.begin(), seq.end());
|
||||
|
||||
int p = int(float(seq[i]) / l * 300);
|
||||
|
||||
cv::line(image, cv::Point(i, 300), cv::Point(i, 300 - p),
|
||||
cv::Scalar(255, 255, 255));
|
||||
}
|
||||
cv::imshow("vis", image);
|
||||
}
|
||||
|
||||
cv::Mat correctPlateImage(cv::Mat skewPlate, float angle, float maxAngle) {
|
||||
cv::Mat dst;
|
||||
cv::Size size_o(skewPlate.cols, skewPlate.rows);
|
||||
int extend_padding = 0;
|
||||
extend_padding =
|
||||
static_cast<int>(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<float> 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<cv::Vec6f>(j, i)[4];
|
||||
float y2 = eigen.at<cv::Vec6f>(j, i)[5];
|
||||
int angle_cell = angle(x2, y2);
|
||||
angle_list[(angle_cell + 180) % 180] += 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<float> 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 < ANGLE_MIN)
|
||||
maxPos -= 90;
|
||||
maxPos = 90 - maxPos;
|
||||
cv::Mat deskewed = correctPlateImage(bak, static_cast<float>(maxPos), 60.0f);
|
||||
return deskewed;
|
||||
}
|
||||
|
||||
} // namespace pr
|
||||
@@ -0,0 +1,165 @@
|
||||
#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) {
|
||||
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<int>(prob.at<float>(0, 0) * FinedVertical.cols);
|
||||
int back = static_cast<int>(prob.at<float>(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<int, int> FitLineRansac(std::vector<cv::Point> pts, int zeroadd = 0) {
|
||||
std::pair<int, int> 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<int>((-x * vy / vx) + y);
|
||||
int righty = static_cast<int>(((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<float>(upper - lower);
|
||||
diff /= static_cast<float>(sliceNum - 1);
|
||||
cv::Mat binary_adaptive;
|
||||
std::vector<cv::Point> line_upper;
|
||||
std::vector<cv::Point> line_lower;
|
||||
int contours_nums = 0;
|
||||
for (int i = 0; i < sliceNum; i++) {
|
||||
std::vector<std::vector<cv::Point>> 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<float>(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<std::vector<cv::Point>> 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<float>(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<int, int> A;
|
||||
std::pair<int, int> 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<cv::Point2f> 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<cv::Point2f> 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;
|
||||
}
|
||||
} // namespace pr
|
||||
@@ -0,0 +1,82 @@
|
||||
//
|
||||
// Created by Jack Yu on 23/10/2017.
|
||||
//
|
||||
|
||||
#include "../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<PlateInfo> PipelinePR::RunPiplineAsImage(cv::Mat plateImage,
|
||||
int method) {
|
||||
std::vector<PlateInfo> results;
|
||||
std::vector<pr::PlateInfo> 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));
|
||||
plateinfo.setPlateImage(image_finemapping);
|
||||
std::vector<cv::Rect> 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));
|
||||
plateinfo.setPlateImage(image_finemapping);
|
||||
std::pair<std::string, float> res =
|
||||
segmentationFreeRecognizer->SegmentationFreeForSinglePlate(
|
||||
plateinfo.getPlateImage(), pr::CH_PLATE_CODE);
|
||||
plateinfo.confidence = res.second;
|
||||
plateinfo.setPlateName(res.first);
|
||||
}
|
||||
results.push_back(plateinfo);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
} // namespace pr
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "../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<pr::PlateInfo> &plateInfos,
|
||||
int min_w, int max_w) {
|
||||
cv::Mat processImage;
|
||||
cv::cvtColor(InputImage, processImage, cv::COLOR_BGR2GRAY);
|
||||
std::vector<cv::Rect> 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<int>(plate.width * 0.30);
|
||||
int zeroadd_h = static_cast<int>(plate.height * 2);
|
||||
int zeroadd_x = static_cast<int>(plate.width * 0.15);
|
||||
int zeroadd_y = static_cast<int>(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
|
||||
@@ -0,0 +1,305 @@
|
||||
//
|
||||
// Created by Jack Yu on 16/10/2017.
|
||||
//
|
||||
|
||||
#include "../include/PlateSegmentation.h"
|
||||
#include "../include/niBlackThreshold.h"
|
||||
|
||||
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 < size; i++) {
|
||||
int p = int(float(seq[i]) / l * 300);
|
||||
cv::line(image, cv::Point(i, 300), cv::Point(i, 300 - p),
|
||||
cv::Scalar(255, 255, 255));
|
||||
}
|
||||
cv::resize(image, image, cv::Size(600, 100));
|
||||
cv::imshow(name, image);
|
||||
}
|
||||
|
||||
inline void computeSafeMargin(int &val, const int &rows) {
|
||||
val = std::min(val, rows);
|
||||
val = std::max(val, 0);
|
||||
}
|
||||
|
||||
cv::Rect boxFromCenter(const cv::Point center, int left, int right, int top,
|
||||
int bottom, cv::Size bdSize) {
|
||||
cv::Point p1(center.x - left, center.y - top);
|
||||
cv::Point p2(center.x + right, center.y + bottom);
|
||||
p1.x = std::max(0, p1.x);
|
||||
p1.y = std::max(0, p1.y);
|
||||
p2.x = std::min(p2.x, bdSize.width - 1);
|
||||
p2.y = std::min(p2.y, bdSize.height - 1);
|
||||
cv::Rect rect(p1, p2);
|
||||
return rect;
|
||||
}
|
||||
|
||||
cv::Rect boxPadding(cv::Rect rect, int left, int right, int top, int bottom,
|
||||
cv::Size bdSize) {
|
||||
|
||||
cv::Point center(rect.x + (rect.width >> 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<int> &candidatePts,
|
||||
const int padding,
|
||||
std::vector<cv::Rect> &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<std::vector<cv::Point>> 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 < final_dist and bdbox.height > rows >> 1) {
|
||||
final_dist = dist;
|
||||
final_center = center;
|
||||
final_bdbox = bdbox;
|
||||
}
|
||||
}
|
||||
|
||||
// rebuild box
|
||||
if (final_bdbox.height / static_cast<float>(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<<final_bdbox<<std::endl;
|
||||
// std::cout<<roiImage.size()<<std::endl;
|
||||
#ifdef DEBUG
|
||||
cv::imshow("char_thres", roi_thres);
|
||||
|
||||
cv::imshow("char", roiImage(final_bdbox));
|
||||
cv::waitKey(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
final_bdbox.x += left;
|
||||
|
||||
rects.push_back(final_bdbox);
|
||||
//
|
||||
|
||||
} else {
|
||||
rects.push_back(roi);
|
||||
}
|
||||
|
||||
// else
|
||||
// {
|
||||
//
|
||||
// }
|
||||
|
||||
// cv::GaussianBlur(roiImage,roiImage,cv::Size(7,7),3);
|
||||
//
|
||||
// cv::imshow("image",roiImage);
|
||||
// cv::waitKey(0);
|
||||
}
|
||||
}
|
||||
void avgfilter(float *angle_list, int size, int windowsSize) {
|
||||
float *filterd = new float[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
filterd[i] = angle_list[i];
|
||||
// memcpy(filterd,angle_list,size);
|
||||
|
||||
cv::Mat kernal_gaussian = cv::getGaussianKernel(windowsSize, 3, CV_32F);
|
||||
float *kernal = (float *)kernal_gaussian.data;
|
||||
// kernal+=windowsSize;
|
||||
int r = windowsSize / 2;
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
float avg = 0.00f;
|
||||
for (int j = 0; j < windowsSize; j++) {
|
||||
if (i + j - r > 0 && i + j + r < size - 1)
|
||||
avg += filterd[i + j - r] * kernal[j];
|
||||
}
|
||||
// avg = avg / windowsSize;
|
||||
angle_list[i] = avg;
|
||||
}
|
||||
|
||||
delete filterd;
|
||||
}
|
||||
|
||||
void PlateSegmentation::templateMatchFinding(
|
||||
const cv::Mat &respones, int windowsWidth,
|
||||
std::pair<float, std::vector<int>> &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);
|
||||
std::vector<int> candidate_pts(7);
|
||||
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] * 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::Mat plateImageGray;
|
||||
cv::cvtColor(plateImage, plateImageGray, cv::COLOR_BGR2GRAY);
|
||||
int padding = plateImage.cols - 136;
|
||||
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();
|
||||
}
|
||||
|
||||
void PlateSegmentation::segmentPlatePipline(PlateInfo &plateInfo, int stride,
|
||||
std::vector<cv::Rect> &Char_rects) {
|
||||
cv::Mat plateImage = plateInfo.getPlateImage(); // get src image .
|
||||
cv::Mat plateImageGray;
|
||||
cv::cvtColor(plateImage, plateImageGray, cv::COLOR_BGR2GRAY);
|
||||
// do binarzation
|
||||
std::pair<float, std::vector<int>> 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;
|
||||
}
|
||||
refineRegion(plateImageGray, sections.second, 5, Char_rects);
|
||||
}
|
||||
|
||||
void PlateSegmentation::ExtractRegions(PlateInfo &plateInfo,
|
||||
std::vector<cv::Rect> &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::equalizeHist(charImage, charImage);
|
||||
std::pair<CharType, cv::Mat> 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
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Created by Jack Yu on 22/10/2017.
|
||||
//
|
||||
|
||||
#include "../include/Recognizer.h"
|
||||
|
||||
namespace pr {
|
||||
void GeneralRecognizer::SegmentBasedSequenceRecognition(PlateInfo &plateinfo) {
|
||||
for (auto char_instance : plateinfo.plateChars) {
|
||||
std::pair<CharType, cv::Mat> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace pr
|
||||
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// Created by Jack Yu on 28/11/2017.
|
||||
//
|
||||
#include "../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<std::string, float>
|
||||
decodeResults(cv::Mat code_table, std::vector<std::string> 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<int> seq(sequencelength);
|
||||
std::vector<std::pair<int, float>> 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<int, float> 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 < seq_decode_res[1].second;
|
||||
name += mapping_table[seq_decode_res[c].first];
|
||||
sum_confidence += seq_decode_res[c].second;
|
||||
plate_lenghth++;
|
||||
}
|
||||
|
||||
for (; i < seq_decode_res.size(); i++) {
|
||||
name += mapping_table[seq_decode_res[i].first];
|
||||
sum_confidence += seq_decode_res[i].second;
|
||||
plate_lenghth++;
|
||||
}
|
||||
std::pair<std::string, float> res;
|
||||
res.second = sum_confidence / plate_lenghth;
|
||||
res.first = name;
|
||||
return res;
|
||||
}
|
||||
std::string decodeResults(cv::Mat code_table,
|
||||
std::vector<std::string> 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<int> 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<std::string, float>
|
||||
SegmentationFreeRecognizer::SegmentationFreeForSinglePlate(
|
||||
cv::Mat Image, std::vector<std::string> 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);
|
||||
}
|
||||
} // namespace pr
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// Created by Jack Yu on 04/04/2017.
|
||||
//
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
namespace util {
|
||||
template <class T> void swap(T &a, T &b) {
|
||||
T c(a);
|
||||
a = b;
|
||||
b = c;
|
||||
}
|
||||
template <class T> 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<cv::Mat> 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
|
||||
Reference in New Issue
Block a user