diff --git a/src/main.cpp b/src/main.cpp index e8a3505..c51cc32 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,90 +17,83 @@ * along with this program. If not, see . */ +#include +#include +#include "opencv2/highgui/highgui.hpp" +#include "opencv2/imgproc/imgproc.hpp" - #include - #include +#include "tclap/CmdLine.h" +#include "support/filesystem.h" +#include "support/timing.h" +#include "alpr.h" - #include "opencv2/highgui/highgui.hpp" - #include "opencv2/imgproc/imgproc.hpp" +const std::string MAIN_WINDOW_NAME = "ALPR main window"; +const bool SAVE_LAST_VIDEO_STILL = false; +const std::string LAST_VIDEO_STILL_LOCATION = "/tmp/laststill.jpg"; - #include "tclap/CmdLine.h" - #include "support/filesystem.h" - #include "support/timing.h" - #include "alpr.h" +/** Function Headers */ +bool detectandshow(Alpr* alpr, cv::Mat frame, std::string region, bool writeJson); +bool measureProcessingTime = false; +int main( int argc, const char** argv ) +{ + std::string filename; + std::string runtimePath = ""; + bool outputJson = false; + int seektoms = 0; + bool detectRegion = false; + std::string templateRegion; + std::string country; + int topn; - const std::string MAIN_WINDOW_NAME = "ALPR main window"; + try + { + TCLAP::CmdLine cmd("OpenAlpr Command Line Utility", ' ', OPENALPR_VERSION); - const bool SAVE_LAST_VIDEO_STILL = false; - const std::string LAST_VIDEO_STILL_LOCATION = "/tmp/laststill.jpg"; + TCLAP::UnlabeledValueArg fileArg( "image_file", "Image containing license plates", true, "", "image_file_path" ); + TCLAP::ValueArg countryCodeArg("c","country","Country code to identify (either us for USA or eu for Europe). Default=us",false, "us" ,"country_code"); + TCLAP::ValueArg seekToMsArg("","seek","Seek to the specied millisecond in a video file. Default=0",false, 0 ,"integer_ms"); + TCLAP::ValueArg runtimeDirArg("r","runtime_dir","Path to the OpenAlpr runtime data directory",false, "" ,"runtime_dir"); + TCLAP::ValueArg templateRegionArg("t","template_region","Attempt to match the plate number against a region template (e.g., md for Maryland, ca for California)",false, "" ,"region code"); + TCLAP::ValueArg topNArg("n","topn","Max number of possible plate numbers to return. Default=10",false, 10 ,"topN"); - /** Function Headers */ - bool detectandshow(Alpr* alpr, cv::Mat frame, std::string region, bool writeJson); + TCLAP::SwitchArg jsonSwitch("j","json","Output recognition results in JSON format. Default=off", cmd, false); + TCLAP::SwitchArg detectRegionSwitch("d","detect_region","Attempt to detect the region of the plate image. Default=off", cmd, false); + TCLAP::SwitchArg clockSwitch("","clock","Measure/print the total time to process image and all plates. Default=off", cmd, false); + cmd.add( fileArg ); + cmd.add( countryCodeArg ); + cmd.add( seekToMsArg ); + cmd.add( topNArg ); + cmd.add( runtimeDirArg ); + cmd.add( templateRegionArg ); - bool measureProcessingTime = false; + cmd.parse( argc, argv ); - int main( int argc, const char** argv ) - { - std::string filename; - std::string runtimePath = ""; - bool outputJson = false; - int seektoms = 0; - bool detectRegion = false; - std::string templateRegion; - std::string country; - int topn; + filename = fileArg.getValue(); - try { + country = countryCodeArg.getValue(); + seektoms = seekToMsArg.getValue(); + outputJson = jsonSwitch.getValue(); + runtimePath = runtimeDirArg.getValue(); + detectRegion = detectRegionSwitch.getValue(); + templateRegion = templateRegionArg.getValue(); + topn = topNArg.getValue(); + measureProcessingTime = clockSwitch.getValue(); - TCLAP::CmdLine cmd("OpenAlpr Command Line Utility", ' ', OPENALPR_VERSION); - - TCLAP::UnlabeledValueArg fileArg( "image_file", "Image containing license plates", true, "", "image_file_path" ); - - TCLAP::ValueArg countryCodeArg("c","country","Country code to identify (either us for USA or eu for Europe). Default=us",false, "us" ,"country_code"); - TCLAP::ValueArg seekToMsArg("","seek","Seek to the specied millisecond in a video file. Default=0",false, 0 ,"integer_ms"); - TCLAP::ValueArg runtimeDirArg("r","runtime_dir","Path to the OpenAlpr runtime data directory",false, "" ,"runtime_dir"); - TCLAP::ValueArg templateRegionArg("t","template_region","Attempt to match the plate number against a region template (e.g., md for Maryland, ca for California)",false, "" ,"region code"); - TCLAP::ValueArg topNArg("n","topn","Max number of possible plate numbers to return. Default=10",false, 10 ,"topN"); - - TCLAP::SwitchArg jsonSwitch("j","json","Output recognition results in JSON format. Default=off", cmd, false); - TCLAP::SwitchArg detectRegionSwitch("d","detect_region","Attempt to detect the region of the plate image. Default=off", cmd, false); - TCLAP::SwitchArg clockSwitch("","clock","Measure/print the total time to process image and all plates. Default=off", cmd, false); - - cmd.add( fileArg ); - cmd.add( countryCodeArg ); - cmd.add( seekToMsArg ); - cmd.add( topNArg ); - cmd.add( runtimeDirArg ); - cmd.add( templateRegionArg ); - - cmd.parse( argc, argv ); - - filename = fileArg.getValue(); - - country = countryCodeArg.getValue(); - seektoms = seekToMsArg.getValue(); - outputJson = jsonSwitch.getValue(); - runtimePath = runtimeDirArg.getValue(); - detectRegion = detectRegionSwitch.getValue(); - templateRegion = templateRegionArg.getValue(); - topn = topNArg.getValue(); - measureProcessingTime = clockSwitch.getValue(); - - } catch (TCLAP::ArgException &e) // catch any exceptions - { - std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl; - return 1; - } - - cv::Mat frame; + } + catch (TCLAP::ArgException &e) // catch any exceptions + { + std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl; + return 1; + } + cv::Mat frame; Alpr alpr(country, runtimePath); alpr.setTopN(topn); @@ -125,15 +118,15 @@ cv::VideoCapture cap(0); if (!cap.isOpened()) { - std::cout << "Error opening webcam" << std::endl; - return 1; + std::cout << "Error opening webcam" << std::endl; + return 1; } while (cap.read(frame) == true) { - detectandshow(&alpr, frame, "", outputJson); - cv::waitKey(1); - framenum++; + detectandshow(&alpr, frame, "", outputJson); + cv::waitKey(1); + framenum++; } } else if (hasEnding(filename, ".avi") || hasEnding(filename, ".mp4") || hasEnding(filename, ".webm") || hasEnding(filename, ".flv")) @@ -148,16 +141,16 @@ while (cap.read(frame) == true) { - if (SAVE_LAST_VIDEO_STILL == true) - { - cv::imwrite(LAST_VIDEO_STILL_LOCATION, frame); - } - std::cout << "Frame: " << framenum << std::endl; + if (SAVE_LAST_VIDEO_STILL == true) + { + cv::imwrite(LAST_VIDEO_STILL_LOCATION, frame); + } + std::cout << "Frame: " << framenum << std::endl; - detectandshow( &alpr, frame, "", outputJson); - //create a 1ms delay - cv::waitKey(1); - framenum++; + detectandshow( &alpr, frame, "", outputJson); + //create a 1ms delay + cv::waitKey(1); + framenum++; } } else @@ -179,32 +172,31 @@ std::cerr << "Image file not found: " << filename << std::endl; } - } else if (DirectoryExists(filename.c_str())) { - std::vector files = getFilesInDir(filename.c_str()); + std::vector files = getFilesInDir(filename.c_str()); - std::sort( files.begin(), files.end(), stringCompare ); + std::sort( files.begin(), files.end(), stringCompare ); - for (int i = 0; i< files.size(); i++) + for (int i = 0; i< files.size(); i++) + { + if (hasEnding(files[i], ".jpg") || hasEnding(files[i], ".png")) { - if (hasEnding(files[i], ".jpg") || hasEnding(files[i], ".png")) - { - std::string fullpath = filename + "/" + files[i]; - std::cout << fullpath << std::endl; - frame = cv::imread( fullpath.c_str() ); - if (detectandshow( &alpr, frame, "", outputJson)) - { - //while ((char) cv::waitKey(50) != 'c') { } - } - else - { - //cv::waitKey(50); - } - } - + std::string fullpath = filename + "/" + files[i]; + std::cout << fullpath << std::endl; + frame = cv::imread( fullpath.c_str() ); + if (detectandshow( &alpr, frame, "", outputJson)) + { + //while ((char) cv::waitKey(50) != 'c') { } + } + else + { + //cv::waitKey(50); + } } + + } } else { @@ -212,53 +204,45 @@ return 1; } - - return 0; - } +} +bool detectandshow( Alpr* alpr, cv::Mat frame, std::string region, bool writeJson) +{ - bool detectandshow( Alpr* alpr, cv::Mat frame, std::string region, bool writeJson) - { + std::vector buffer; + cv::imencode(".bmp", frame, buffer ); + timespec startTime; + getTime(&startTime); - std::vector buffer; - cv::imencode(".bmp", frame, buffer ); + std::vector results = alpr->recognize(buffer); - - timespec startTime; - getTime(&startTime); - - - std::vector results = alpr->recognize(buffer); - - - if (writeJson) - { + if (writeJson) + { std::cout << alpr->toJson(results) << std::endl; - } - else - { + } + else + { for (int i = 0; i < results.size(); i++) { std::cout << "plate" << i << ": " << results[i].result_count << " results -- Processing Time = " << results[i].processing_time_ms << "ms." << std::endl; for (int k = 0; k < results[i].topNPlates.size(); k++) { - std::cout << " - " << results[i].topNPlates[k].characters << "\t confidence: " << results[i].topNPlates[k].overall_confidence << "\t template_match: " << results[i].topNPlates[k].matches_template << std::endl; + std::cout << " - " << results[i].topNPlates[k].characters << "\t confidence: " << results[i].topNPlates[k].overall_confidence << "\t template_match: " << results[i].topNPlates[k].matches_template << std::endl; } } - } + } - timespec endTime; - getTime(&endTime); - if (measureProcessingTime) - std::cout << "Total Time to process image: " << diffclock(startTime, endTime) << "ms." << std::endl; + timespec endTime; + getTime(&endTime); + if (measureProcessingTime) + std::cout << "Total Time to process image: " << diffclock(startTime, endTime) << "ms." << std::endl; + if (results.size() > 0) + return true; + return false; - if (results.size() > 0) - return true; - return false; - - } +} diff --git a/src/misc_utilities/benchmark.cpp b/src/misc_utilities/benchmark.cpp index 909775b..bcfe1b9 100644 --- a/src/misc_utilities/benchmark.cpp +++ b/src/misc_utilities/benchmark.cpp @@ -17,12 +17,12 @@ * along with this program. If not, see . */ - #include "opencv2/highgui/highgui.hpp" - #include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/highgui/highgui.hpp" +#include "opencv2/imgproc/imgproc.hpp" - #include - #include - #include +#include +#include +#include #include #include // std::accumulate @@ -34,8 +34,8 @@ //#include "utility.h" #include "support/filesystem.h" - using namespace std; - using namespace cv; +using namespace std; +using namespace cv; // Given a directory full of lp images (named [statecode]#.png) crop out the alphanumeric characters. // These will be used to train the OCR @@ -44,47 +44,44 @@ void outputStats(vector datapoints); int main( int argc, const char** argv ) { - string country; - string benchmarkName; - string inDir; - string outDir; - Mat frame; - + string country; + string benchmarkName; + string inDir; + string outDir; + Mat frame; //Check if user specify image to process if(argc == 5) { - country = argv[1]; - benchmarkName = argv[2]; - inDir = argv[3]; - outDir = argv[4]; + country = argv[1]; + benchmarkName = argv[2]; + inDir = argv[3]; + outDir = argv[4]; - - }else{ - printf("Use:\n\t%s [country] [benchmark name] [img input dir] [results output dir]\n",argv[0]); - printf("\tex: %s us speed ./speed/usimages ./speed\n",argv[0]); - printf("\n"); - printf("\ttest names are: speed, segocr, detection\n\n" ); - return 0; } - + else + { + printf("Use:\n\t%s [country] [benchmark name] [img input dir] [results output dir]\n",argv[0]); + printf("\tex: %s us speed ./speed/usimages ./speed\n",argv[0]); + printf("\n"); + printf("\ttest names are: speed, segocr, detection\n\n" ); + return 0; + } if (DirectoryExists(inDir.c_str()) == false) { - printf("Input dir does not exist\n"); - return 0; + printf("Input dir does not exist\n"); + return 0; } if (DirectoryExists(outDir.c_str()) == false) { - printf("Output dir does not exist\n"); - return 0; + printf("Output dir does not exist\n"); + return 0; } - vector files = getFilesInDir(inDir.c_str()); sort( files.begin(), files.end(), stringCompare ); - if (benchmarkName.compare("segocr") == 0) { Config* config = new Config(country); @@ -92,55 +89,50 @@ int main( int argc, const char** argv ) OCR* ocr = new OCR(config); - for (int i = 0; i< files.size(); i++) { if (hasEnding(files[i], ".png")) { - string fullpath = inDir + "/" + files[i]; + string fullpath = inDir + "/" + files[i]; - frame = imread( fullpath.c_str() ); - resize(frame, frame, Size(config->ocrImageWidthPx, config->ocrImageHeightPx)); + frame = imread( fullpath.c_str() ); + resize(frame, frame, Size(config->ocrImageWidthPx, config->ocrImageHeightPx)); - Rect plateCoords; - plateCoords.x = 0; - plateCoords.y = 0; - plateCoords.width = frame.cols; - plateCoords.height = frame.rows; + Rect plateCoords; + plateCoords.x = 0; + plateCoords.y = 0; + plateCoords.width = frame.cols; + plateCoords.height = frame.rows; - char statecode[3]; - statecode[0] = files[i][0]; - statecode[1] = files[i][1]; - statecode[2] = '\0'; - string statecodestr(statecode); + char statecode[3]; + statecode[0] = files[i][0]; + statecode[1] = files[i][1]; + statecode[2] = '\0'; + string statecodestr(statecode); - CharacterRegion charRegion(frame, config); + CharacterRegion charRegion(frame, config); - if (abs(charRegion.getTopLine().angle) > 4) - { - // Rotate image: - Mat rotated(frame.size(), frame.type()); - Mat rot_mat( 2, 3, CV_32FC1 ); - Point center = Point( frame.cols/2, frame.rows/2 ); + if (abs(charRegion.getTopLine().angle) > 4) + { + // Rotate image: + Mat rotated(frame.size(), frame.type()); + Mat rot_mat( 2, 3, CV_32FC1 ); + Point center = Point( frame.cols/2, frame.rows/2 ); - rot_mat = getRotationMatrix2D( center, charRegion.getTopLine().angle, 1.0 ); - warpAffine( frame, rotated, rot_mat, frame.size() ); + rot_mat = getRotationMatrix2D( center, charRegion.getTopLine().angle, 1.0 ); + warpAffine( frame, rotated, rot_mat, frame.size() ); - rotated.copyTo(frame); - } + rotated.copyTo(frame); + } + CharacterSegmenter charSegmenter(frame, charRegion.thresholdsInverted(), config); + ocr->performOCR(charSegmenter.getThresholds(), charSegmenter.characters); + ocr->postProcessor->analyze(statecode, 25); + cout << files[i] << "," << statecode << "," << ocr->postProcessor->bestChars << endl; - CharacterSegmenter charSegmenter(frame, charRegion.thresholdsInverted(), config); - ocr->performOCR(charSegmenter.getThresholds(), charSegmenter.characters); - ocr->postProcessor->analyze(statecode, 25); - - cout << files[i] << "," << statecode << "," << ocr->postProcessor->bestChars << endl; - - - imshow("Current LP", frame); - waitKey(5); - + imshow("Current LP", frame); + waitKey(5); } @@ -158,14 +150,13 @@ int main( int argc, const char** argv ) { if (hasEnding(files[i], ".png")) { - string fullpath = inDir + "/" + files[i]; - frame = imread( fullpath.c_str() ); + string fullpath = inDir + "/" + files[i]; + frame = imread( fullpath.c_str() ); - vector regions = plateDetector.detect(frame); - - imshow("Current LP", frame); - waitKey(5); + vector regions = plateDetector.detect(frame); + imshow("Current LP", frame); + waitKey(5); } @@ -173,7 +164,7 @@ int main( int argc, const char** argv ) } else if (benchmarkName.compare("speed") == 0) { - // Benchmarks speed of region detection, plate analysis, and OCR + // Benchmarks speed of region detection, plate analysis, and OCR timespec startTime; timespec endTime; @@ -197,75 +188,72 @@ int main( int argc, const char** argv ) vector ocrTimes; vector postProcessTimes; - for (int i = 0; i< files.size(); i++) { if (hasEnding(files[i], ".png")) { - cout << "Image: " << files[i] << endl; + cout << "Image: " << files[i] << endl; - string fullpath = inDir + "/" + files[i]; - frame = imread( fullpath.c_str() ); + string fullpath = inDir + "/" + files[i]; + frame = imread( fullpath.c_str() ); + getTime(&startTime); + alpr.recognize(frame); + getTime(&endTime); + double endToEndTime = diffclock(startTime, endTime); + cout << " -- End to End recognition time: " << endToEndTime << "ms." << endl; + endToEndTimes.push_back(endToEndTime); - getTime(&startTime); - alpr.recognize(frame); - getTime(&endTime); - double endToEndTime = diffclock(startTime, endTime); - cout << " -- End to End recognition time: " << endToEndTime << "ms." << endl; - endToEndTimes.push_back(endToEndTime); + getTime(&startTime); + vector regions = plateDetector.detect(frame); + getTime(&endTime); - getTime(&startTime); - vector regions = plateDetector.detect(frame); - getTime(&endTime); + double regionDetectionTime = diffclock(startTime, endTime); + cout << " -- Region detection time: " << regionDetectionTime << "ms." << endl; + regionDetectionTimes.push_back(regionDetectionTime); - double regionDetectionTime = diffclock(startTime, endTime); - cout << " -- Region detection time: " << regionDetectionTime << "ms." << endl; - regionDetectionTimes.push_back(regionDetectionTime); + for (int z = 0; z < regions.size(); z++) + { + getTime(&startTime); + char temp[5]; + stateIdentifier.recognize(frame, regions[z], temp); + getTime(&endTime); + double stateidTime = diffclock(startTime, endTime); + cout << "\tRegion " << z << ": State ID time: " << stateidTime << "ms." << endl; + stateIdTimes.push_back(stateidTime); - for (int z = 0; z < regions.size(); z++) - { - getTime(&startTime); - char temp[5]; - stateIdentifier.recognize(frame, regions[z], temp); - getTime(&endTime); - double stateidTime = diffclock(startTime, endTime); - cout << "\tRegion " << z << ": State ID time: " << stateidTime << "ms." << endl; - stateIdTimes.push_back(stateidTime); + getTime(&startTime); + LicensePlateCandidate lp(frame, regions[z], &config); + lp.recognize(); + getTime(&endTime); + double analysisTime = diffclock(startTime, endTime); + cout << "\tRegion " << z << ": Analysis time: " << analysisTime << "ms." << endl; - getTime(&startTime); - LicensePlateCandidate lp(frame, regions[z], &config); - lp.recognize(); - getTime(&endTime); - double analysisTime = diffclock(startTime, endTime); - cout << "\tRegion " << z << ": Analysis time: " << analysisTime << "ms." << endl; + if (lp.confidence > 10) + { + lpAnalysisPositiveTimes.push_back(analysisTime); - if (lp.confidence > 10) - { - lpAnalysisPositiveTimes.push_back(analysisTime); + getTime(&startTime); + ocr.performOCR(lp.charSegmenter->getThresholds(), lp.charSegmenter->characters); + getTime(&endTime); + double ocrTime = diffclock(startTime, endTime); + cout << "\tRegion " << z << ": OCR time: " << ocrTime << "ms." << endl; + ocrTimes.push_back(ocrTime); + getTime(&startTime); + ocr.postProcessor->analyze("", 25); + getTime(&endTime); + double postProcessTime = diffclock(startTime, endTime); + cout << "\tRegion " << z << ": PostProcess time: " << postProcessTime << "ms." << endl; + postProcessTimes.push_back(postProcessTime); + } + else + { + lpAnalysisNegativeTimes.push_back(analysisTime); + } + } - getTime(&startTime); - ocr.performOCR(lp.charSegmenter->getThresholds(), lp.charSegmenter->characters); - getTime(&endTime); - double ocrTime = diffclock(startTime, endTime); - cout << "\tRegion " << z << ": OCR time: " << ocrTime << "ms." << endl; - ocrTimes.push_back(ocrTime); - - getTime(&startTime); - ocr.postProcessor->analyze("", 25); - getTime(&endTime); - double postProcessTime = diffclock(startTime, endTime); - cout << "\tRegion " << z << ": PostProcess time: " << postProcessTime << "ms." << endl; - postProcessTimes.push_back(postProcessTime); - } - else - { - lpAnalysisNegativeTimes.push_back(analysisTime); - } - } - - waitKey(5); + waitKey(5); } @@ -273,7 +261,6 @@ int main( int argc, const char** argv ) cout << endl << "---------------------" << endl; - cout << "End to End Time Statistics:" << endl; outputStats(endToEndTimes); cout << endl; @@ -315,24 +302,23 @@ int main( int argc, const char** argv ) { if (hasEnding(files[i], ".png")) { - string fullpath = inDir + "/" + files[i]; - frame = imread( fullpath.c_str() ); + string fullpath = inDir + "/" + files[i]; + frame = imread( fullpath.c_str() ); - vector buffer; - imencode(".bmp", frame, buffer ); + vector buffer; + imencode(".bmp", frame, buffer ); - vector results = alpr.recognize(buffer); + vector results = alpr.recognize(buffer); - outputdatafile << files[i] << ": "; - for (int z = 0; z < results.size(); z++) - { - outputdatafile << results[z].bestPlate.characters << ", "; - } - outputdatafile << endl; - - imshow("Current LP", frame); - waitKey(5); + outputdatafile << files[i] << ": "; + for (int z = 0; z < results.size(); z++) + { + outputdatafile << results[z].bestPlate.characters << ", "; + } + outputdatafile << endl; + imshow("Current LP", frame); + waitKey(5); } @@ -350,7 +336,7 @@ void outputStats(vector datapoints) std::vector diff(datapoints.size()); std::transform(datapoints.begin(), datapoints.end(), diff.begin(), - std::bind2nd(std::minus(), mean)); + std::bind2nd(std::minus(), mean)); double sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0); double stdev = std::sqrt(sq_sum / datapoints.size()); diff --git a/src/misc_utilities/classifychars.cpp b/src/misc_utilities/classifychars.cpp index 47c200b..c8f2463 100644 --- a/src/misc_utilities/classifychars.cpp +++ b/src/misc_utilities/classifychars.cpp @@ -31,8 +31,8 @@ #include "support/filesystem.h" #include "ocr.h" - using namespace std; - using namespace cv; +using namespace std; +using namespace cv; // Given a directory full of lp images (named [statecode]#.png) crop out the alphanumeric characters. // These will be used to train the OCR @@ -61,36 +61,34 @@ const int DASHBOARD_COLUMNS = 3; #endif - void showDashboard(vector images, vector selectedImages, int selectedIndex); vector showCharSelection(Mat image, vector charRegions, string state); int main( int argc, const char** argv ) { - string inDir; - string outDir; - Mat frame; - + string inDir; + string outDir; + Mat frame; //Check if user specify image to process if(argc == 3) { - inDir = argv[1]; - outDir = argv[2]; + inDir = argv[1]; + outDir = argv[2]; - - }else{ - printf("Use:\n\t%s indirectory outdirectory\n",argv[0]); - printf("Ex: \n\t%s ./pics/ ./out \n",argv[0]); - return 0; } - + else + { + printf("Use:\n\t%s indirectory outdirectory\n",argv[0]); + printf("Ex: \n\t%s ./pics/ ./out \n",argv[0]); + return 0; + } if (DirectoryExists(outDir.c_str()) == false) { - printf("Output dir does not exist\n"); - return 0; + printf("Output dir does not exist\n"); + return 0; } cout << "Usage: " << endl; @@ -110,172 +108,166 @@ int main( int argc, const char** argv ) if (DirectoryExists(inDir.c_str())) { - vector files = getFilesInDir(inDir.c_str()); + vector files = getFilesInDir(inDir.c_str()); - sort( files.begin(), files.end(), stringCompare ); + sort( files.begin(), files.end(), stringCompare ); - for (int i = 0; i< files.size(); i++) + for (int i = 0; i< files.size(); i++) + { + if (hasEnding(files[i], ".png") || hasEnding(files[i], ".jpg")) { - if (hasEnding(files[i], ".png") || hasEnding(files[i], ".jpg")) - { - string fullpath = inDir + "/" + files[i]; - cout << fullpath << endl; - frame = imread( fullpath.c_str() ); - resize(frame, frame, Size(config->ocrImageWidthPx, config->ocrImageHeightPx)); + string fullpath = inDir + "/" + files[i]; + cout << fullpath << endl; + frame = imread( fullpath.c_str() ); + resize(frame, frame, Size(config->ocrImageWidthPx, config->ocrImageHeightPx)); - imshow ("Original", frame); + imshow ("Original", frame); + char statecode[3]; + statecode[0] = files[i][0]; + statecode[1] = files[i][1]; + statecode[2] = '\0'; + string statecodestr(statecode); - char statecode[3]; - statecode[0] = files[i][0]; - statecode[1] = files[i][1]; - statecode[2] = '\0'; - string statecodestr(statecode); + CharacterRegion regionizer(frame, config); - CharacterRegion regionizer(frame, config); + if (abs(regionizer.getTopLine().angle) > 4) + { + // Rotate image: + Mat rotated(frame.size(), frame.type()); + Mat rot_mat( 2, 3, CV_32FC1 ); + Point center = Point( frame.cols/2, frame.rows/2 ); - if (abs(regionizer.getTopLine().angle) > 4) - { - // Rotate image: - Mat rotated(frame.size(), frame.type()); - Mat rot_mat( 2, 3, CV_32FC1 ); - Point center = Point( frame.cols/2, frame.rows/2 ); + rot_mat = getRotationMatrix2D( center, regionizer.getTopLine().angle, 1.0 ); + warpAffine( frame, rotated, rot_mat, frame.size() ); - rot_mat = getRotationMatrix2D( center, regionizer.getTopLine().angle, 1.0 ); - warpAffine( frame, rotated, rot_mat, frame.size() ); + rotated.copyTo(frame); + } - rotated.copyTo(frame); - } + CharacterSegmenter charSegmenter(frame, regionizer.thresholdsInverted(), config); - CharacterSegmenter charSegmenter(frame, regionizer.thresholdsInverted(), config); + //ocr.cleanCharRegions(charSegmenter.thresholds, charSegmenter.characters); - //ocr.cleanCharRegions(charSegmenter.thresholds, charSegmenter.characters); + ocr.performOCR(charSegmenter.getThresholds(), charSegmenter.characters); + ocr.postProcessor->analyze(statecodestr, 25); + cout << "OCR results: " << ocr.postProcessor->bestChars << endl; - ocr.performOCR(charSegmenter.getThresholds(), charSegmenter.characters); - ocr.postProcessor->analyze(statecodestr, 25); - cout << "OCR results: " << ocr.postProcessor->bestChars << endl; + vector selectedBoxes(charSegmenter.getThresholds().size()); + for (int z = 0; z < charSegmenter.getThresholds().size(); z++) + selectedBoxes[z] = false; + int curDashboardSelection = 0; - vector selectedBoxes(charSegmenter.getThresholds().size()); - for (int z = 0; z < charSegmenter.getThresholds().size(); z++) - selectedBoxes[z] = false; + vector humanInputs(charSegmenter.characters.size()); - int curDashboardSelection = 0; + for (int z = 0; z < charSegmenter.characters.size(); z++) + humanInputs[z] = ' '; - vector humanInputs(charSegmenter.characters.size()); + showDashboard(charSegmenter.getThresholds(), selectedBoxes, 0); - for (int z = 0; z < charSegmenter.characters.size(); z++) - humanInputs[z] = ' '; + char waitkey = (char) waitKey(50); - showDashboard(charSegmenter.getThresholds(), selectedBoxes, 0); + while (waitkey != 'n' && waitkey != 'p') // Next image + { + if (waitkey == LEFT_ARROW_KEY) // left arrow key + { + if (curDashboardSelection > 0) + curDashboardSelection--; + showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection); + } + else if (waitkey == RIGHT_ARROW_KEY) // right arrow key + { + if (curDashboardSelection < charSegmenter.getThresholds().size() - 1) + curDashboardSelection++; + showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection); + } + else if (waitkey == DOWN_ARROW_KEY) + { + if (curDashboardSelection + DASHBOARD_COLUMNS <= charSegmenter.getThresholds().size() - 1) + curDashboardSelection += DASHBOARD_COLUMNS; + showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection); + } + else if (waitkey == UP_ARROW_KEY) + { + if (curDashboardSelection - DASHBOARD_COLUMNS >= 0) + curDashboardSelection -= DASHBOARD_COLUMNS; + showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection); + } + else if (waitkey == ENTER_KEY) + { + vector tempdata = showCharSelection(charSegmenter.getThresholds()[curDashboardSelection], charSegmenter.characters, statecodestr); + for (int c = 0; c < charSegmenter.characters.size(); c++) + humanInputs[c] = tempdata[c]; + } + else if (waitkey == SPACE_KEY) + { + selectedBoxes[curDashboardSelection] = !selectedBoxes[curDashboardSelection]; + showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection); + } + else if (waitkey == 's' || waitkey == 'S') + { + bool somethingSelected = false; + bool chardataTagged = false; + for (int c = 0; c < charSegmenter.getThresholds().size(); c++) + { + if (selectedBoxes[c]) + { + somethingSelected = true; + break; + } + } + for (int c = 0; c < charSegmenter.characters.size(); c++) + { + if (humanInputs[c] != ' ') + { + chardataTagged = true; + break; + } + } + // Save + if (somethingSelected && chardataTagged) + { + for (int c = 0; c < charSegmenter.characters.size(); c++) + { + if (humanInputs[c] == ' ') + continue; + for (int t = 0; t < charSegmenter.getThresholds().size(); t++) + { + if (selectedBoxes[t] == false) + continue; - char waitkey = (char) waitKey(50); + stringstream filename; + Mat cropped = charSegmenter.getThresholds()[t](charSegmenter.characters[c]); + filename << outDir << "/" << humanInputs[c] << "-" << t << "-" << files[i]; + imwrite(filename.str(), cropped); + cout << "Writing char image: " << filename.str() << endl; + } + } + } + else if (somethingSelected == false) + cout << "Did not select any boxes" << endl; + else if (chardataTagged == false) + cout << "You have not tagged any characters" << endl; + } - while (waitkey != 'n' && waitkey != 'p') // Next image - { - if (waitkey == LEFT_ARROW_KEY) // left arrow key - { - if (curDashboardSelection > 0) - curDashboardSelection--; - showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection); - } - else if (waitkey == RIGHT_ARROW_KEY) // right arrow key - { - if (curDashboardSelection < charSegmenter.getThresholds().size() - 1) - curDashboardSelection++; - showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection); - } - else if (waitkey == DOWN_ARROW_KEY) - { - if (curDashboardSelection + DASHBOARD_COLUMNS <= charSegmenter.getThresholds().size() - 1) - curDashboardSelection += DASHBOARD_COLUMNS; - showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection); - } - else if (waitkey == UP_ARROW_KEY) - { - if (curDashboardSelection - DASHBOARD_COLUMNS >= 0) - curDashboardSelection -= DASHBOARD_COLUMNS; - showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection); - } - else if (waitkey == ENTER_KEY) - { - vector tempdata = showCharSelection(charSegmenter.getThresholds()[curDashboardSelection], charSegmenter.characters, statecodestr); - for (int c = 0; c < charSegmenter.characters.size(); c++) - humanInputs[c] = tempdata[c]; - } - else if (waitkey == SPACE_KEY) - { - selectedBoxes[curDashboardSelection] = !selectedBoxes[curDashboardSelection]; - showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection); - } - else if (waitkey == 's' || waitkey == 'S') - { - bool somethingSelected = false; - bool chardataTagged = false; - for (int c = 0; c < charSegmenter.getThresholds().size(); c++) - { - if (selectedBoxes[c]) - { - somethingSelected = true; - break; - } - } - for (int c = 0; c < charSegmenter.characters.size(); c++) - { - if (humanInputs[c] != ' ') - { - chardataTagged = true; - break; - } - } - // Save - if (somethingSelected && chardataTagged) - { + waitkey = (char) waitKey(50); - for (int c = 0; c < charSegmenter.characters.size(); c++) - { - if (humanInputs[c] == ' ') - continue; + } - for (int t = 0; t < charSegmenter.getThresholds().size(); t++) - { - if (selectedBoxes[t] == false) - continue; - - stringstream filename; - Mat cropped = charSegmenter.getThresholds()[t](charSegmenter.characters[c]); - filename << outDir << "/" << humanInputs[c] << "-" << t << "-" << files[i]; - imwrite(filename.str(), cropped); - cout << "Writing char image: " << filename.str() << endl; - } - } - } - else if (somethingSelected == false) - cout << "Did not select any boxes" << endl; - else if (chardataTagged == false) - cout << "You have not tagged any characters" << endl; - } - - waitkey = (char) waitKey(50); - - } - - if (waitkey == 'p') - i = i - 2; - if (i < -1) - i = -1; - - - } + if (waitkey == 'p') + i = i - 2; + if (i < -1) + i = -1; } + + } } } - void showDashboard(vector images, vector selectedImages, int selectedIndex) { vector vecCopy; @@ -309,62 +301,60 @@ void showDashboard(vector images, vector selectedImages, int selected vector showCharSelection(Mat image, vector charRegions, string state) { - int curCharIdx = 0; + int curCharIdx = 0; - vector humanInputs(charRegions.size()); - for (int i = 0; i < charRegions.size(); i++) - humanInputs[i] = (char) SPACE_KEY; + vector humanInputs(charRegions.size()); + for (int i = 0; i < charRegions.size(); i++) + humanInputs[i] = (char) SPACE_KEY; + char waitkey = (char) waitKey(50); + while (waitkey != ENTER_KEY && waitkey != ESCAPE_KEY) + { + Mat imgCopy(image.size(), image.type()); + image.copyTo(imgCopy); + cvtColor(imgCopy, imgCopy, CV_GRAY2BGR); - char waitkey = (char) waitKey(50); - while (waitkey != ENTER_KEY && waitkey != ESCAPE_KEY) + rectangle(imgCopy, charRegions[curCharIdx], Scalar(0, 255, 0), 1); + + imshow("Character selector", imgCopy); + + if (waitkey == LEFT_ARROW_KEY) + curCharIdx--; + else if (waitkey == RIGHT_ARROW_KEY ) + curCharIdx++; + else if ((waitkey >= '0' && waitkey <= '9') || (waitkey >= 'a' && waitkey <= 'z') || waitkey == SPACE_KEY) { - Mat imgCopy(image.size(), image.type()); - image.copyTo(imgCopy); - cvtColor(imgCopy, imgCopy, CV_GRAY2BGR); + // Save the character to disk + humanInputs[curCharIdx] = toupper((char) waitkey); + curCharIdx++; - rectangle(imgCopy, charRegions[curCharIdx], Scalar(0, 255, 0), 1); - - imshow("Character selector", imgCopy); - - if (waitkey == LEFT_ARROW_KEY) - curCharIdx--; - else if (waitkey == RIGHT_ARROW_KEY ) - curCharIdx++; - else if ((waitkey >= '0' && waitkey <= '9') || (waitkey >= 'a' && waitkey <= 'z') || waitkey == SPACE_KEY) - { - // Save the character to disk - humanInputs[curCharIdx] = toupper((char) waitkey); - curCharIdx++; - - if (curCharIdx >= charRegions.size()) - { - waitkey = (char) ENTER_KEY; - break; - } - } - - if (curCharIdx < 0) - curCharIdx = 0; if (curCharIdx >= charRegions.size()) - curCharIdx = charRegions.size() -1; - - waitkey = (char) waitKey(50); + { + waitkey = (char) ENTER_KEY; + break; + } } - if (waitkey == ENTER_KEY) + if (curCharIdx < 0) + curCharIdx = 0; + if (curCharIdx >= charRegions.size()) + curCharIdx = charRegions.size() -1; + + waitkey = (char) waitKey(50); + } + + if (waitkey == ENTER_KEY) + { + // Save all the inputs + for (int i = 0; i < charRegions.size(); i++) { - // Save all the inputs - for (int i = 0; i < charRegions.size(); i++) - { - if (humanInputs[i] != (char) SPACE_KEY) - cout << "Tagged " << state << " char code: '" << humanInputs[i] << "' at char position: " << i << endl; - } - + if (humanInputs[i] != (char) SPACE_KEY) + cout << "Tagged " << state << " char code: '" << humanInputs[i] << "' at char position: " << i << endl; } - destroyWindow("Character selector"); + } + destroyWindow("Character selector"); - return humanInputs; + return humanInputs; } diff --git a/src/misc_utilities/prepcharsfortraining.cpp b/src/misc_utilities/prepcharsfortraining.cpp index 6f51ce1..b49ac33 100644 --- a/src/misc_utilities/prepcharsfortraining.cpp +++ b/src/misc_utilities/prepcharsfortraining.cpp @@ -17,49 +17,46 @@ * along with this program. If not, see . */ - #include "opencv2/highgui/highgui.hpp" - #include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/highgui/highgui.hpp" +#include "opencv2/imgproc/imgproc.hpp" - #include - #include +#include +#include #include #include #include "support/filesystem.h" - using namespace std; - using namespace cv; - - +using namespace std; +using namespace cv; // Takes a directory full of single char images, and plops them on a big tif files // Also creates a box file so Tesseract can recognize it int main( int argc, const char** argv ) { - string inDir; - + string inDir; //Check if user specify image to process if(argc == 2) { - inDir = argv[1]; + inDir = argv[1]; - }else{ - printf("Use:\n\t%s input dir \n",argv[0]); - return 0; } - + else + { + printf("Use:\n\t%s input dir \n",argv[0]); + return 0; + } if (DirectoryExists(inDir.c_str()) == false) { - printf("Output dir does not exist\n"); - return 0; + printf("Output dir does not exist\n"); + return 0; } cout << "Usage: " << endl; cout << "\tinputdir -- input dir for benchmark data" << endl; - if (DirectoryExists(inDir.c_str())) { const int X_OFFSET = 10; @@ -74,97 +71,92 @@ int main( int argc, const char** argv ) const int TILE_HEIGHT = 70; const int CHAR_VERT_OFFSET = 48; - vector files = getFilesInDir(inDir.c_str()); + vector files = getFilesInDir(inDir.c_str()); - sort( files.begin(), files.end(), stringCompare ); + sort( files.begin(), files.end(), stringCompare ); + int tiles_per_row = ((float) (HORIZONTAL_RESOLUTION - (PAGE_MARGIN_X * 2))) / ((float) TILE_WIDTH); + int lines = files.size() / (tiles_per_row); + int vertical_resolution = (lines * TILE_HEIGHT) + (PAGE_MARGIN_Y * 2) ; + cout << tiles_per_row << " : " << vertical_resolution << endl; - int tiles_per_row = ((float) (HORIZONTAL_RESOLUTION - (PAGE_MARGIN_X * 2))) / ((float) TILE_WIDTH); - int lines = files.size() / (tiles_per_row); - int vertical_resolution = (lines * TILE_HEIGHT) + (PAGE_MARGIN_Y * 2) ; - cout << tiles_per_row << " : " << vertical_resolution << endl; + Mat bigTif = Mat::zeros(Size(HORIZONTAL_RESOLUTION, vertical_resolution), CV_8U); + bitwise_not(bigTif, bigTif); - Mat bigTif = Mat::zeros(Size(HORIZONTAL_RESOLUTION, vertical_resolution), CV_8U); - bitwise_not(bigTif, bigTif); + stringstream boxFileOut; - stringstream boxFileOut; + for (int i = 0; i< files.size(); i++) + { + int col = i % tiles_per_row; + int line = i / tiles_per_row; - for (int i = 0; i< files.size(); i++) + int xPos = (col * TILE_WIDTH) + PAGE_MARGIN_X; + int yPos = (line * TILE_HEIGHT) + PAGE_MARGIN_Y; + + if (hasEnding(files[i], ".png") || hasEnding(files[i], ".jpg")) { - int col = i % tiles_per_row; - int line = i / tiles_per_row; + string fullpath = inDir + "/" + files[i]; - int xPos = (col * TILE_WIDTH) + PAGE_MARGIN_X; - int yPos = (line * TILE_HEIGHT) + PAGE_MARGIN_Y; + cout << "Processing file: " << (i + 1) << " of " << files.size() << endl; - if (hasEnding(files[i], ".png") || hasEnding(files[i], ".jpg")) - { - string fullpath = inDir + "/" + files[i]; + char charcode = files[i][0]; - cout << "Processing file: " << (i + 1) << " of " << files.size() << endl; + Mat characterImg = imread(fullpath); + Mat charImgCopy = Mat::zeros(Size(150, 150), characterImg.type()); + bitwise_not(charImgCopy, charImgCopy); - char charcode = files[i][0]; + characterImg.copyTo(charImgCopy(Rect(X_OFFSET, Y_OFFSET, characterImg.cols, characterImg.rows))); + cvtColor(charImgCopy, charImgCopy, CV_BGR2GRAY); + bitwise_not(charImgCopy, charImgCopy); - Mat characterImg = imread(fullpath); - Mat charImgCopy = Mat::zeros(Size(150, 150), characterImg.type()); - bitwise_not(charImgCopy, charImgCopy); + vector > contours; - characterImg.copyTo(charImgCopy(Rect(X_OFFSET, Y_OFFSET, characterImg.cols, characterImg.rows))); - cvtColor(charImgCopy, charImgCopy, CV_BGR2GRAY); - bitwise_not(charImgCopy, charImgCopy); + //imshow("copy", charImgCopy); + findContours(charImgCopy, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); - vector > contours; + Rect tallestRect(0, 0, 0, 0); + for (int c = 0; c < contours.size(); c++) + { + Rect tmpRect = boundingRect(contours[c]); + if (tmpRect.height > tallestRect.height) + tallestRect = tmpRect; + } - //imshow("copy", charImgCopy); - findContours(charImgCopy, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); + //cout << tallestRect.x << ":" << tallestRect.y << " -- " << tallestRect.width << ":" << tallestRect.height << endl; - Rect tallestRect(0, 0, 0, 0); - for (int c = 0; c < contours.size(); c++) - { - Rect tmpRect = boundingRect(contours[c]); - if (tmpRect.height > tallestRect.height) - tallestRect = tmpRect; - } + Rect cropRect(0, tallestRect.y - Y_OFFSET, tallestRect.width, tallestRect.height); - //cout << tallestRect.x << ":" << tallestRect.y << " -- " << tallestRect.width << ":" << tallestRect.height << endl; + //cout << "Cropped: " << cropRect.x << ":" << cropRect.y << " -- " << cropRect.width << ":" << cropRect.height << endl; + Mat cropped(characterImg, cropRect); + cvtColor(cropped, cropped, CV_BGR2GRAY); - Rect cropRect(0, tallestRect.y - Y_OFFSET, tallestRect.width, tallestRect.height); + Rect destinationRect(xPos + (CHAR_HORIZ_OFFSET - tallestRect.width), yPos + (CHAR_VERT_OFFSET - tallestRect.height), tallestRect.width, tallestRect.height); + //cout << "1" << endl; - //cout << "Cropped: " << cropRect.x << ":" << cropRect.y << " -- " << cropRect.width << ":" << cropRect.height << endl; - Mat cropped(characterImg, cropRect); - cvtColor(cropped, cropped, CV_BGR2GRAY); + cropped.copyTo(bigTif(destinationRect)); - Rect destinationRect(xPos + (CHAR_HORIZ_OFFSET - tallestRect.width), yPos + (CHAR_VERT_OFFSET - tallestRect.height), tallestRect.width, tallestRect.height); + int x1= destinationRect.x - 2; + int y1 = (vertical_resolution - destinationRect.y - destinationRect.height) - 2; + int x2 = (destinationRect.x + destinationRect.width) + 2; + int y2 = (vertical_resolution - destinationRect.y) + 2; + //0 70 5602 85 5636 0 + boxFileOut << charcode << " " << x1 << " " << y1 << " "; + boxFileOut << x2 << " " << y2 ; + boxFileOut << " 0" << endl; - //cout << "1" << endl; + //rectangle(characterImg, tallestRect, Scalar(0, 255, 0)); + //imshow("characterImg", cropped); - cropped.copyTo(bigTif(destinationRect)); - - - int x1= destinationRect.x - 2; - int y1 = (vertical_resolution - destinationRect.y - destinationRect.height) - 2; - int x2 = (destinationRect.x + destinationRect.width) + 2; - int y2 = (vertical_resolution - destinationRect.y) + 2; - //0 70 5602 85 5636 0 - boxFileOut << charcode << " " << x1 << " " << y1 << " "; - boxFileOut << x2 << " " << y2 ; - boxFileOut << " 0" << endl; - - //rectangle(characterImg, tallestRect, Scalar(0, 255, 0)); - //imshow("characterImg", cropped); - - waitKey(2); - - - } + waitKey(2); } + } - imwrite("combined.tif", bigTif); - ofstream boxFile("combined.box", std::ios::out); - boxFile << boxFileOut.str(); + imwrite("combined.tif", bigTif); + ofstream boxFile("combined.box", std::ios::out); + boxFile << boxFileOut.str(); } diff --git a/src/misc_utilities/sortstate.cpp b/src/misc_utilities/sortstate.cpp index d43fbe9..36449dd 100644 --- a/src/misc_utilities/sortstate.cpp +++ b/src/misc_utilities/sortstate.cpp @@ -17,11 +17,11 @@ * along with this program. If not, see . */ - #include "opencv2/highgui/highgui.hpp" - #include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/highgui/highgui.hpp" +#include "opencv2/imgproc/imgproc.hpp" - #include - #include +#include +#include #include #include "regiondetector.h" @@ -30,34 +30,34 @@ #include "utility.h" #include "support/filesystem.h" - using namespace std; - using namespace cv; +using namespace std; +using namespace cv; // Given a directory full of pre-cropped images, identify the state that each image belongs to. // This is used to sort our own positive image database as a first step before grabbing characters to use to train the OCR. - bool detectPlate( StateIdentifier* identifier, Mat frame); +bool detectPlate( StateIdentifier* identifier, Mat frame); int main( int argc, const char** argv ) { - string inDir; - string outDir; - Mat frame; - + string inDir; + string outDir; + Mat frame; //Check if user specify image to process if(argc == 3 ) { - inDir = argv[1]; - outDir = argv[2]; - outDir = outDir + "/"; + inDir = argv[1]; + outDir = argv[2]; + outDir = outDir + "/"; - - }else{ - printf("Use:\n\t%s directory \n",argv[0]); - printf("Ex: \n\t%s ./pics/ \n",argv[0]); - return 0; + } + else + { + printf("Use:\n\t%s directory \n",argv[0]); + printf("Ex: \n\t%s ./pics/ \n",argv[0]); + return 0; } Config config("us"); @@ -65,53 +65,52 @@ int main( int argc, const char** argv ) if (DirectoryExists(outDir.c_str()) == false) { - printf("Output dir does not exist\n"); - return 0; + printf("Output dir does not exist\n"); + return 0; } if (DirectoryExists(inDir.c_str())) { - vector files = getFilesInDir(inDir.c_str()); + vector files = getFilesInDir(inDir.c_str()); - for (int i = 0; i< files.size(); i++) + for (int i = 0; i< files.size(); i++) + { + if (hasEnding(files[i], ".png")) { - if (hasEnding(files[i], ".png")) - { - string fullpath = inDir + "/" + files[i]; - cout << fullpath << endl; - frame = imread( fullpath.c_str() ); + string fullpath = inDir + "/" + files[i]; + cout << fullpath << endl; + frame = imread( fullpath.c_str() ); - char code[4]; - int confidence = identifier.recognize(frame, code); + char code[4]; + int confidence = identifier.recognize(frame, code); - if (confidence <= 20) - { - code[0] = 'z'; - code[1] = 'z'; - confidence = 100; - } + if (confidence <= 20) + { + code[0] = 'z'; + code[1] = 'z'; + confidence = 100; + } - //imshow("Plate", frame); - if (confidence > 20) - { - cout << confidence << " : " << code; + //imshow("Plate", frame); + if (confidence > 20) + { + cout << confidence << " : " << code; - ostringstream convert; // stream used for the conversion - convert << i; // insert the textual representation of 'Number' in the characters in the stream - - - string copyCommand = "cp \"" + fullpath + "\" " + outDir + code + convert.str() + ".png"; - system( copyCommand.c_str() ); - waitKey(50); - //while ((char) waitKey(50) != 'c') { } - } - else - waitKey(50); - } + ostringstream convert; // stream used for the conversion + convert << i; // insert the textual representation of 'Number' in the characters in the stream + string copyCommand = "cp \"" + fullpath + "\" " + outDir + code + convert.str() + ".png"; + system( copyCommand.c_str() ); + waitKey(50); + //while ((char) waitKey(50) != 'c') { } + } + else + waitKey(50); } + + } } } - bool detectPlate( StateIdentifier* identifier, Mat frame); +bool detectPlate( StateIdentifier* identifier, Mat frame); diff --git a/src/openalpr/alpr.cpp b/src/openalpr/alpr.cpp index c5e2c9e..be03f49 100644 --- a/src/openalpr/alpr.cpp +++ b/src/openalpr/alpr.cpp @@ -20,9 +20,6 @@ #include "alpr.h" #include "alpr_impl.h" - - - // ALPR code Alpr::Alpr(const std::string country, const std::string runtimeDir) @@ -40,22 +37,19 @@ std::vector Alpr::recognize(std::string filepath) } - std::vector Alpr::recognize(std::vector imageBuffer) { - // Not sure if this actually works + // Not sure if this actually works cv::Mat img = cv::imdecode(Mat(imageBuffer), 1); return impl->recognize(img); } - string Alpr::toJson(const vector< AlprResult > results) { return impl->toJson(results); } - void Alpr::setDetectRegion(bool detectRegion) { impl->setDetectRegion(detectRegion); @@ -74,11 +68,8 @@ bool Alpr::isLoaded() return true; } - - // Results code - AlprResult::AlprResult() { diff --git a/src/openalpr/alpr_impl.cpp b/src/openalpr/alpr_impl.cpp index 1bab02a..22178f0 100644 --- a/src/openalpr/alpr_impl.cpp +++ b/src/openalpr/alpr_impl.cpp @@ -19,7 +19,6 @@ #include "alpr_impl.h" - AlprImpl::AlprImpl(const std::string country, const std::string runtimeDir) { config = new Config(country, runtimeDir); @@ -39,7 +38,7 @@ AlprImpl::AlprImpl(const std::string country, const std::string runtimeDir) for (int i = 0; i < platinfo.size(); i++) { - std::cout << platinfo[i]->platformName << std::endl; + std::cout << platinfo[i]->platformName << std::endl; } cv::ocl::DevicesInfo devices; @@ -68,7 +67,6 @@ AlprImpl::~AlprImpl() delete ocr; } - std::vector AlprImpl::recognize(cv::Mat img) { timespec startTime; @@ -76,100 +74,95 @@ std::vector AlprImpl::recognize(cv::Mat img) vector response; - vector plateRegions = plateDetector->detect(img); - // Recognize. for (int i = 0; i < plateRegions.size(); i++) { - timespec platestarttime; - getTime(&platestarttime); + timespec platestarttime; + getTime(&platestarttime); - LicensePlateCandidate lp(img, plateRegions[i], config); + LicensePlateCandidate lp(img, plateRegions[i], config); - lp.recognize(); + lp.recognize(); + if (lp.confidence > 10) + { + AlprResult plateResult; + plateResult.region = defaultRegion; + plateResult.regionConfidence = 0; - if (lp.confidence > 10) + for (int pointidx = 0; pointidx < 4; pointidx++) { - AlprResult plateResult; - plateResult.region = defaultRegion; - plateResult.regionConfidence = 0; - - for (int pointidx = 0; pointidx < 4; pointidx++) - { - plateResult.plate_points[pointidx].x = (int) lp.plateCorners[pointidx].x; - plateResult.plate_points[pointidx].y = (int) lp.plateCorners[pointidx].y; - } - - if (detectRegion) - { - char statecode[4]; - plateResult.regionConfidence = stateIdentifier->recognize(img, plateRegions[i], statecode); - if (plateResult.regionConfidence > 0) - { - plateResult.region = statecode; - } - } - - - ocr->performOCR(lp.charSegmenter->getThresholds(), lp.charSegmenter->characters); - - ocr->postProcessor->analyze(plateResult.region, topN); - - //plateResult.characters = ocr->postProcessor->bestChars; - const vector ppResults = ocr->postProcessor->getResults(); - - int bestPlateIndex = 0; - - for (int pp = 0; pp < ppResults.size(); pp++) - { - if (pp >= topN) - break; - - // Set our "best plate" match to either the first entry, or the first entry with a postprocessor template match - if (bestPlateIndex == 0 && ppResults[pp].matchesTemplate) - bestPlateIndex = pp; - - if (ppResults[pp].letters.size() >= config->postProcessMinCharacters && - ppResults[pp].letters.size() <= config->postProcessMaxCharacters) - { - AlprPlate aplate; - aplate.characters = ppResults[pp].letters; - aplate.overall_confidence = ppResults[pp].totalscore; - aplate.matches_template = ppResults[pp].matchesTemplate; - plateResult.topNPlates.push_back(aplate); - } - } - plateResult.result_count = plateResult.topNPlates.size(); - - if (plateResult.topNPlates.size() > 0) - plateResult.bestPlate = plateResult.topNPlates[bestPlateIndex]; - - timespec plateEndTime; - getTime(&plateEndTime); - plateResult.processing_time_ms = diffclock(platestarttime, plateEndTime); - - if (plateResult.result_count > 0) - response.push_back(plateResult); - - if (config->debugGeneral) - { - rectangle(img, plateRegions[i], Scalar(0, 255, 0), 2); - for (int z = 0; z < 4; z++) - line(img, lp.plateCorners[z], lp.plateCorners[(z + 1) % 4], Scalar(255,0,255), 2); - } - - + plateResult.plate_points[pointidx].x = (int) lp.plateCorners[pointidx].x; + plateResult.plate_points[pointidx].y = (int) lp.plateCorners[pointidx].y; } - else + + if (detectRegion) { - if (config->debugGeneral) - rectangle(img, plateRegions[i], Scalar(0, 0, 255), 2); + char statecode[4]; + plateResult.regionConfidence = stateIdentifier->recognize(img, plateRegions[i], statecode); + if (plateResult.regionConfidence > 0) + { + plateResult.region = statecode; + } } + ocr->performOCR(lp.charSegmenter->getThresholds(), lp.charSegmenter->characters); + + ocr->postProcessor->analyze(plateResult.region, topN); + + //plateResult.characters = ocr->postProcessor->bestChars; + const vector ppResults = ocr->postProcessor->getResults(); + + int bestPlateIndex = 0; + + for (int pp = 0; pp < ppResults.size(); pp++) + { + if (pp >= topN) + break; + + // Set our "best plate" match to either the first entry, or the first entry with a postprocessor template match + if (bestPlateIndex == 0 && ppResults[pp].matchesTemplate) + bestPlateIndex = pp; + + if (ppResults[pp].letters.size() >= config->postProcessMinCharacters && + ppResults[pp].letters.size() <= config->postProcessMaxCharacters) + { + AlprPlate aplate; + aplate.characters = ppResults[pp].letters; + aplate.overall_confidence = ppResults[pp].totalscore; + aplate.matches_template = ppResults[pp].matchesTemplate; + plateResult.topNPlates.push_back(aplate); + } + } + plateResult.result_count = plateResult.topNPlates.size(); + + if (plateResult.topNPlates.size() > 0) + plateResult.bestPlate = plateResult.topNPlates[bestPlateIndex]; + + timespec plateEndTime; + getTime(&plateEndTime); + plateResult.processing_time_ms = diffclock(platestarttime, plateEndTime); + + if (plateResult.result_count > 0) + response.push_back(plateResult); + + if (config->debugGeneral) + { + rectangle(img, plateRegions[i], Scalar(0, 255, 0), 2); + for (int z = 0; z < 4; z++) + line(img, lp.plateCorners[z], lp.plateCorners[(z + 1) % 4], Scalar(255,0,255), 2); + } + + } + else + { + if (config->debugGeneral) + rectangle(img, plateRegions[i], Scalar(0, 0, 255), 2); + } + } if (config->debugTiming) @@ -210,8 +203,6 @@ string AlprImpl::toJson(const vector< AlprResult > results) return response; } - - cJSON* AlprImpl::createJsonObj(const AlprResult* result) { cJSON *root, *coords, *candidates; @@ -228,7 +219,7 @@ cJSON* AlprImpl::createJsonObj(const AlprResult* result) cJSON_AddNumberToObject(root,"processing_time_ms", result->processing_time_ms); cJSON_AddItemToObject(root, "coordinates", coords=cJSON_CreateArray()); - for (int i=0;i<4;i++) + for (int i=0; i<4; i++) { cJSON *coords_object; coords_object = cJSON_CreateObject(); @@ -238,7 +229,6 @@ cJSON* AlprImpl::createJsonObj(const AlprResult* result) cJSON_AddItemToArray(coords, coords_object); } - cJSON_AddItemToObject(root, "candidates", candidates=cJSON_CreateArray()); for (int i = 0; i < result->topNPlates.size(); i++) { @@ -254,7 +244,6 @@ cJSON* AlprImpl::createJsonObj(const AlprResult* result) return root; } - void AlprImpl::setDetectRegion(bool detectRegion) { this->detectRegion = detectRegion; diff --git a/src/openalpr/binarize_wolf.cpp b/src/openalpr/binarize_wolf.cpp index a80fe7e..a8bf5bd 100644 --- a/src/openalpr/binarize_wolf.cpp +++ b/src/openalpr/binarize_wolf.cpp @@ -33,190 +33,192 @@ #include "binarize_wolf.h" - // ************************************************************* // glide a window across the image and // create two maps: mean and standard deviation. // ************************************************************* +float calcLocalStats (Mat &im, Mat &map_m, Mat &map_s, int winx, int winy) +{ -float calcLocalStats (Mat &im, Mat &map_m, Mat &map_s, int winx, int winy) { + float m,s,max_s; + long sum, sum_sq; + uchar foo; + int wxh = winx/2; + int wyh = winy/2; + int x_firstth= wxh; + int y_lastth = im.rows-wyh-1; + int y_firstth= wyh; + float winarea = winx*winy; - float m,s,max_s; - long sum, sum_sq; - uchar foo; - int wxh = winx/2; - int wyh = winy/2; - int x_firstth= wxh; - int y_lastth = im.rows-wyh-1; - int y_firstth= wyh; - float winarea = winx*winy; + max_s = 0; + for (int j = y_firstth ; j<=y_lastth; j++) + { + // Calculate the initial window at the beginning of the line + sum = sum_sq = 0; + for (int wy=0 ; wy max_s) + max_s = s; + map_m.fset(x_firstth, j, m); + map_s.fset(x_firstth, j, s); + // Shift the window, add and remove new/old values to the histogram + for (int i=1 ; i <= im.cols-winx; i++) + { - max_s = 0; - for (int j = y_firstth ; j<=y_lastth; j++) - { - // Calculate the initial window at the beginning of the line - sum = sum_sq = 0; - for (int wy=0 ; wy max_s) - max_s = s; - map_m.fset(x_firstth, j, m); - map_s.fset(x_firstth, j, s); + // Remove the left old column and add the right new column + for (int wy=0; wy max_s) + max_s = s; + map_m.fset(i+wxh, j, m); + map_s.fset(i+wxh, j, s); + } + } - // Shift the window, add and remove new/old values to the histogram - for (int i=1 ; i <= im.cols-winx; i++) { - - // Remove the left old column and add the right new column - for (int wy=0; wy max_s) - max_s = s; - map_m.fset(i+wxh, j, m); - map_s.fset(i+wxh, j, s); - } - } - - return max_s; + return max_s; } - /********************************************************** * The binarization routine **********************************************************/ - void NiblackSauvolaWolfJolion (Mat im, Mat output, NiblackVersion version, - int winx, int winy, float k) { + int winx, int winy, float k) +{ - float dR = BINARIZEWOLF_DEFAULTDR; + float dR = BINARIZEWOLF_DEFAULTDR; - float m, s, max_s; - float th=0; - double min_I, max_I; - int wxh = winx/2; - int wyh = winy/2; - int x_firstth= wxh; - int x_lastth = im.cols-wxh-1; - int y_lastth = im.rows-wyh-1; - int y_firstth= wyh; - int mx, my; + float m, s, max_s; + float th=0; + double min_I, max_I; + int wxh = winx/2; + int wyh = winy/2; + int x_firstth= wxh; + int x_lastth = im.cols-wxh-1; + int y_lastth = im.rows-wyh-1; + int y_firstth= wyh; + int mx, my; - // Create local statistics and store them in a float matrices - Mat map_m = Mat::zeros (im.rows, im.cols, CV_32F); - Mat map_s = Mat::zeros (im.rows, im.cols, CV_32F); - max_s = calcLocalStats (im, map_m, map_s, winx, winy); + // Create local statistics and store them in a float matrices + Mat map_m = Mat::zeros (im.rows, im.cols, CV_32F); + Mat map_s = Mat::zeros (im.rows, im.cols, CV_32F); + max_s = calcLocalStats (im, map_m, map_s, winx, winy); - minMaxLoc(im, &min_I, &max_I); + minMaxLoc(im, &min_I, &max_I); - Mat thsurf (im.rows, im.cols, CV_32F); + Mat thsurf (im.rows, im.cols, CV_32F); - // Create the threshold surface, including border processing - // ---------------------------------------------------- + // Create the threshold surface, including border processing + // ---------------------------------------------------- - for (int j = y_firstth ; j<=y_lastth; j++) { + for (int j = y_firstth ; j<=y_lastth; j++) + { - // NORMAL, NON-BORDER AREA IN THE MIDDLE OF THE WINDOW: - for (int i=0 ; i <= im.cols-winx; i++) { + // NORMAL, NON-BORDER AREA IN THE MIDDLE OF THE WINDOW: + for (int i=0 ; i <= im.cols-winx; i++) + { - m = map_m.fget(i+wxh, j); - s = map_s.fget(i+wxh, j); + m = map_m.fget(i+wxh, j); + s = map_s.fget(i+wxh, j); - // Calculate the threshold - switch (version) { + // Calculate the threshold + switch (version) + { - case NIBLACK: - th = m + k*s; - break; + case NIBLACK: + th = m + k*s; + break; - case SAUVOLA: - th = m * (1 + k*(s/dR-1)); - break; + case SAUVOLA: + th = m * (1 + k*(s/dR-1)); + break; - case WOLFJOLION: - th = m + k * (s/max_s-1) * (m-min_I); - break; + case WOLFJOLION: + th = m + k * (s/max_s-1) * (m-min_I); + break; - default: - cerr << "Unknown threshold type in ImageThresholder::surfaceNiblackImproved()\n"; - exit (1); - } + default: + cerr << "Unknown threshold type in ImageThresholder::surfaceNiblackImproved()\n"; + exit (1); + } - thsurf.fset(i+wxh,j,th); + thsurf.fset(i+wxh,j,th); - if (i==0) { - // LEFT BORDER - for (int i=0; i<=x_firstth; ++i) - thsurf.fset(i,j,th); + if (i==0) + { + // LEFT BORDER + for (int i=0; i<=x_firstth; ++i) + thsurf.fset(i,j,th); - // LEFT-UPPER CORNER - if (j==y_firstth) - for (int u=0; u= thsurf.fget(x,y)) - { - output.uset(x,y,255); - } - else - { - output.uset(x,y,0); - } + for (int y=0; y= thsurf.fget(x,y)) + { + output.uset(x,y,255); + } + else + { + output.uset(x,y,0); + } } } diff --git a/src/openalpr/characteranalysis.cpp b/src/openalpr/characteranalysis.cpp index c15a112..b9b3c39 100644 --- a/src/openalpr/characteranalysis.cpp +++ b/src/openalpr/characteranalysis.cpp @@ -29,13 +29,12 @@ CharacterAnalysis::CharacterAnalysis(Mat img, Config* config) if (this->config->debugCharAnalysis) cout << "Starting CharacterAnalysis identification" << endl; - if (img.type() != CV_8U) cvtColor( img, this->img_gray, CV_BGR2GRAY ); else { - img_gray = Mat(img.size(), img.type()); - img.copyTo(img_gray); + img_gray = Mat(img.size(), img.type()); + img.copyTo(img_gray); } } @@ -44,20 +43,16 @@ CharacterAnalysis::~CharacterAnalysis() { for (int i = 0; i < thresholds.size(); i++) { - thresholds[i].release(); + thresholds[i].release(); } thresholds.clear(); } - - void CharacterAnalysis::analyze() { - thresholds = produceThresholds(img_gray, config); - /* // Morph Close the gray image to make it easier to detect blobs int morph_elem = 1; @@ -73,11 +68,9 @@ void CharacterAnalysis::analyze() } */ - timespec startTime; getTime(&startTime); - for (int i = 0; i < thresholds.size(); i++) { vector > contours; @@ -86,18 +79,15 @@ void CharacterAnalysis::analyze() Mat tempThreshold(thresholds[i].size(), CV_8U); thresholds[i].copyTo(tempThreshold); findContours(tempThreshold, - contours, // a vector of contours - hierarchy, - CV_RETR_TREE, // retrieve all contours - CV_CHAIN_APPROX_SIMPLE ); // all pixels of each contours + contours, // a vector of contours + hierarchy, + CV_RETR_TREE, // retrieve all contours + CV_CHAIN_APPROX_SIMPLE ); // all pixels of each contours allContours.push_back(contours); allHierarchy.push_back(hierarchy); } - - - if (config->debugTiming) { timespec endTime; @@ -106,7 +96,6 @@ void CharacterAnalysis::analyze() } //Mat img_equalized = equalizeBrightness(img_gray); - getTime(&startTime); for (int i = 0; i < thresholds.size(); i++) @@ -125,8 +114,6 @@ void CharacterAnalysis::analyze() cout << " -- Character Analysis Filter Time: " << diffclock(startTime, endTime) << "ms." << endl; } - - this->plateMask = findOuterBoxMask(); if (hasPlateMask) @@ -138,7 +125,6 @@ void CharacterAnalysis::analyze() } } - int bestFitScore = -1; int bestFitIndex = -1; for (int i = 0; i < thresholds.size(); i++) @@ -149,7 +135,6 @@ void CharacterAnalysis::analyze() int segmentCount = getGoodIndicesCount(charSegments[i]); - if (segmentCount > bestFitScore) { bestFitScore = segmentCount; @@ -165,11 +150,9 @@ void CharacterAnalysis::analyze() if (this->config->debugCharAnalysis) cout << "Best fit score: " << bestFitScore << " Index: " << bestFitIndex << endl; - if (bestFitScore <= 1) return; - //getColorMask(img, allContours, allHierarchy, charSegments); if (this->config->debugCharAnalysis) @@ -182,29 +165,25 @@ void CharacterAnalysis::analyze() vector > allowedContours; for (int i = 0; i < bestContours.size(); i++) { - if (bestCharSegments[i]) - allowedContours.push_back(bestContours[i]); + if (bestCharSegments[i]) + allowedContours.push_back(bestContours[i]); } drawContours(img_contours, bestContours, - -1, // draw all contours - cv::Scalar(255,0,0), // in blue - 1); // with a thickness of 1 + -1, // draw all contours + cv::Scalar(255,0,0), // in blue + 1); // with a thickness of 1 drawContours(img_contours, allowedContours, - -1, // draw all contours - cv::Scalar(0,255,0), // in green - 1); // with a thickness of 1 - + -1, // draw all contours + cv::Scalar(0,255,0), // in green + 1); // with a thickness of 1 displayImage(config, "Matching Contours", img_contours); } - //charsegments = this->getPossibleCharRegions(img_threshold, allContours, allHierarchy, STARTING_MIN_HEIGHT + (bestFitIndex * HEIGHT_STEP), STARTING_MAX_HEIGHT + (bestFitIndex * HEIGHT_STEP)); - - this->linePolygon = getBestVotedLines(img_gray, bestContours, bestCharSegments); if (this->linePolygon.size() > 0) @@ -223,14 +202,11 @@ void CharacterAnalysis::analyze() this->charBoxLeft = LineSegment(this->charArea[3].x, this->charArea[3].y, this->charArea[0].x, this->charArea[0].y); this->charBoxRight = LineSegment(this->charArea[2].x, this->charArea[2].y, this->charArea[1].x, this->charArea[1].y); - } } this->thresholdsInverted = isPlateInverted(); - - } int CharacterAnalysis::getGoodIndicesCount(vector goodIndices) @@ -245,8 +221,6 @@ int CharacterAnalysis::getGoodIndicesCount(vector goodIndices) return count; } - - Mat CharacterAnalysis::findOuterBoxMask() { double min_parent_area = config->templateHeightPx * config->templateWidthPx * 0.10; // Needs to be at least 10% of the plate area to be considered. @@ -256,7 +230,6 @@ Mat CharacterAnalysis::findOuterBoxMask() int bestCharCount = 0; double lowestArea = 99999999999999; - if (this->config->debugCharAnalysis) cout << "CharacterAnalysis::findOuterBoxMask" << endl; @@ -272,8 +245,8 @@ Mat CharacterAnalysis::findOuterBoxMask() if (charSegments[imgIndex][i]) charsRecognized++; if (charSegments[imgIndex][i] && allHierarchy[imgIndex][i][3] != -1) { - parentId = allHierarchy[imgIndex][i][3]; - hasParent = true; + parentId = allHierarchy[imgIndex][i][3]; + hasParent = true; } } @@ -284,28 +257,24 @@ Mat CharacterAnalysis::findOuterBoxMask() { double boxArea = contourArea(allContours[imgIndex][parentId]); if (boxArea < min_parent_area) - continue; + continue; if ((charsRecognized > bestCharCount) || - (charsRecognized == bestCharCount && boxArea < lowestArea)) - //(boxArea < lowestArea) + (charsRecognized == bestCharCount && boxArea < lowestArea)) + //(boxArea < lowestArea) { - bestCharCount = charsRecognized; - winningIndex = imgIndex; - winningParentId = parentId; - lowestArea = boxArea; + bestCharCount = charsRecognized; + winningIndex = imgIndex; + winningParentId = parentId; + lowestArea = boxArea; } } - } if (this->config->debugCharAnalysis) cout << "Winning image index is: " << winningIndex << endl; - - - if (winningIndex != -1 && bestCharCount >= 3) { int longestChildIndex = -1; @@ -313,36 +282,31 @@ Mat CharacterAnalysis::findOuterBoxMask() // Find the child with the longest permiter/arc length ( just for kicks) for (int i = 0; i < allContours[winningIndex].size(); i++) { - for (int j = 0; j < allContours[winningIndex].size(); j++) - { - if (allHierarchy[winningIndex][j][3] == winningParentId) - { - double arclength = arcLength(allContours[winningIndex][j], false); - if (arclength > longestChildLength) - { - longestChildIndex = j; - longestChildLength = arclength; - } - } - } + for (int j = 0; j < allContours[winningIndex].size(); j++) + { + if (allHierarchy[winningIndex][j][3] == winningParentId) + { + double arclength = arcLength(allContours[winningIndex][j], false); + if (arclength > longestChildLength) + { + longestChildIndex = j; + longestChildLength = arclength; + } + } + } } - - - - Mat mask = Mat::zeros(thresholds[winningIndex].size(), CV_8U); // get rid of the outline by drawing a 1 pixel width black line drawContours(mask, allContours[winningIndex], - winningParentId, // draw this contour - cv::Scalar(255,255,255), // in - CV_FILLED, - 8, - allHierarchy[winningIndex], - 0 - ); - + winningParentId, // draw this contour + cv::Scalar(255,255,255), // in + CV_FILLED, + 8, + allHierarchy[winningIndex], + 0 + ); // Morph Open the mask to get rid of any little connectors to non-plate portions int morph_elem = 2; @@ -356,7 +320,6 @@ Mat CharacterAnalysis::findOuterBoxMask() //element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) ); //dilate(mask, mask, element); - // Drawing the edge black effectively erodes the image. This may clip off some extra junk from the edges. // We'll want to do the contour again and find the larges one so that we remove the clipped portion. @@ -368,33 +331,31 @@ Mat CharacterAnalysis::findOuterBoxMask() for (int c = 0; c < contoursSecondRound.size(); c++) { double area = contourArea(contoursSecondRound[c]); - if (area > largestArea) - { - biggestContourIndex = c; - largestArea = area; - } + if (area > largestArea) + { + biggestContourIndex = c; + largestArea = area; + } } if (biggestContourIndex != -1) { - mask = Mat::zeros(thresholds[winningIndex].size(), CV_8U); + mask = Mat::zeros(thresholds[winningIndex].size(), CV_8U); - vector smoothedMaskPoints; - approxPolyDP(contoursSecondRound[biggestContourIndex], smoothedMaskPoints, 2, true); + vector smoothedMaskPoints; + approxPolyDP(contoursSecondRound[biggestContourIndex], smoothedMaskPoints, 2, true); - vector > tempvec; - tempvec.push_back(smoothedMaskPoints); - //fillPoly(mask, smoothedMaskPoints.data(), smoothedMaskPoints, Scalar(255,255,255)); + vector > tempvec; + tempvec.push_back(smoothedMaskPoints); + //fillPoly(mask, smoothedMaskPoints.data(), smoothedMaskPoints, Scalar(255,255,255)); drawContours(mask, tempvec, - 0, // draw this contour - cv::Scalar(255,255,255), // in - CV_FILLED, - 8, - allHierarchy[winningIndex], - 0 - ); - - + 0, // draw this contour + cv::Scalar(255,255,255), // in + CV_FILLED, + 8, + allHierarchy[winningIndex], + 0 + ); } @@ -422,48 +383,41 @@ Mat CharacterAnalysis::findOuterBoxMask() bitwise_not(fullMask, fullMask); return fullMask; - - } - Mat CharacterAnalysis::getCharacterMask() { - Mat charMask = Mat::zeros(bestThreshold.size(), CV_8U); + Mat charMask = Mat::zeros(bestThreshold.size(), CV_8U); - for (int i = 0; i < bestContours.size(); i++) - { - if (bestCharSegments[i] == false) - continue; + for (int i = 0; i < bestContours.size(); i++) + { + if (bestCharSegments[i] == false) + continue; + drawContours(charMask, bestContours, + i, // draw this contour + cv::Scalar(255,255,255), // in + CV_FILLED, + 8, + bestHierarchy, + 1 + ); - drawContours(charMask, bestContours, - i, // draw this contour - cv::Scalar(255,255,255), // in - CV_FILLED, - 8, - bestHierarchy, - 1 - ); + // get rid of the outline by drawing a 1 pixel width black line + drawContours(charMask, bestContours, + i, // draw this contour + cv::Scalar(0,0,0), // in + 1, + 8, + bestHierarchy, + 1 + ); + } - // get rid of the outline by drawing a 1 pixel width black line - drawContours(charMask, bestContours, - i, // draw this contour - cv::Scalar(0,0,0), // in - 1, - 8, - bestHierarchy, - 1 - ); - } - - - return charMask; + return charMask; } - - // Returns a polygon "stripe" across the width of the character region. The lines are voted and the polygon starts at 0 and extends to image width vector CharacterAnalysis::getBestVotedLines(Mat img, vector > contours, vector goodIndices) { @@ -481,161 +435,150 @@ vector CharacterAnalysis::getBestVotedLines(Mat img, vector charRegions.push_back(boundingRect(contours[i])); } - - // Find the best fit line segment that is parallel with the most char segments - if (charRegions.size() <= 1) + // Find the best fit line segment that is parallel with the most char segments + if (charRegions.size() <= 1) + { + // Maybe do something about this later, for now let's just ignore + } + else + { + vector topLines; + vector bottomLines; + // Iterate through each possible char and find all possible lines for the top and bottom of each char segment + for (int i = 0; i < charRegions.size() - 1; i++) { - // Maybe do something about this later, for now let's just ignore - } - else - { - vector topLines; - vector bottomLines; - // Iterate through each possible char and find all possible lines for the top and bottom of each char segment - for (int i = 0; i < charRegions.size() - 1; i++) + for (int k = i+1; k < charRegions.size(); k++) { - for (int k = i+1; k < charRegions.size(); k++) - { - //Mat tempImg; - //result.copyTo(tempImg); + //Mat tempImg; + //result.copyTo(tempImg); + Rect* leftRect; + Rect* rightRect; + if (charRegions[i].x < charRegions[k].x) + { + leftRect = &charRegions[i]; + rightRect = &charRegions[k]; + } + else + { + leftRect = &charRegions[k]; + rightRect = &charRegions[i]; + } - Rect* leftRect; - Rect* rightRect; - if (charRegions[i].x < charRegions[k].x) - { - leftRect = &charRegions[i]; - rightRect = &charRegions[k]; - } - else - { - leftRect = &charRegions[k]; - rightRect = &charRegions[i]; - } + //rectangle(tempImg, *leftRect, Scalar(0, 255, 0), 2); + //rectangle(tempImg, *rightRect, Scalar(255, 255, 255), 2); - //rectangle(tempImg, *leftRect, Scalar(0, 255, 0), 2); - //rectangle(tempImg, *rightRect, Scalar(255, 255, 255), 2); + int x1, y1, x2, y2; - int x1, y1, x2, y2; + if (leftRect->y > rightRect->y) // Rising line, use the top left corner of the rect + { + x1 = leftRect->x; + x2 = rightRect->x; + } + else // falling line, use the top right corner of the rect + { + x1 = leftRect->x + leftRect->width; + x2 = rightRect->x + rightRect->width; + } + y1 = leftRect->y; + y2 = rightRect->y; - if (leftRect->y > rightRect->y) // Rising line, use the top left corner of the rect - { - x1 = leftRect->x; - x2 = rightRect->x; - } - else // falling line, use the top right corner of the rect - { - x1 = leftRect->x + leftRect->width; - x2 = rightRect->x + rightRect->width; - } - y1 = leftRect->y; - y2 = rightRect->y; + //cv::line(tempImg, Point(x1, y1), Point(x2, y2), Scalar(0, 0, 255)); + topLines.push_back(LineSegment(x1, y1, x2, y2)); - //cv::line(tempImg, Point(x1, y1), Point(x2, y2), Scalar(0, 0, 255)); - topLines.push_back(LineSegment(x1, y1, x2, y2)); + if (leftRect->y > rightRect->y) // Rising line, use the bottom right corner of the rect + { + x1 = leftRect->x + leftRect->width; + x2 = rightRect->x + rightRect->width; + } + else // falling line, use the bottom left corner of the rect + { + x1 = leftRect->x; + x2 = rightRect->x; + } + y1 = leftRect->y + leftRect->height; + y2 = rightRect->y + leftRect->height; + //cv::line(tempImg, Point(x1, y1), Point(x2, y2), Scalar(0, 0, 255)); + bottomLines.push_back(LineSegment(x1, y1, x2, y2)); - if (leftRect->y > rightRect->y) // Rising line, use the bottom right corner of the rect - { - x1 = leftRect->x + leftRect->width; - x2 = rightRect->x + rightRect->width; - } - else // falling line, use the bottom left corner of the rect - { - x1 = leftRect->x; - x2 = rightRect->x; - } - y1 = leftRect->y + leftRect->height; - y2 = rightRect->y + leftRect->height; - - //cv::line(tempImg, Point(x1, y1), Point(x2, y2), Scalar(0, 0, 255)); - bottomLines.push_back(LineSegment(x1, y1, x2, y2)); - - //drawAndWait(&tempImg); - } + //drawAndWait(&tempImg); } - - int bestScoreIndex = 0; - int bestScore = -1; - int bestScoreDistance = -1; // Line segment distance is used as a tie breaker - - // Now, among all possible lines, find the one that is the best fit - for (int i = 0; i < topLines.size(); i++) - { - float SCORING_MIN_THRESHOLD = 0.97; - float SCORING_MAX_THRESHOLD = 1.03; - - - int curScore = 0; - for (int charidx = 0; charidx < charRegions.size(); charidx++) - { - - float topYPos = topLines[i].getPointAt(charRegions[charidx].x); - float botYPos = bottomLines[i].getPointAt(charRegions[charidx].x); - - float minTop = charRegions[charidx].y * SCORING_MIN_THRESHOLD; - float maxTop = charRegions[charidx].y * SCORING_MAX_THRESHOLD; - float minBot = (charRegions[charidx].y + charRegions[charidx].height) * SCORING_MIN_THRESHOLD; - float maxBot = (charRegions[charidx].y + charRegions[charidx].height) * SCORING_MAX_THRESHOLD; - if ( (topYPos >= minTop && topYPos <= maxTop) && - (botYPos >= minBot && botYPos <= maxBot)) - { - curScore++; - } - - //cout << "Slope: " << topslope << " yPos: " << topYPos << endl; - //drawAndWait(&tempImg); - - } - - - // Tie goes to the one with longer line segments - if ((curScore > bestScore) || - (curScore == bestScore && topLines[i].length > bestScoreDistance)) - { - bestScore = curScore; - bestScoreIndex = i; - // Just use x distance for now - bestScoreDistance = topLines[i].length; - } - } - - - if (this->config->debugCharAnalysis) - { - cout << "The winning score is: " << bestScore << endl; - // Draw the winning line segment - //Mat tempImg; - //result.copyTo(tempImg); - //cv::line(tempImg, topLines[bestScoreIndex].p1, topLines[bestScoreIndex].p2, Scalar(0, 0, 255), 2); - //cv::line(tempImg, bottomLines[bestScoreIndex].p1, bottomLines[bestScoreIndex].p2, Scalar(0, 0, 255), 2); - - //displayImage(config, "Lines", tempImg); - } - - //winningLines.push_back(topLines[bestScoreIndex]); - //winningLines.push_back(bottomLines[bestScoreIndex]); - - Point topLeft = Point(0, topLines[bestScoreIndex].getPointAt(0) ); - Point topRight = Point(img.cols, topLines[bestScoreIndex].getPointAt(img.cols)); - Point bottomRight = Point(img.cols, bottomLines[bestScoreIndex].getPointAt(img.cols)); - Point bottomLeft = Point(0, bottomLines[bestScoreIndex].getPointAt(0)); - - - bestStripe.push_back(topLeft); - bestStripe.push_back(topRight); - bestStripe.push_back(bottomRight); - bestStripe.push_back(bottomLeft); - - } + int bestScoreIndex = 0; + int bestScore = -1; + int bestScoreDistance = -1; // Line segment distance is used as a tie breaker - return bestStripe; + // Now, among all possible lines, find the one that is the best fit + for (int i = 0; i < topLines.size(); i++) + { + float SCORING_MIN_THRESHOLD = 0.97; + float SCORING_MAX_THRESHOLD = 1.03; + + int curScore = 0; + for (int charidx = 0; charidx < charRegions.size(); charidx++) + { + + float topYPos = topLines[i].getPointAt(charRegions[charidx].x); + float botYPos = bottomLines[i].getPointAt(charRegions[charidx].x); + + float minTop = charRegions[charidx].y * SCORING_MIN_THRESHOLD; + float maxTop = charRegions[charidx].y * SCORING_MAX_THRESHOLD; + float minBot = (charRegions[charidx].y + charRegions[charidx].height) * SCORING_MIN_THRESHOLD; + float maxBot = (charRegions[charidx].y + charRegions[charidx].height) * SCORING_MAX_THRESHOLD; + if ( (topYPos >= minTop && topYPos <= maxTop) && + (botYPos >= minBot && botYPos <= maxBot)) + { + curScore++; + } + + //cout << "Slope: " << topslope << " yPos: " << topYPos << endl; + //drawAndWait(&tempImg); + + } + + // Tie goes to the one with longer line segments + if ((curScore > bestScore) || + (curScore == bestScore && topLines[i].length > bestScoreDistance)) + { + bestScore = curScore; + bestScoreIndex = i; + // Just use x distance for now + bestScoreDistance = topLines[i].length; + } + } + + if (this->config->debugCharAnalysis) + { + cout << "The winning score is: " << bestScore << endl; + // Draw the winning line segment + //Mat tempImg; + //result.copyTo(tempImg); + //cv::line(tempImg, topLines[bestScoreIndex].p1, topLines[bestScoreIndex].p2, Scalar(0, 0, 255), 2); + //cv::line(tempImg, bottomLines[bestScoreIndex].p1, bottomLines[bestScoreIndex].p2, Scalar(0, 0, 255), 2); + + //displayImage(config, "Lines", tempImg); + } + + //winningLines.push_back(topLines[bestScoreIndex]); + //winningLines.push_back(bottomLines[bestScoreIndex]); + + Point topLeft = Point(0, topLines[bestScoreIndex].getPointAt(0) ); + Point topRight = Point(img.cols, topLines[bestScoreIndex].getPointAt(img.cols)); + Point bottomRight = Point(img.cols, bottomLines[bestScoreIndex].getPointAt(img.cols)); + Point bottomLeft = Point(0, bottomLines[bestScoreIndex].getPointAt(0)); + + bestStripe.push_back(topLeft); + bestStripe.push_back(topRight); + bestStripe.push_back(bottomRight); + bestStripe.push_back(bottomLeft); + + } + + return bestStripe; } - - vector CharacterAnalysis::filter(Mat img, vector > contours, vector hierarchy) { static int STARTING_MIN_HEIGHT = round (((float) img.rows) * config->charAnalysisMinPercent); @@ -643,7 +586,6 @@ vector CharacterAnalysis::filter(Mat img, vector > contours, static int HEIGHT_STEP = round (((float) img.rows) * config->charAnalysisHeightStepSize); static int NUM_STEPS = config->charAnalysisNumSteps; - vector charSegments; int bestFitScore = -1; for (int i = 0; i < NUM_STEPS; i++) @@ -655,7 +597,6 @@ vector CharacterAnalysis::filter(Mat img, vector > contours, goodIndices = this->filterByBoxSize(contours, goodIndices, STARTING_MIN_HEIGHT + (i * HEIGHT_STEP), STARTING_MAX_HEIGHT + (i * HEIGHT_STEP)); - goodIndicesCount = getGoodIndicesCount(goodIndices); if ( goodIndicesCount > 0 && goodIndicesCount <= bestFitScore) // Don't bother doing more filtering if we already lost... continue; @@ -668,7 +609,6 @@ vector CharacterAnalysis::filter(Mat img, vector > contours, vector lines = getBestVotedLines(img, contours, goodIndices); goodIndices = this->filterBetweenLines(img, contours, hierarchy, lines, goodIndices); - int segmentCount = getGoodIndicesCount(goodIndices); if (segmentCount > bestFitScore) @@ -678,227 +618,216 @@ vector CharacterAnalysis::filter(Mat img, vector > contours, } } - return charSegments; } - // Goes through the contours for the plate and picks out possible char segments based on min/max height vector CharacterAnalysis::filterByBoxSize(vector< vector< Point> > contours, vector goodIndices, int minHeightPx, int maxHeightPx) { - float idealAspect=config->charWidthMM / config->charHeightMM; - float aspecttolerance=0.25; + float idealAspect=config->charWidthMM / config->charHeightMM; + float aspecttolerance=0.25; + vector includedIndices(contours.size()); + for (int j = 0; j < contours.size(); j++) + includedIndices.push_back(false); - vector includedIndices(contours.size()); - for (int j = 0; j < contours.size(); j++) - includedIndices.push_back(false); + for (int i = 0; i < contours.size(); i++) + { + if (goodIndices[i] == false) + continue; - for (int i = 0; i < contours.size(); i++) + //Create bounding rect of object + Rect mr= boundingRect(contours[i]); + + float minWidth = mr.height * 0.2; + //Crop image + //Mat auxRoi(img, mr); + if(mr.height >= minHeightPx && mr.height <= maxHeightPx && mr.width > minWidth) { - if (goodIndices[i] == false) - continue; - //Create bounding rect of object - Rect mr= boundingRect(contours[i]); + float charAspect= (float)mr.width/(float)mr.height; - float minWidth = mr.height * 0.2; - //Crop image - //Mat auxRoi(img, mr); - if(mr.height >= minHeightPx && mr.height <= maxHeightPx && mr.width > minWidth){ - - float charAspect= (float)mr.width/(float)mr.height; - - if (abs(charAspect - idealAspect) < aspecttolerance) - includedIndices[i] = true; - } + if (abs(charAspect - idealAspect) < aspecttolerance) + includedIndices[i] = true; } + } - return includedIndices; + return includedIndices; } - - vector< bool > CharacterAnalysis::filterContourHoles(vector< vector< Point > > contours, vector< Vec4i > hierarchy, vector< bool > goodIndices) { - vector includedIndices(contours.size()); - for (int j = 0; j < contours.size(); j++) - includedIndices.push_back(false); + vector includedIndices(contours.size()); + for (int j = 0; j < contours.size(); j++) + includedIndices.push_back(false); - for (int i = 0; i < contours.size(); i++) + for (int i = 0; i < contours.size(); i++) + { + if (goodIndices[i] == false) + continue; + + int parentIndex = hierarchy[i][3]; + + if (parentIndex >= 0 && goodIndices[parentIndex] == true) { - if (goodIndices[i] == false) - continue; - - int parentIndex = hierarchy[i][3]; - - if (parentIndex >= 0 && goodIndices[parentIndex] == true) - { - // this contour is a child of an already identified contour. REMOVE it - if (this->config->debugCharAnalysis) - { - cout << "filterContourHoles: contour index: " << i << endl; - } - } - else - { - includedIndices[i] = true; - } + // this contour is a child of an already identified contour. REMOVE it + if (this->config->debugCharAnalysis) + { + cout << "filterContourHoles: contour index: " << i << endl; + } } + else + { + includedIndices[i] = true; + } + } - return includedIndices; + return includedIndices; } - // Goes through the contours for the plate and picks out possible char segments based on min/max height // returns a vector of indices corresponding to valid contours vector CharacterAnalysis::filterByParentContour( vector< vector< Point> > contours, vector hierarchy, vector goodIndices) { - vector includedIndices(contours.size()); - for (int j = 0; j < contours.size(); j++) - includedIndices[j] = false; + vector includedIndices(contours.size()); + for (int j = 0; j < contours.size(); j++) + includedIndices[j] = false; - vector parentIDs; - vector votes; + vector parentIDs; + vector votes; - for (int i = 0; i < contours.size(); i++) + for (int i = 0; i < contours.size(); i++) + { + if (goodIndices[i] == false) + continue; + + int voteIndex = -1; + int parentID = hierarchy[i][3]; + // check if parentID is already in the lsit + for (int j = 0; j < parentIDs.size(); j++) { - if (goodIndices[i] == false) - continue; - - int voteIndex = -1; - int parentID = hierarchy[i][3]; - // check if parentID is already in the lsit - for (int j = 0; j < parentIDs.size(); j++) - { - if (parentIDs[j] == parentID) - { - voteIndex = j; - break; - } - } - if (voteIndex == -1) - { - parentIDs.push_back(parentID); - votes.push_back(1); - } - else - { - votes[voteIndex] = votes[voteIndex] + 1; - } - - } - - // Tally up the votes, pick the winner - int totalVotes = 0; - int winningParentId = 0; - int highestVotes = 0; - for (int i = 0; i < parentIDs.size(); i++) - { - if (votes[i] > highestVotes) + if (parentIDs[j] == parentID) { - winningParentId = parentIDs[i]; - highestVotes = votes[i]; + voteIndex = j; + break; } - totalVotes += votes[i]; } - - // Now filter out all the contours with a different parent ID (assuming the totalVotes > 2) - for (int i = 0; i < contours.size(); i++) + if (voteIndex == -1) { - if (goodIndices[i] == false) - continue; - - if (totalVotes <= 2) - { - includedIndices[i] = true; - } - else if (hierarchy[i][3] == winningParentId) - { - includedIndices[i] = true; - } + parentIDs.push_back(parentID); + votes.push_back(1); + } + else + { + votes[voteIndex] = votes[voteIndex] + 1; } - return includedIndices; -} + } + // Tally up the votes, pick the winner + int totalVotes = 0; + int winningParentId = 0; + int highestVotes = 0; + for (int i = 0; i < parentIDs.size(); i++) + { + if (votes[i] > highestVotes) + { + winningParentId = parentIDs[i]; + highestVotes = votes[i]; + } + totalVotes += votes[i]; + } + + // Now filter out all the contours with a different parent ID (assuming the totalVotes > 2) + for (int i = 0; i < contours.size(); i++) + { + if (goodIndices[i] == false) + continue; + + if (totalVotes <= 2) + { + includedIndices[i] = true; + } + else if (hierarchy[i][3] == winningParentId) + { + includedIndices[i] = true; + } + } + + return includedIndices; +} vector CharacterAnalysis::filterBetweenLines(Mat img, vector > contours, vector hierarchy, vector outerPolygon, vector goodIndices) { - static float MIN_AREA_PERCENT_WITHIN_LINES = 0.88; + static float MIN_AREA_PERCENT_WITHIN_LINES = 0.88; - vector includedIndices(contours.size()); - for (int j = 0; j < contours.size(); j++) - includedIndices[j] = false; + vector includedIndices(contours.size()); + for (int j = 0; j < contours.size(); j++) + includedIndices[j] = false; + if (outerPolygon.size() == 0) + return includedIndices; - if (outerPolygon.size() == 0) - return includedIndices; + vector validPoints; - vector validPoints; + // Figure out the line height + LineSegment topLine(outerPolygon[0].x, outerPolygon[0].y, outerPolygon[1].x, outerPolygon[1].y); + LineSegment bottomLine(outerPolygon[3].x, outerPolygon[3].y, outerPolygon[2].x, outerPolygon[2].y); - // Figure out the line height - LineSegment topLine(outerPolygon[0].x, outerPolygon[0].y, outerPolygon[1].x, outerPolygon[1].y); - LineSegment bottomLine(outerPolygon[3].x, outerPolygon[3].y, outerPolygon[2].x, outerPolygon[2].y); + float x = ((float) img.cols) / 2; + Point midpoint = Point(x, bottomLine.getPointAt(x)); + Point acrossFromMidpoint = topLine.closestPointOnSegmentTo(midpoint); + float lineHeight = distanceBetweenPoints(midpoint, acrossFromMidpoint); - float x = ((float) img.cols) / 2; - Point midpoint = Point(x, bottomLine.getPointAt(x)); - Point acrossFromMidpoint = topLine.closestPointOnSegmentTo(midpoint); - float lineHeight = distanceBetweenPoints(midpoint, acrossFromMidpoint); + // Create a white mask for the area inside the polygon + Mat outerMask = Mat::zeros(img.size(), CV_8U); + Mat innerArea = Mat::zeros(img.size(), CV_8U); + fillConvexPoly(outerMask, outerPolygon.data(), outerPolygon.size(), Scalar(255,255,255)); - // Create a white mask for the area inside the polygon - Mat outerMask = Mat::zeros(img.size(), CV_8U); - Mat innerArea = Mat::zeros(img.size(), CV_8U); - fillConvexPoly(outerMask, outerPolygon.data(), outerPolygon.size(), Scalar(255,255,255)); + for (int i = 0; i < contours.size(); i++) + { + if (goodIndices[i] == false) + continue; + // get rid of the outline by drawing a 1 pixel width black line + drawContours(innerArea, contours, + i, // draw this contour + cv::Scalar(255,255,255), // in + CV_FILLED, + 8, + hierarchy, + 0 + ); - for (int i = 0; i < contours.size(); i++) + bitwise_and(innerArea, outerMask, innerArea); + + vector > tempContours; + findContours(innerArea, tempContours, + CV_RETR_EXTERNAL, // retrieve the external contours + CV_CHAIN_APPROX_SIMPLE ); // all pixels of each contours ); + + double totalArea = contourArea(contours[i]); + double areaBetweenLines = 0; + + for (int tempContourIdx = 0; tempContourIdx < tempContours.size(); tempContourIdx++) { - if (goodIndices[i] == false) - continue; + areaBetweenLines += contourArea(tempContours[tempContourIdx]); - // get rid of the outline by drawing a 1 pixel width black line - drawContours(innerArea, contours, - i, // draw this contour - cv::Scalar(255,255,255), // in - CV_FILLED, - 8, - hierarchy, - 0 - ); - - - bitwise_and(innerArea, outerMask, innerArea); - - - vector > tempContours; - findContours(innerArea, tempContours, - CV_RETR_EXTERNAL, // retrieve the external contours - CV_CHAIN_APPROX_SIMPLE ); // all pixels of each contours ); - - double totalArea = contourArea(contours[i]); - double areaBetweenLines = 0; - - for (int tempContourIdx = 0; tempContourIdx < tempContours.size(); tempContourIdx++) - { - areaBetweenLines += contourArea(tempContours[tempContourIdx]); - - } - - - if (areaBetweenLines / totalArea >= MIN_AREA_PERCENT_WITHIN_LINES) - { - includedIndices[i] = true; - } - - innerArea.setTo(Scalar(0,0,0)); } - return includedIndices; + if (areaBetweenLines / totalArea >= MIN_AREA_PERCENT_WITHIN_LINES) + { + includedIndices[i] = true; + } + + innerArea.setTo(Scalar(0,0,0)); + } + + return includedIndices; } std::vector< bool > CharacterAnalysis::filterByOuterMask(vector< vector< Point > > contours, vector< Vec4i > hierarchy, std::vector< bool > goodIndices) @@ -909,7 +838,6 @@ std::vector< bool > CharacterAnalysis::filterByOuterMask(vector< vector< Point > if (hasPlateMask == false) return goodIndices; - vector passingIndices; for (int i = 0; i < goodIndices.size(); i++) passingIndices.push_back(false); @@ -935,8 +863,8 @@ std::vector< bool > CharacterAnalysis::filterByOuterMask(vector< vector< Point > if (afterMaskWhiteness / beforeMaskWhiteness > MINIMUM_PERCENT_LEFT_AFTER_MASK) { - charsInsideMask++; - passingIndices[i] = true; + charsInsideMask++; + passingIndices[i] = true; } } @@ -952,8 +880,6 @@ std::vector< bool > CharacterAnalysis::filterByOuterMask(vector< vector< Point > return passingIndices; } - - bool CharacterAnalysis::isPlateInverted() { @@ -964,42 +890,39 @@ bool CharacterAnalysis::isPlateInverted() if (this->config->debugCharAnalysis) cout << "CharacterAnalysis, plate inverted: MEAN: " << meanVal << " : " << bestThreshold.type() << endl; - if (meanVal[0] < 100) // Half would be 122.5. Give it a little extra oomf before saying it needs inversion. Most states aren't inverted. return true; return false; } - bool CharacterAnalysis::verifySize(Mat r, float minHeightPx, float maxHeightPx) { - //Char sizes 45x90 - float aspect=config->charWidthMM / config->charHeightMM; - float charAspect= (float)r.cols/(float)r.rows; - float error=0.35; - //float minHeight=TEMPLATE_PLATE_HEIGHT * .35; - //float maxHeight=TEMPLATE_PLATE_HEIGHT * .65; - //We have a different aspect ratio for number 1, and it can be ~0.2 - float minAspect=0.2; - float maxAspect=aspect+aspect*error; - //area of pixels - float area=countNonZero(r); - //bb area - float bbArea=r.cols*r.rows; - //% of pixel in area - float percPixels=area/bbArea; + //Char sizes 45x90 + float aspect=config->charWidthMM / config->charHeightMM; + float charAspect= (float)r.cols/(float)r.rows; + float error=0.35; + //float minHeight=TEMPLATE_PLATE_HEIGHT * .35; + //float maxHeight=TEMPLATE_PLATE_HEIGHT * .65; + //We have a different aspect ratio for number 1, and it can be ~0.2 + float minAspect=0.2; + float maxAspect=aspect+aspect*error; + //area of pixels + float area=countNonZero(r); + //bb area + float bbArea=r.cols*r.rows; + //% of pixel in area + float percPixels=area/bbArea; - //if(DEBUG) - //cout << "Aspect: "<< aspect << " ["<< minAspect << "," << maxAspect << "] " << "Area "<< percPixels <<" Char aspect " << charAspect << " Height char "<< r.rows << "\n"; - if(percPixels < 0.8 && charAspect > minAspect && charAspect < maxAspect && r.rows >= minHeightPx && r.rows < maxHeightPx) - return true; - else - return false; + //if(DEBUG) + //cout << "Aspect: "<< aspect << " ["<< minAspect << "," << maxAspect << "] " << "Area "<< percPixels <<" Char aspect " << charAspect << " Height char "<< r.rows << "\n"; + if(percPixels < 0.8 && charAspect > minAspect && charAspect < maxAspect && r.rows >= minHeightPx && r.rows < maxHeightPx) + return true; + else + return false; } - vector CharacterAnalysis::getCharArea() { const int MAX = 100000; @@ -1010,16 +933,16 @@ vector CharacterAnalysis::getCharArea() for (int i = 0; i < bestContours.size(); i++) { - if (bestCharSegments[i] == false) - continue; + if (bestCharSegments[i] == false) + continue; - for (int z = 0; z < bestContours[i].size(); z++) - { - if (bestContours[i][z].x < leftX) - leftX = bestContours[i][z].x; - if (bestContours[i][z].x > rightX) - rightX = bestContours[i][z].x; - } + for (int z = 0; z < bestContours[i].size(); z++) + { + if (bestContours[i][z].x < leftX) + leftX = bestContours[i][z].x; + if (bestContours[i][z].x > rightX) + rightX = bestContours[i][z].x; + } } vector charArea; @@ -1029,10 +952,10 @@ vector CharacterAnalysis::getCharArea() Point tr(rightX, topLine.getPointAt(rightX)); Point br(rightX, bottomLine.getPointAt(rightX)); Point bl(leftX, bottomLine.getPointAt(leftX)); - charArea.push_back(tl); - charArea.push_back(tr); - charArea.push_back(br); - charArea.push_back(bl); + charArea.push_back(tl); + charArea.push_back(tr); + charArea.push_back(br); + charArea.push_back(bl); } return charArea; diff --git a/src/openalpr/characterregion.cpp b/src/openalpr/characterregion.cpp index 9660c73..5146d75 100644 --- a/src/openalpr/characterregion.cpp +++ b/src/openalpr/characterregion.cpp @@ -28,19 +28,15 @@ CharacterRegion::CharacterRegion(Mat img, Config* config) this->confidence = 0; - - if (this->debug) cout << "Starting CharacterRegion identification" << endl; timespec startTime; getTime(&startTime); - charAnalysis = new CharacterAnalysis(img, config); charAnalysis->analyze(); - if (this->debug) { vector tempDash; @@ -53,7 +49,6 @@ CharacterRegion::CharacterRegion(Mat img, Config* config) tempDash.push_back(tmp); } - Mat bestVal(charAnalysis->bestThreshold.size(), charAnalysis->bestThreshold.type()); charAnalysis->bestThreshold.copyTo(bestVal); cvtColor(bestVal, bestVal, CV_GRAY2BGR); @@ -62,18 +57,16 @@ CharacterRegion::CharacterRegion(Mat img, Config* config) { Scalar dcolor(255,0,0); if (charAnalysis->bestCharSegments[z]) - dcolor = Scalar(0,255,0); + dcolor = Scalar(0,255,0); drawContours(bestVal, charAnalysis->bestContours, z, dcolor, 1); } tempDash.push_back(bestVal); displayImage(config, "Character Region Step 1 Thresholds", drawImageDashboard(tempDash, bestVal.type(), 3)); } - - if (this->debug) { - /* + /* Mat img_contours(img_threshold.size(), CV_8U); img_threshold.copyTo(img_contours); cvtColor(img_contours, img_contours, CV_GRAY2RGB); @@ -81,29 +74,26 @@ CharacterRegion::CharacterRegion(Mat img, Config* config) vector > allowedContours; for (int i = 0; i < contours.size(); i++) { - if (charSegments[i]) - allowedContours.push_back(contours[i]); + if (charSegments[i]) + allowedContours.push_back(contours[i]); } drawContours(img_contours, contours, - -1, // draw all contours - cv::Scalar(255,0,0), // in blue - 1); // with a thickness of 1 + -1, // draw all contours + cv::Scalar(255,0,0), // in blue + 1); // with a thickness of 1 drawContours(img_contours, allowedContours, - -1, // draw all contours - cv::Scalar(0,255,0), // in green - 1); // with a thickness of 1 - + -1, // draw all contours + cv::Scalar(0,255,0), // in green + 1); // with a thickness of 1 displayImage(config, "Matching Contours", img_contours); */ } - //charsegments = this->getPossibleCharRegions(img_threshold, allContours, allHierarchy, STARTING_MIN_HEIGHT + (bestFitIndex * HEIGHT_STEP), STARTING_MAX_HEIGHT + (bestFitIndex * HEIGHT_STEP)); - if (charAnalysis->linePolygon.size() > 0) { @@ -120,7 +110,6 @@ CharacterRegion::CharacterRegion(Mat img, Config* config) else if (absangle > 1) confidenceDrainers += (10 - absangle) * 5; - if (confidenceDrainers >= 100) this->confidence=1; else @@ -128,8 +117,6 @@ CharacterRegion::CharacterRegion(Mat img, Config* config) } - - if (config->debugTiming) { timespec endTime; @@ -144,49 +131,47 @@ CharacterRegion::~CharacterRegion() delete(charAnalysis); } - - Mat CharacterRegion::getPlateMask() { - return charAnalysis->plateMask; + return charAnalysis->plateMask; } LineSegment CharacterRegion::getTopLine() { - return charAnalysis->topLine; + return charAnalysis->topLine; } LineSegment CharacterRegion::getBottomLine() { - return charAnalysis->bottomLine; + return charAnalysis->bottomLine; } vector CharacterRegion::getCharArea() { - return charAnalysis->charArea; + return charAnalysis->charArea; } LineSegment CharacterRegion::getCharBoxTop() { - return charAnalysis->charBoxTop; + return charAnalysis->charBoxTop; } LineSegment CharacterRegion::getCharBoxBottom() { - return charAnalysis->charBoxBottom; + return charAnalysis->charBoxBottom; } LineSegment CharacterRegion::getCharBoxLeft() { - return charAnalysis->charBoxLeft; + return charAnalysis->charBoxLeft; } LineSegment CharacterRegion::getCharBoxRight() { - return charAnalysis->charBoxRight; + return charAnalysis->charBoxRight; } bool CharacterRegion::thresholdsInverted() { - return charAnalysis->thresholdsInverted; + return charAnalysis->thresholdsInverted; } diff --git a/src/openalpr/charactersegmenter.cpp b/src/openalpr/charactersegmenter.cpp index 81777eb..3eef1de 100644 --- a/src/openalpr/charactersegmenter.cpp +++ b/src/openalpr/charactersegmenter.cpp @@ -34,7 +34,6 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con timespec startTime; getTime(&startTime); - Mat img_gray(img.size(), CV_8U); cvtColor( img, img_gray, CV_BGR2GRAY ); @@ -43,22 +42,14 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con if (invertedColors) bitwise_not(img_gray, img_gray); - charAnalysis = new CharacterAnalysis(img_gray, config); charAnalysis->analyze(); - - if (this->config->debugCharSegmenter) { - displayImage(config, "CharacterSegmenter Thresholds", drawImageDashboard(charAnalysis->thresholds, CV_8U, 3)); + displayImage(config, "CharacterSegmenter Thresholds", drawImageDashboard(charAnalysis->thresholds, CV_8U, 3)); } - - - - - if (this->config->debugCharSegmenter) { @@ -69,19 +60,19 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con vector > allowedContours; for (int i = 0; i < charAnalysis->bestContours.size(); i++) { - if (charAnalysis->bestCharSegments[i]) - allowedContours.push_back(charAnalysis->bestContours[i]); + if (charAnalysis->bestCharSegments[i]) + allowedContours.push_back(charAnalysis->bestContours[i]); } drawContours(img_contours, charAnalysis->bestContours, - -1, // draw all contours - cv::Scalar(255,0,0), // in blue - 1); // with a thickness of 1 + -1, // draw all contours + cv::Scalar(255,0,0), // in blue + 1); // with a thickness of 1 drawContours(img_contours, allowedContours, - -1, // draw all contours - cv::Scalar(0,255,0), // in green - 1); // with a thickness of 1 + -1, // draw all contours + cv::Scalar(0,255,0), // in green + 1); // with a thickness of 1 if (charAnalysis->linePolygon.size() > 0) { @@ -93,8 +84,6 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con imgDbgGeneral.push_back(bordered); } - - if (charAnalysis->linePolygon.size() > 0) { this->top = LineSegment(charAnalysis->linePolygon[0].x, charAnalysis->linePolygon[0].y, charAnalysis->linePolygon[1].x, charAnalysis->linePolygon[1].y); @@ -105,14 +94,13 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con for (int i = 0; i < charAnalysis->bestContours.size(); i++) { - if (charAnalysis->bestCharSegments[i] == false) - continue; + if (charAnalysis->bestCharSegments[i] == false) + continue; + Rect mr = boundingRect(charAnalysis->bestContours[i]); - Rect mr = boundingRect(charAnalysis->bestContours[i]); - - charWidths.push_back(mr.width); - charHeights.push_back(mr.height); + charWidths.push_back(mr.width); + charHeights.push_back(mr.height); } float avgCharWidth = median(charWidths.data(), charWidths.size()); @@ -122,56 +110,49 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con // Do the histogram analysis to figure out char regions - timespec startTime; getTime(&startTime); - vector allHistograms; vector allBoxes; for (int i = 0; i < charAnalysis->allContours.size(); i++) { - Mat histogramMask = Mat::zeros(charAnalysis->thresholds[i].size(), CV_8U); fillConvexPoly(histogramMask, charAnalysis->linePolygon.data(), charAnalysis->linePolygon.size(), Scalar(255,255,255)); - VerticalHistogram vertHistogram(charAnalysis->thresholds[i], histogramMask); - if (this->config->debugCharSegmenter) { - Mat histoCopy(vertHistogram.histoImg.size(), vertHistogram.histoImg.type()); - //vertHistogram.copyTo(histoCopy); - cvtColor(vertHistogram.histoImg, histoCopy, CV_GRAY2RGB); - allHistograms.push_back(histoCopy); + Mat histoCopy(vertHistogram.histoImg.size(), vertHistogram.histoImg.type()); + //vertHistogram.copyTo(histoCopy); + cvtColor(vertHistogram.histoImg, histoCopy, CV_GRAY2RGB); + allHistograms.push_back(histoCopy); } // float score = 0; vector charBoxes = getHistogramBoxes(vertHistogram, avgCharWidth, avgCharHeight, &score); - if (this->config->debugCharSegmenter) { - for (int cboxIdx = 0; cboxIdx < charBoxes.size(); cboxIdx++) - { - rectangle(allHistograms[i], charBoxes[cboxIdx], Scalar(0, 255, 0)); - } + for (int cboxIdx = 0; cboxIdx < charBoxes.size(); cboxIdx++) + { + rectangle(allHistograms[i], charBoxes[cboxIdx], Scalar(0, 255, 0)); + } - Mat histDashboard = drawImageDashboard(allHistograms, allHistograms[0].type(), 3); - displayImage(config, "Char seg histograms", histDashboard); + Mat histDashboard = drawImageDashboard(allHistograms, allHistograms[0].type(), 3); + displayImage(config, "Char seg histograms", histDashboard); } for (int z = 0; z < charBoxes.size(); z++) - allBoxes.push_back(charBoxes[z]); + allBoxes.push_back(charBoxes[z]); //drawAndWait(&histogramMask); } - float medianCharWidth = avgCharWidth; vector widthValues; // Compute largest char width @@ -182,7 +163,6 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con medianCharWidth = median(widthValues.data(), widthValues.size()); - if (config->debugTiming) { timespec endTime; @@ -193,25 +173,23 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con //ColorFilter colorFilter(img, charAnalysis->getCharacterMask()); vector candidateBoxes = getBestCharBoxes(charAnalysis->thresholds[0], allBoxes, medianCharWidth); - if (this->config->debugCharSegmenter) { - // Setup the dashboard images to show the cleaning filters + // Setup the dashboard images to show the cleaning filters for (int i = 0; i < charAnalysis->thresholds.size(); i++) { - Mat cleanImg = Mat::zeros(charAnalysis->thresholds[i].size(), charAnalysis->thresholds[i].type()); - Mat boxMask = getCharBoxMask(charAnalysis->thresholds[i], candidateBoxes); - charAnalysis->thresholds[i].copyTo(cleanImg); - bitwise_and(cleanImg, boxMask, cleanImg); - cvtColor(cleanImg, cleanImg, CV_GRAY2BGR); + Mat cleanImg = Mat::zeros(charAnalysis->thresholds[i].size(), charAnalysis->thresholds[i].type()); + Mat boxMask = getCharBoxMask(charAnalysis->thresholds[i], candidateBoxes); + charAnalysis->thresholds[i].copyTo(cleanImg); + bitwise_and(cleanImg, boxMask, cleanImg); + cvtColor(cleanImg, cleanImg, CV_GRAY2BGR); - for (int c = 0; c < candidateBoxes.size(); c++) - rectangle(cleanImg, candidateBoxes[c], Scalar(0, 255, 0), 1); - imgDbgCleanStages.push_back(cleanImg); + for (int c = 0; c < candidateBoxes.size(); c++) + rectangle(cleanImg, candidateBoxes[c], Scalar(0, 255, 0), 1); + imgDbgCleanStages.push_back(cleanImg); } } - getTime(&startTime); filterEdgeBoxes(charAnalysis->thresholds, candidateBoxes, medianCharWidth, avgCharHeight); @@ -238,7 +216,6 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con if (this->config->debugCharSegmenter) { - Mat imgDash = drawImageDashboard(charAnalysis->thresholds, CV_8U, 3); displayImage(config, "Segmentation after cleaning", imgDash); @@ -250,11 +227,6 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con } } - - - - - if (config->debugTiming) { timespec endTime; @@ -268,11 +240,6 @@ CharacterSegmenter::~CharacterSegmenter() delete charAnalysis; } - - - - - // Given a histogram and the horizontal line boundaries, respond with an array of boxes where the characters are // Scores the histogram quality as well based on num chars, char volume, and even separation vector CharacterSegmenter::getHistogramBoxes(VerticalHistogram histogram, float avgCharWidth, float avgCharHeight, float* score) @@ -298,8 +265,8 @@ vector CharacterSegmenter::getHistogramBoxes(VerticalHistogram histogram, } else if (allBoxes[i].width > avgCharWidth * 2 && allBoxes[i].width < MAX_SEGMENT_WIDTH * 2 && allBoxes[i].height > MIN_HISTOGRAM_HEIGHT) { - // rectangle(histogram.histoImg, allBoxes[i], Scalar(255, 0, 0) ); - // drawAndWait(&histogram.histoImg); + // rectangle(histogram.histoImg, allBoxes[i], Scalar(255, 0, 0) ); + // drawAndWait(&histogram.histoImg); // Try to split up doubles into two good char regions, check for a break between 40% and 60% int leftEdge = allBoxes[i].x + (int) (((float) allBoxes[i].width) * 0.4f); int rightEdge = allBoxes[i].x + (int) (((float) allBoxes[i].width) * 0.6f); @@ -314,29 +281,26 @@ vector CharacterSegmenter::getHistogramBoxes(VerticalHistogram histogram, if (maxHeightChar1 > MIN_HISTOGRAM_HEIGHT && minHeight < (0.25 * ((float) maxHeightChar1))) { - // Add a box for Char1 - Point botRight = Point(minX - 1, allBoxes[i].y + allBoxes[i].height); - charBoxes.push_back(Rect(allBoxes[i].tl(), botRight) ); + // Add a box for Char1 + Point botRight = Point(minX - 1, allBoxes[i].y + allBoxes[i].height); + charBoxes.push_back(Rect(allBoxes[i].tl(), botRight) ); } if (maxHeightChar2 > MIN_HISTOGRAM_HEIGHT && minHeight < (0.25 * ((float) maxHeightChar2))) { - // Add a box for Char2 - Point topLeft = Point(minX + 1, allBoxes[i].y); - charBoxes.push_back(Rect(topLeft, allBoxes[i].br()) ); + // Add a box for Char2 + Point topLeft = Point(minX + 1, allBoxes[i].y); + charBoxes.push_back(Rect(topLeft, allBoxes[i].br()) ); } } } - - return charBoxes; } vector CharacterSegmenter::getBestCharBoxes(Mat img, vector charBoxes, float avgCharWidth) { - float MAX_SEGMENT_WIDTH = avgCharWidth * 1.55; // This histogram is based on how many char boxes (from ALL of the many thresholded images) are covering each column @@ -344,7 +308,6 @@ vector CharacterSegmenter::getBestCharBoxes(Mat img, vector charBoxe Mat histoImg = Mat::zeros(Size(img.cols, img.rows), CV_8U); - int columnCount; for (int col = 0; col < img.cols; col++) @@ -354,7 +317,7 @@ vector CharacterSegmenter::getBestCharBoxes(Mat img, vector charBoxe for (int i = 0; i < charBoxes.size(); i++) { if (col >= charBoxes[i].x && col < (charBoxes[i].x + charBoxes[i].width)) - columnCount++; + columnCount++; } // Fill the line of the histogram @@ -370,7 +333,6 @@ vector CharacterSegmenter::getBestCharBoxes(Mat img, vector charBoxe float bestRowScore = 0; vector bestBoxes; - for (int row = 0; row < histoImg.rows; row++) { vector validBoxes; @@ -387,48 +349,46 @@ vector CharacterSegmenter::getBestCharBoxes(Mat img, vector charBoxe for (int boxidx = 0; boxidx < allBoxes.size(); boxidx++) { int w = allBoxes[boxidx].width; - if (w >= config->segmentationMinBoxWidthPx && w <= MAX_SEGMENT_WIDTH) - { - float widthDiffPixels = abs(w - avgCharWidth); - float widthDiffPercent = widthDiffPixels / avgCharWidth; - rowScore += 10 * (1 - widthDiffPercent); + if (w >= config->segmentationMinBoxWidthPx && w <= MAX_SEGMENT_WIDTH) + { + float widthDiffPixels = abs(w - avgCharWidth); + float widthDiffPercent = widthDiffPixels / avgCharWidth; + rowScore += 10 * (1 - widthDiffPercent); - if (widthDiffPercent < 0.25) // Bonus points when it's close to the average character width - rowScore += 8; + if (widthDiffPercent < 0.25) // Bonus points when it's close to the average character width + rowScore += 8; - validBoxes.push_back(allBoxes[boxidx]); - } - else if (w > avgCharWidth * 2 && w <= MAX_SEGMENT_WIDTH * 2 ) - { - // Try to split up doubles into two good char regions, check for a break between 40% and 60% - int leftEdge = allBoxes[boxidx].x + (int) (((float) allBoxes[boxidx].width) * 0.4f); - int rightEdge = allBoxes[boxidx].x + (int) (((float) allBoxes[boxidx].width) * 0.6f); + validBoxes.push_back(allBoxes[boxidx]); + } + else if (w > avgCharWidth * 2 && w <= MAX_SEGMENT_WIDTH * 2 ) + { + // Try to split up doubles into two good char regions, check for a break between 40% and 60% + int leftEdge = allBoxes[boxidx].x + (int) (((float) allBoxes[boxidx].width) * 0.4f); + int rightEdge = allBoxes[boxidx].x + (int) (((float) allBoxes[boxidx].width) * 0.6f); - int minX = histogram.getLocalMinimum(leftEdge, rightEdge); - int maxXChar1 = histogram.getLocalMaximum(allBoxes[boxidx].x, minX); - int maxXChar2 = histogram.getLocalMaximum(minX, allBoxes[boxidx].x + allBoxes[boxidx].width); - int minHeight = histogram.getHeightAt(minX); + int minX = histogram.getLocalMinimum(leftEdge, rightEdge); + int maxXChar1 = histogram.getLocalMaximum(allBoxes[boxidx].x, minX); + int maxXChar2 = histogram.getLocalMaximum(minX, allBoxes[boxidx].x + allBoxes[boxidx].width); + int minHeight = histogram.getHeightAt(minX); - int maxHeightChar1 = histogram.getHeightAt(maxXChar1); - int maxHeightChar2 = histogram.getHeightAt(maxXChar2); + int maxHeightChar1 = histogram.getHeightAt(maxXChar1); + int maxHeightChar2 = histogram.getHeightAt(maxXChar2); - if ( minHeight < (0.25 * ((float) maxHeightChar1))) - { - // Add a box for Char1 - Point botRight = Point(minX - 1, allBoxes[boxidx].y + allBoxes[boxidx].height); - validBoxes.push_back(Rect(allBoxes[boxidx].tl(), botRight) ); - } - if ( minHeight < (0.25 * ((float) maxHeightChar2))) - { - // Add a box for Char2 - Point topLeft = Point(minX + 1, allBoxes[boxidx].y); - validBoxes.push_back(Rect(topLeft, allBoxes[boxidx].br()) ); - } - } + if ( minHeight < (0.25 * ((float) maxHeightChar1))) + { + // Add a box for Char1 + Point botRight = Point(minX - 1, allBoxes[boxidx].y + allBoxes[boxidx].height); + validBoxes.push_back(Rect(allBoxes[boxidx].tl(), botRight) ); + } + if ( minHeight < (0.25 * ((float) maxHeightChar2))) + { + // Add a box for Char2 + Point topLeft = Point(minX + 1, allBoxes[boxidx].y); + validBoxes.push_back(Rect(topLeft, allBoxes[boxidx].br()) ); + } + } } - - if (rowScore > bestRowScore) { bestRowScore = rowScore; @@ -437,8 +397,6 @@ vector CharacterSegmenter::getBestCharBoxes(Mat img, vector charBoxe } } - - if (this->config->debugCharSegmenter) { cvtColor(histoImg, histoImg, CV_GRAY2BGR); @@ -455,7 +413,6 @@ vector CharacterSegmenter::getBestCharBoxes(Mat img, vector charBoxe } - return bestBoxes; } @@ -476,7 +433,7 @@ vector CharacterSegmenter::get1DHits(Mat img, int yOffset) } if ((isOn == false && onSegment == true) || - (col == img.cols - 1 && onSegment == true)) + (col == img.cols - 1 && onSegment == true)) { // A segment just ended or we're at the very end of the row and we're on a segment Point topLeft = Point(col - curSegmentLength, top.getPointAt(col - curSegmentLength) - 1); @@ -489,11 +446,9 @@ vector CharacterSegmenter::get1DHits(Mat img, int yOffset) } - return hits; } - void CharacterSegmenter::removeSmallContours(vector thresholds, vector > > allContours, float avgCharWidth, float avgCharHeight) { //const float MIN_CHAR_AREA = 0.02 * avgCharWidth * avgCharHeight; // To clear out the tiny specks @@ -505,14 +460,14 @@ void CharacterSegmenter::removeSmallContours(vector thresholds, vector CharacterSegmenter::combineCloseBoxes( vector charBoxes, floa { if (i == charBoxes.size() - 1) { - newCharBoxes.push_back(charBoxes[i]); - break; + newCharBoxes.push_back(charBoxes[i]); + break; } float bigWidth = (charBoxes[i + 1].x + charBoxes[i + 1].width - charBoxes[i].x); @@ -543,24 +498,22 @@ vector CharacterSegmenter::combineCloseBoxes( vector charBoxes, floa newCharBoxes.push_back(bigRect); if (this->config->debugCharSegmenter) { - for (int z = 0; z < charAnalysis->thresholds.size(); z++) - { - Point center(bigRect.x + bigRect.width / 2, bigRect.y + bigRect.height / 2); - RotatedRect rrect(center, Size2f(bigRect.width, bigRect.height + (bigRect.height / 2)), 0); - ellipse(imgDbgCleanStages[z], rrect, Scalar(0,255,0), 1); - } - cout << "Merging 2 boxes -- " << i << " and " << i + 1 << endl; + for (int z = 0; z < charAnalysis->thresholds.size(); z++) + { + Point center(bigRect.x + bigRect.width / 2, bigRect.y + bigRect.height / 2); + RotatedRect rrect(center, Size2f(bigRect.width, bigRect.height + (bigRect.height / 2)), 0); + ellipse(imgDbgCleanStages[z], rrect, Scalar(0,255,0), 1); + } + cout << "Merging 2 boxes -- " << i << " and " << i + 1 << endl; } i++; } else { - newCharBoxes.push_back(charBoxes[i]); + newCharBoxes.push_back(charBoxes[i]); } - - } return newCharBoxes; @@ -575,7 +528,6 @@ void CharacterSegmenter::cleanCharRegions(vector thresholds, vector c Mat mask = getCharBoxMask(thresholds[0], charRegions); - for (int i = 0; i < thresholds.size(); i++) { bitwise_and(thresholds[i], mask, thresholds[i]); @@ -598,82 +550,74 @@ void CharacterSegmenter::cleanCharRegions(vector thresholds, vector c const float MIN_SPECKLE_HEIGHT = ((float)charRegions[j].height) * MIN_SPECKLE_HEIGHT_PERCENT; const float MIN_CONTOUR_AREA = ((float)charRegions[j].area()) * MIN_CONTOUR_AREA_PERCENT; - int tallestContourHeight = 0; float totalArea = 0; for (int c = 0; c < contours.size(); c++) { - if (contours[c].size() == 0) - continue; - if (charRegions[j].contains(contours[c][0]) == false) - continue; + if (contours[c].size() == 0) + continue; + if (charRegions[j].contains(contours[c][0]) == false) + continue; + Rect r = boundingRect(contours[c]); + if (r.height <= MIN_SPECKLE_HEIGHT || r.width <= MIN_SPECKLE_WIDTH_PX) + { + // Erase this speckle + drawContours(thresholds[i], contours, c, Scalar(0,0,0), CV_FILLED); - Rect r = boundingRect(contours[c]); + if (this->config->debugCharSegmenter) + { + drawContours(imgDbgCleanStages[i], contours, c, COLOR_DEBUG_SPECKLES, CV_FILLED); + } + } + else + { + if (r.height > tallestContourHeight) + tallestContourHeight = r.height; - if (r.height <= MIN_SPECKLE_HEIGHT || r.width <= MIN_SPECKLE_WIDTH_PX) - { - // Erase this speckle - drawContours(thresholds[i], contours, c, Scalar(0,0,0), CV_FILLED); + totalArea += contourArea(contours[c]); - if (this->config->debugCharSegmenter) - { - drawContours(imgDbgCleanStages[i], contours, c, COLOR_DEBUG_SPECKLES, CV_FILLED); - } - } - else - { - if (r.height > tallestContourHeight) - tallestContourHeight = r.height; - - totalArea += contourArea(contours[c]); - - - } - //else if (r.height > tallestContourHeight) - //{ - // tallestContourIndex = c; - // tallestContourHeight = h; - //} + } + //else if (r.height > tallestContourHeight) + //{ + // tallestContourIndex = c; + // tallestContourHeight = h; + //} } - - if (totalArea < MIN_CONTOUR_AREA) { - // Character is not voluminous enough. Erase it. - if (this->config->debugCharSegmenter) - { - cout << "Character CLEAN: (area) removing box " << j << " in threshold " << i << " -- Area " << totalArea << " < " << MIN_CONTOUR_AREA << endl; + // Character is not voluminous enough. Erase it. + if (this->config->debugCharSegmenter) + { + cout << "Character CLEAN: (area) removing box " << j << " in threshold " << i << " -- Area " << totalArea << " < " << MIN_CONTOUR_AREA << endl; - Rect boxTop(charRegions[j].x, charRegions[j].y - 10, charRegions[j].width, 10); - rectangle(imgDbgCleanStages[i], boxTop, COLOR_DEBUG_MIN_AREA, -1); - } + Rect boxTop(charRegions[j].x, charRegions[j].y - 10, charRegions[j].width, 10); + rectangle(imgDbgCleanStages[i], boxTop, COLOR_DEBUG_MIN_AREA, -1); + } - - rectangle(thresholds[i], charRegions[j], Scalar(0, 0, 0), -1); + rectangle(thresholds[i], charRegions[j], Scalar(0, 0, 0), -1); } else if (tallestContourHeight < ((float) charRegions[j].height * MIN_CONTOUR_HEIGHT_PERCENT)) { - // This character is too short. Black the whole thing out - if (this->config->debugCharSegmenter) - { - cout << "Character CLEAN: (height) removing box " << j << " in threshold " << i << " -- Height " << tallestContourHeight << " < " << ((float) charRegions[j].height * MIN_CONTOUR_HEIGHT_PERCENT) << endl; + // This character is too short. Black the whole thing out + if (this->config->debugCharSegmenter) + { + cout << "Character CLEAN: (height) removing box " << j << " in threshold " << i << " -- Height " << tallestContourHeight << " < " << ((float) charRegions[j].height * MIN_CONTOUR_HEIGHT_PERCENT) << endl; - Rect boxBottom(charRegions[j].x, charRegions[j].y + charRegions[j].height, charRegions[j].width, 10); - rectangle(imgDbgCleanStages[i], boxBottom, COLOR_DEBUG_MIN_HEIGHT, -1); - } - rectangle(thresholds[i], charRegions[j], Scalar(0, 0, 0), -1); + Rect boxBottom(charRegions[j].x, charRegions[j].y + charRegions[j].height, charRegions[j].width, 10); + rectangle(imgDbgCleanStages[i], boxBottom, COLOR_DEBUG_MIN_HEIGHT, -1); + } + rectangle(thresholds[i], charRegions[j], Scalar(0, 0, 0), -1); } } - Mat closureElement = getStructuringElement( 1, - Size( 2 + 1, 2+1 ), - Point( 1, 1 ) ); + Size( 2 + 1, 2+1 ), + Point( 1, 1 ) ); //morphologyEx(thresholds[i], thresholds[i], MORPH_OPEN, element); @@ -685,14 +629,13 @@ void CharacterSegmenter::cleanCharRegions(vector thresholds, vector c // Lastly, draw a clipping line between each character boxes for (int j = 0; j < charRegions.size(); j++) { - line(thresholds[i], Point(charRegions[j].x - 1, charRegions[j].y), Point(charRegions[j].x - 1, charRegions[j].y + charRegions[j].height), Scalar(0, 0, 0)); - line(thresholds[i], Point(charRegions[j].x + charRegions[j].width + 1, charRegions[j].y), Point(charRegions[j].x + charRegions[j].width + 1, charRegions[j].y + charRegions[j].height), Scalar(0, 0, 0)); + line(thresholds[i], Point(charRegions[j].x - 1, charRegions[j].y), Point(charRegions[j].x - 1, charRegions[j].y + charRegions[j].height), Scalar(0, 0, 0)); + line(thresholds[i], Point(charRegions[j].x + charRegions[j].width + 1, charRegions[j].y), Point(charRegions[j].x + charRegions[j].width + 1, charRegions[j].y + charRegions[j].height), Scalar(0, 0, 0)); } } } - void CharacterSegmenter::cleanBasedOnColor(vector thresholds, Mat colorMask, vector charRegions) { // If I knock out x% of the contour area from this thing (after applying the color filter) @@ -702,79 +645,75 @@ void CharacterSegmenter::cleanBasedOnColor(vector thresholds, Mat colorMask for (int i = 0; i < thresholds.size(); i++) { - for (int j = 0; j < charRegions.size(); j++) + for (int j = 0; j < charRegions.size(); j++) + { + + Mat boxChar = Mat::zeros(thresholds[i].size(), CV_8U); + rectangle(boxChar, charRegions[j], Scalar(255,255,255), CV_FILLED); + + bitwise_and(thresholds[i], boxChar, boxChar); + + float meanBefore = mean(boxChar, boxChar)[0]; + + Mat thresholdCopy(thresholds[i].size(), CV_8U); + bitwise_and(colorMask, boxChar, thresholdCopy); + + float meanAfter = mean(thresholdCopy, boxChar)[0]; + + if (meanAfter < meanBefore * (1-MIN_PERCENT_CHUNK_REMOVED)) { + rectangle(thresholds[i], charRegions[j], Scalar(0,0,0), CV_FILLED); - Mat boxChar = Mat::zeros(thresholds[i].size(), CV_8U); - rectangle(boxChar, charRegions[j], Scalar(255,255,255), CV_FILLED); - - bitwise_and(thresholds[i], boxChar, boxChar); - - - - float meanBefore = mean(boxChar, boxChar)[0]; - - Mat thresholdCopy(thresholds[i].size(), CV_8U); - bitwise_and(colorMask, boxChar, thresholdCopy); - - float meanAfter = mean(thresholdCopy, boxChar)[0]; - - if (meanAfter < meanBefore * (1-MIN_PERCENT_CHUNK_REMOVED)) - { - rectangle(thresholds[i], charRegions[j], Scalar(0,0,0), CV_FILLED); - - if (this->config->debugCharSegmenter) - { - //vector tmpDebug; - //Mat thresholdCopy2 = Mat::zeros(thresholds[i].size(), CV_8U); - //rectangle(thresholdCopy2, charRegions[j], Scalar(255,255,255), CV_FILLED); - //tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask")); - //bitwise_and(thresholds[i], thresholdCopy2, thresholdCopy2); - //tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask + Thresh")); - //bitwise_and(colorMask, thresholdCopy2, thresholdCopy2); - //tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask + Thresh + Color")); + if (this->config->debugCharSegmenter) + { + //vector tmpDebug; + //Mat thresholdCopy2 = Mat::zeros(thresholds[i].size(), CV_8U); + //rectangle(thresholdCopy2, charRegions[j], Scalar(255,255,255), CV_FILLED); + //tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask")); + //bitwise_and(thresholds[i], thresholdCopy2, thresholdCopy2); + //tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask + Thresh")); + //bitwise_and(colorMask, thresholdCopy2, thresholdCopy2); + //tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask + Thresh + Color")); // // Mat tmpytmp = addLabel(thresholdCopy2, "box Mask + Thresh + Color"); // Mat tmpx = drawImageDashboard(tmpDebug, tmpytmp.type(), 1); - //drawAndWait( &tmpx ); + //drawAndWait( &tmpx ); + cout << "Segmentation Filter Clean by Color: Removed Threshold " << i << " charregion " << j << endl; + cout << "Segmentation Filter Clean by Color: before=" << meanBefore << " after=" << meanAfter << endl; - cout << "Segmentation Filter Clean by Color: Removed Threshold " << i << " charregion " << j << endl; - cout << "Segmentation Filter Clean by Color: before=" << meanBefore << " after=" << meanAfter << endl; - - Point topLeft(charRegions[j].x, charRegions[j].y); - circle(imgDbgCleanStages[i], topLeft, 5, COLOR_DEBUG_COLORFILTER, CV_FILLED); - } - } + Point topLeft(charRegions[j].x, charRegions[j].y); + circle(imgDbgCleanStages[i], topLeft, 5, COLOR_DEBUG_COLORFILTER, CV_FILLED); + } } + } } } - void CharacterSegmenter::cleanMostlyFullBoxes(vector thresholds, const vector charRegions) { float MAX_FILLED = 0.95 * 255; for (int i = 0; i < charRegions.size(); i++) { - Mat mask = Mat::zeros(thresholds[0].size(), CV_8U); - rectangle(mask, charRegions[i], Scalar(255,255,255), -1); + Mat mask = Mat::zeros(thresholds[0].size(), CV_8U); + rectangle(mask, charRegions[i], Scalar(255,255,255), -1); - for (int j = 0; j < thresholds.size(); j++) + for (int j = 0; j < thresholds.size(); j++) + { + if (mean(thresholds[j], mask)[0] > MAX_FILLED) { - if (mean(thresholds[j], mask)[0] > MAX_FILLED) - { - // Empty the rect - rectangle(thresholds[j], charRegions[i], Scalar(0,0,0), -1); + // Empty the rect + rectangle(thresholds[j], charRegions[i], Scalar(0,0,0), -1); - if (this->config->debugCharSegmenter) - { - Rect inset(charRegions[i].x + 2, charRegions[i].y - 2, charRegions[i].width - 4, charRegions[i].height - 4); - rectangle(imgDbgCleanStages[j], inset, COLOR_DEBUG_FULLBOX, 1); - } - } + if (this->config->debugCharSegmenter) + { + Rect inset(charRegions[i].x + 2, charRegions[i].y - 2, charRegions[i].width - 4, charRegions[i].height - 4); + rectangle(imgDbgCleanStages[j], inset, COLOR_DEBUG_FULLBOX, 1); + } } + } } } vector CharacterSegmenter::filterMostlyEmptyBoxes(vector thresholds, const vector charRegions) @@ -811,31 +750,29 @@ vector CharacterSegmenter::filterMostlyEmptyBoxes(vector thresholds, vector allPointsInBox; for (int c = 0; c < contours.size(); c++) { - if (contours[c].size() == 0) - continue; + if (contours[c].size() == 0) + continue; - for (int z = 0; z < contours[c].size(); z++) - allPointsInBox.push_back(contours[c][z]); + for (int z = 0; z < contours[c].size(); z++) + allPointsInBox.push_back(contours[c][z]); } float height = 0; if (allPointsInBox.size() > 0) { - height = boundingRect(allPointsInBox).height; + height = boundingRect(allPointsInBox).height; } - if (height >= ((float) charRegions[j].height * MIN_CONTOUR_HEIGHT_PERCENT)) { - boxScores[j] = boxScores[j] + 1; + boxScores[j] = boxScores[j] + 1; } else if (this->config->debugCharSegmenter) { - drawX(imgDbgCleanStages[i], charRegions[j], COLOR_DEBUG_EMPTYFILTER, 3); + drawX(imgDbgCleanStages[i], charRegions[j], COLOR_DEBUG_EMPTYFILTER, 3); } - } } @@ -862,16 +799,16 @@ vector CharacterSegmenter::filterMostlyEmptyBoxes(vector thresholds, // Erase the box from the Mat... mainly for debug purposes if (this->config->debugCharSegmenter) { - cout << "Mostly Empty Filter: box index: " << i; - cout << " this box had a score of : " << boxScores[i];; - cout << " MIN_FULL_BOXES: " << MIN_FULL_BOXES << endl;; + cout << "Mostly Empty Filter: box index: " << i; + cout << " this box had a score of : " << boxScores[i];; + cout << " MIN_FULL_BOXES: " << MIN_FULL_BOXES << endl;; - for (int z = 0; z < thresholds.size(); z++) - { - rectangle(thresholds[z], charRegions[i], Scalar(0,0,0), -1); + for (int z = 0; z < thresholds.size(); z++) + { + rectangle(thresholds[z], charRegions[i], Scalar(0,0,0), -1); - drawX(imgDbgCleanStages[z], charRegions[i], COLOR_DEBUG_EMPTYFILTER, 1); - } + drawX(imgDbgCleanStages[z], charRegions[i], COLOR_DEBUG_EMPTYFILTER, 1); + } } } @@ -902,7 +839,6 @@ void CharacterSegmenter::filterEdgeBoxes(vector thresholds, const vector 0 && charBoxes[0].width < MIN_SEGMENT_WIDTH_EDGES) // charBoxes.erase(charBoxes.begin() + 0); - // TECHNIQUE #1 // Check for long vertical lines. Once the line is too long, mask the whole region @@ -934,7 +870,7 @@ void CharacterSegmenter::filterEdgeBoxes(vector thresholds, const vector thresholds, const vector MIN_CONNECTED_EDGE_PIXELS) { - leftEdgeX = col; - break; + leftEdgeX = col; + break; } col--; @@ -963,13 +899,12 @@ void CharacterSegmenter::filterEdgeBoxes(vector thresholds, const vector MIN_CONNECTED_EDGE_PIXELS) { - rightEdgeX = col; - break; + rightEdgeX = col; + break; } col++; } - if (leftEdgeX != 0) leftEdges.push_back(leftEdgeX); if (rightEdgeX != thresholds[i].cols) @@ -1007,7 +942,6 @@ void CharacterSegmenter::filterEdgeBoxes(vector thresholds, const vector thresholds, const vector MAX_COVERAGE_PERCENT) || - (charRegions[0].width - leftCoveragePx < config->segmentationMinBoxWidthPx)) + (charRegions[0].width - leftCoveragePx < config->segmentationMinBoxWidthPx)) { rectangle(mask, charRegions[0], Scalar(0,0,0), -1); // Mask the whole region if (this->config->debugCharSegmenter) - cout << "Edge Filter: Entire left region is erased" << endl; + cout << "Edge Filter: Entire left region is erased" << endl; } if ((rightCoveragePercent > MAX_COVERAGE_PERCENT) || - (charRegions[charRegions.size() -1].width - rightCoveragePx < config->segmentationMinBoxWidthPx)) + (charRegions[charRegions.size() -1].width - rightCoveragePx < config->segmentationMinBoxWidthPx)) { rectangle(mask, charRegions[charRegions.size() -1], Scalar(0,0,0), -1); if (this->config->debugCharSegmenter) - cout << "Edge Filter: Entire right region is erased" << endl; + cout << "Edge Filter: Entire right region is erased" << endl; } for (int i = 0; i < thresholds.size(); i++) @@ -1034,7 +968,6 @@ void CharacterSegmenter::filterEdgeBoxes(vector thresholds, const vectorconfig->debugCharSegmenter) { cout << "Edge Filter: left=" << leftEdge << " right=" << rightEdge << endl; @@ -1044,7 +977,7 @@ void CharacterSegmenter::filterEdgeBoxes(vector thresholds, const vector thresholds, const vector > contours; - Mat mask = Mat::zeros(thresholds[i].size(),CV_8U); - rectangle(mask, charRegions[boxidx], Scalar(255,255,255), CV_FILLED); + vector > contours; + Mat mask = Mat::zeros(thresholds[i].size(),CV_8U); + rectangle(mask, charRegions[boxidx], Scalar(255,255,255), CV_FILLED); - bitwise_and(thresholds[i], mask, mask); - findContours(mask, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); - //int tallContourIndex = isSkinnyLineInsideBox(thresholds[i], charRegions[boxidx], allContours[i], hierarchy[i], avgCharWidth, avgCharHeight); - float tallestContourHeight = 0; - float fattestContourWidth = 0; - float biggestContourArea = 0; - for (int c = 0; c < contours.size(); c++) - { - Rect r = boundingRect(contours[c]); - if (r.height > tallestContourHeight) - tallestContourHeight = r.height; - if (r.width > fattestContourWidth) - fattestContourWidth = r.width; - float a = r.area(); - if (a > biggestContourArea) - biggestContourArea = a; - } + bitwise_and(thresholds[i], mask, mask); + findContours(mask, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); + //int tallContourIndex = isSkinnyLineInsideBox(thresholds[i], charRegions[boxidx], allContours[i], hierarchy[i], avgCharWidth, avgCharHeight); + float tallestContourHeight = 0; + float fattestContourWidth = 0; + float biggestContourArea = 0; + for (int c = 0; c < contours.size(); c++) + { + Rect r = boundingRect(contours[c]); + if (r.height > tallestContourHeight) + tallestContourHeight = r.height; + if (r.width > fattestContourWidth) + fattestContourWidth = r.width; + float a = r.area(); + if (a > biggestContourArea) + biggestContourArea = a; + } - float minArea = charRegions[boxidx].area() * MIN_EDGE_CONTOUR_AREA_PCT; - if ((fattestContourWidth < MIN_BOX_WIDTH_PX) || - (tallestContourHeight < MIN_EDGE_CONTOUR_HEIGHT) || - (biggestContourArea < minArea) - ) - { - // Find a good place to MASK this contour. - // for now, just mask the whole thing - if (this->debug) - { + float minArea = charRegions[boxidx].area() * MIN_EDGE_CONTOUR_AREA_PCT; + if ((fattestContourWidth < MIN_BOX_WIDTH_PX) || + (tallestContourHeight < MIN_EDGE_CONTOUR_HEIGHT) || + (biggestContourArea < minArea) + ) + { + // Find a good place to MASK this contour. + // for now, just mask the whole thing + if (this->debug) + { - rectangle(imgDbgCleanStages[i], charRegions[boxidx], COLOR_DEBUG_EDGE, 2); - cout << "Edge Filter: threshold " << i << " box " << boxidx << endl; - } - rectangle(thresholds[i], charRegions[boxidx], Scalar(0,0,0), -1); - } - else - { - filteredCharRegions.push_back(charRegions[boxidx]); - } + rectangle(imgDbgCleanStages[i], charRegions[boxidx], COLOR_DEBUG_EDGE, 2); + cout << "Edge Filter: threshold " << i << " box " << boxidx << endl; + } + rectangle(thresholds[i], charRegions[boxidx], Scalar(0,0,0), -1); + } + else + { + filteredCharRegions.push_back(charRegions[boxidx]); + } } } */ @@ -1140,7 +1073,7 @@ int CharacterSegmenter::getLongestBlobLengthBetweenLines(Mat img, int col) // Add a little extra to the score if this is outside of the lines if (!isbetweenLines) - incrementBy = 1.1; + incrementBy = 1.1; onSegment = true; curSegmentLength += incrementBy; @@ -1151,10 +1084,10 @@ int CharacterSegmenter::getLongestBlobLengthBetweenLines(Mat img, int col) } if ((isOn == false && onSegment == true) || - (row == img.rows - 1 && onSegment == true)) + (row == img.rows - 1 && onSegment == true)) { if (wasbetweenLines && curSegmentLength > longestBlobLength) - longestBlobLength = curSegmentLength; + longestBlobLength = curSegmentLength; onSegment = false; isbetweenLines = false; @@ -1163,7 +1096,6 @@ int CharacterSegmenter::getLongestBlobLengthBetweenLines(Mat img, int col) } - return longestBlobLength; } @@ -1183,7 +1115,6 @@ int CharacterSegmenter::isSkinnyLineInsideBox(Mat threshold, Rect box, vector tallestContourHeight) { - tallestContourIdx = s; - tallestContourHeight = r.height; - tallestContourWidth = r.width; - tallestContourArea = contourArea(subContours[s]); + tallestContourIdx = s; + tallestContourHeight = r.height; + tallestContourWidth = r.width; + tallestContourArea = contourArea(subContours[s]); } } if (tallestContourIdx != -1) { - //cout << "Edge Filter: " << tallestContourHeight << " -- " << avgCharHeight << endl; if (tallestContourHeight >= avgCharHeight * 0.9 && - ((tallestContourWidth < config->segmentationMinBoxWidthPx) || (tallestContourArea < avgCharWidth * avgCharHeight * 0.1))) + ((tallestContourWidth < config->segmentationMinBoxWidthPx) || (tallestContourArea < avgCharWidth * avgCharHeight * 0.1))) { - cout << "Edge Filter: Avg contour width: " << avgCharWidth << " This guy is: " << tallestContourWidth << endl; - cout << "Edge Filter: tallestContourArea: " << tallestContourArea << " Minimum: " << avgCharWidth * avgCharHeight * 0.1 << endl; - return i; + cout << "Edge Filter: Avg contour width: " << avgCharWidth << " This guy is: " << tallestContourWidth << endl; + cout << "Edge Filter: tallestContourArea: " << tallestContourArea << " Minimum: " << avgCharWidth * avgCharHeight * 0.1 << endl; + return i; } } } @@ -1230,14 +1160,12 @@ int CharacterSegmenter::isSkinnyLineInsideBox(Mat threshold, Rect box, vector charBoxes) { Mat mask = Mat::zeros(img_threshold.size(), CV_8U); for (int i = 0; i < charBoxes.size(); i++) - rectangle(mask, charBoxes[i], Scalar(255, 255, 255), -1); + rectangle(mask, charBoxes[i], Scalar(255, 255, 255), -1); return mask; } diff --git a/src/openalpr/colorfilter.cpp b/src/openalpr/colorfilter.cpp index cc20bfa..5be932e 100644 --- a/src/openalpr/colorfilter.cpp +++ b/src/openalpr/colorfilter.cpp @@ -17,11 +17,8 @@ * along with this program. If not, see . */ - #include "colorfilter.h" - - ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config) { @@ -32,7 +29,6 @@ ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config) this->debug = config->debugColorFiler; - this->grayscale = imageIsGrayscale(image); if (this->debug) @@ -48,7 +44,6 @@ ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config) findCharColors(); - if (config->debugTiming) { timespec endTime; @@ -75,12 +70,12 @@ bool ColorFilter::imageIsGrayscale(Mat image) if (r == g == b) { - // So far so good + // So far so good } else { - // Image is color. - return false; + // Image is color. + return false; } } } @@ -112,16 +107,14 @@ void ColorFilter::findCharColors() Mat erodedCharMask(charMask.size(), CV_8U); Mat element = getStructuringElement( 1, - Size( 2 + 1, 2+1 ), - Point( 1, 1 ) ); + Size( 2 + 1, 2+1 ), + Point( 1, 1 ) ); erode(charMask, erodedCharMask, element); vector > contours; vector hierarchy; findContours(erodedCharMask, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE); - - vector hMeans, sMeans, vMeans; vector hStdDevs, sStdDevs, vStdDevs; @@ -130,50 +123,46 @@ void ColorFilter::findCharColors() if (hierarchy[i][3] != -1) continue; - Mat singleCharMask = Mat::zeros(hsv.size(), CV_8U); + Mat singleCharMask = Mat::zeros(hsv.size(), CV_8U); - drawContours(singleCharMask, contours, - i, // draw this contour - cv::Scalar(255,255,255), // in - CV_FILLED, - 8, - hierarchy - ); + drawContours(singleCharMask, contours, + i, // draw this contour + cv::Scalar(255,255,255), // in + CV_FILLED, + 8, + hierarchy + ); - // get rid of the outline by drawing a 1 pixel width black line - drawContours(singleCharMask, contours, - i, // draw this contour - cv::Scalar(0,0,0), // in - 1, - 8, - hierarchy - ); + // get rid of the outline by drawing a 1 pixel width black line + drawContours(singleCharMask, contours, + i, // draw this contour + cv::Scalar(0,0,0), // in + 1, + 8, + hierarchy + ); + //drawAndWait(&singleCharMask); + Scalar mean; + Scalar stddev; + meanStdDev(hsv, mean, stddev, singleCharMask); + if (this->debug) + { + cout << "ColorFilter " << setw(3) << i << ". Mean: h: " << setw(7) << mean[0] << " s: " << setw(7) <debug) - { - cout << "ColorFilter " << setw(3) << i << ". Mean: h: " << setw(7) << mean[0] << " s: " << setw(7) <getMajorityOpinion(sMeans, .65, 35); int bestValIndex = this->getMajorityOpinion(vMeans, .65, 30); - if (sMeans[bestSatIndex] < MINIMUM_SATURATION) return; - bool doHueFilter = false, doSatFilter = false, doValFilter = false; float hueMin, hueMax; float satMin, satMax; @@ -259,8 +246,6 @@ void ColorFilter::findCharColors() cout << "ColorFilter Val: " << bestValIndex << " : " << setw(7) << vMeans[bestValIndex] << " -- " << valMin << "-" << valMax << endl; } - - Mat imgDebugHueOnly = Mat::zeros(hsv.size(), hsv.type()); Mat imgDebug = Mat::zeros(hsv.size(), hsv.type()); Mat imgDistanceFromCenter = Mat::zeros(hsv.size(), CV_8U); @@ -291,19 +276,19 @@ void ColorFilter::findCharColors() if (doHueFilter && (h < hueMin || h > hueMax)) { - hPasses = false; - imgDebug.at(row, col)[0] = 0; - debugMask.at(row, col) = 0; + hPasses = false; + imgDebug.at(row, col)[0] = 0; + debugMask.at(row, col) = 0; } if (doSatFilter && (s < satMin || s > satMax)) { - sPasses = false; - imgDebug.at(row, col)[1] = 0; + sPasses = false; + imgDebug.at(row, col)[1] = 0; } if (doValFilter && (v < valMin || v > valMax)) { - vPasses = false; - imgDebug.at(row, col)[2] = 0; + vPasses = false; + imgDebug.at(row, col)[2] = 0; } //if (pixelPasses) @@ -314,27 +299,24 @@ void ColorFilter::findCharColors() //imgDebug.at(row, col)[2] = vPasses & 255; if ((hPasses) || (hPasses && sPasses))//(hPasses && vPasses) || (sPasses && vPasses) || - this->colorMask.at(row, col) = 255; + this->colorMask.at(row, col) = 255; else - this->colorMask.at(row, col) = 0; - + this->colorMask.at(row, col) = 0; if ((hPasses && sPasses) || (hPasses && vPasses) || (sPasses && vPasses)) { - vDistance = pow(vDistance, 0.9); + vDistance = pow(vDistance, 0.9); } else { - vDistance = pow(vDistance, 1.1); + vDistance = pow(vDistance, 1.1); } if (vDistance > 255) - vDistance = 255; + vDistance = 255; imgDistanceFromCenter.at(row, col) = vDistance; } } - - vector debugImagesSet; if (this->debug) @@ -346,14 +328,13 @@ void ColorFilter::findCharColors() debugImagesSet.push_back(addLabel(maskCopy, "color Mask Before")); } - Mat bigElement = getStructuringElement( 1, - Size( 3 + 1, 3+1 ), - Point( 1, 1 ) ); + Size( 3 + 1, 3+1 ), + Point( 1, 1 ) ); Mat smallElement = getStructuringElement( 1, - Size( 1 + 1, 1+1 ), - Point( 1, 1 ) ); + Size( 1 + 1, 1+1 ), + Point( 1, 1 ) ); morphologyEx(this->colorMask, this->colorMask, MORPH_CLOSE, bigElement); //dilate(this->colorMask, this->colorMask, bigElement); @@ -378,15 +359,12 @@ void ColorFilter::findCharColors() debugImagesSet.push_back(addLabel(debugMask, "COLOR Hues off")); - Mat dashboard = drawImageDashboard(debugImagesSet, imgDebugHueOnly.type(), 3); displayImage(config, "Color Filter Images", dashboard); } } - - // Goes through an array of values, picks the winner based on the highest percentage of other values that are within the maxValDifference // Return -1 if it fails. int ColorFilter::getMajorityOpinion(vector values, float minPercentAgreement, float maxValDifference) @@ -403,7 +381,7 @@ int ColorFilter::getMajorityOpinion(vector values, float minPercentAgreem { float diff = abs(values[i] - values[j]); if (diff < maxValDifference) - valuesInRange++; + valuesInRange++; overallDiff += diff; } diff --git a/src/openalpr/config.cpp b/src/openalpr/config.cpp index 5d84e09..b46688f 100644 --- a/src/openalpr/config.cpp +++ b/src/openalpr/config.cpp @@ -19,7 +19,6 @@ #include "config.h" - Config::Config(const std::string country, const std::string runtimeBaseDir) { this->runtimeBaseDir = runtimeBaseDir; @@ -30,7 +29,7 @@ Config::Config(const std::string country, const std::string runtimeBaseDir) envRuntimeDir = getenv (ENV_VARIABLE_RUNTIME_DIR); if (runtimeBaseDir.compare("") != 0) { - // User has supplied a runtime directory. Use that. + // User has supplied a runtime directory. Use that. } else if (envRuntimeDir!=NULL) @@ -93,12 +92,10 @@ void Config::loadValues(string country) ocrImageWidthPx = round(((float) templateWidthPx) * ocrImagePercent); ocrImageHeightPx = round(((float)templateHeightPx) * ocrImagePercent); - float stateIdImagePercent = getFloat("common", "state_id_img_size_percent", 100); stateIdImageWidthPx = round(((float)templateWidthPx) * ocrImagePercent); stateIdimageHeightPx = round(((float)templateHeightPx) * ocrImagePercent); - charAnalysisMinPercent = getFloat(country, "char_analysis_min_pct", 0); charAnalysisHeightRange = getFloat(country, "char_analysis_height_range", 0); charAnalysisHeightStepSize = getFloat(country, "char_analysis_height_step_size", 0); @@ -150,7 +147,6 @@ void Config::debugOff() debugPostProcess = false; } - string Config::getCascadeRuntimeDir() { return this->runtimeBaseDir + CASCADE_DIR; @@ -168,9 +164,6 @@ string Config::getTessdataPrefix() return "TESSDATA_PREFIX=" + this->runtimeBaseDir + "/ocr/"; } - - - float Config::getFloat(string section, string key, float defaultValue) { const char * pszValue = ini->GetValue(section.c_str(), key.c_str(), NULL /*default*/); diff --git a/src/openalpr/featurematcher.cpp b/src/openalpr/featurematcher.cpp index d04e536..0540262 100644 --- a/src/openalpr/featurematcher.cpp +++ b/src/openalpr/featurematcher.cpp @@ -17,15 +17,12 @@ * along with this program. If not, see . */ - #include "featurematcher.h" - //const int DEFAULT_QUERY_FEATURES = 305; //const int DEFAULT_TRAINING_FEATURES = 305; const float MAX_DISTANCE_TO_MATCH = 100.0f; - FeatureMatcher::FeatureMatcher(Config* config) { this->config = config; @@ -35,7 +32,6 @@ FeatureMatcher::FeatureMatcher(Config* config) //this->descriptorMatcher = DescriptorMatcher::create( "FlannBased" ); - this->detector = new FastFeatureDetector(10, true); this->extractor = new BRISK(10, 1, 0.9); } @@ -52,114 +48,103 @@ FeatureMatcher::~FeatureMatcher() } - bool FeatureMatcher::isLoaded() { - if( detector.empty() || extractor.empty() || descriptorMatcher.empty() ) - { - return false; - } + if( detector.empty() || extractor.empty() || descriptorMatcher.empty() ) + { + return false; + } - return true; + return true; } int FeatureMatcher::numTrainingElements() { - return billMapping.size(); + return billMapping.size(); } - - - void FeatureMatcher::surfStyleMatching( const Mat& queryDescriptors, vector queryKeypoints, - vector& matches12 ) + vector& matches12 ) { - vector > matchesKnn; + vector > matchesKnn; - this->descriptorMatcher->radiusMatch(queryDescriptors, matchesKnn, MAX_DISTANCE_TO_MATCH); + this->descriptorMatcher->radiusMatch(queryDescriptors, matchesKnn, MAX_DISTANCE_TO_MATCH); + vector tempMatches; + _surfStyleMatching(queryDescriptors, matchesKnn, tempMatches); - vector tempMatches; - _surfStyleMatching(queryDescriptors, matchesKnn, tempMatches); - - crisscrossFiltering(queryKeypoints, tempMatches, matches12); + crisscrossFiltering(queryKeypoints, tempMatches, matches12); } - - void FeatureMatcher::_surfStyleMatching(const Mat& queryDescriptors, vector > matchesKnn, vector& matches12) { - //objectMatches.clear(); - //objectMatches.resize(objectIds.size()); - //cout << "starting matcher" << matchesKnn.size() << endl; - for (int descInd = 0; descInd < queryDescriptors.rows; descInd++) + //objectMatches.clear(); + //objectMatches.resize(objectIds.size()); + //cout << "starting matcher" << matchesKnn.size() << endl; + for (int descInd = 0; descInd < queryDescriptors.rows; descInd++) + { + const std::vector & matches = matchesKnn[descInd]; + //cout << "two: " << descInd << ":" << matches.size() << endl; + + // Check to make sure we have 2 matches. I think this is always the case, but it doesn't hurt to be sure + if (matchesKnn[descInd].size() > 1) + { + + // Next throw out matches with a crappy score + // Ignore... already handled by the radiusMatch + //if (matchesKnn[descInd][0].distance < MAX_DISTANCE_TO_MATCH) + //{ + float ratioThreshold = 0.75; + + // Check if both matches came from the same image. If they both came from the same image, score them slightly less harshly + if (matchesKnn[descInd][0].imgIdx == matchesKnn[descInd][1].imgIdx) { - const std::vector & matches = matchesKnn[descInd]; - //cout << "two: " << descInd << ":" << matches.size() << endl; - - // Check to make sure we have 2 matches. I think this is always the case, but it doesn't hurt to be sure - if (matchesKnn[descInd].size() > 1) - { - - // Next throw out matches with a crappy score - // Ignore... already handled by the radiusMatch - //if (matchesKnn[descInd][0].distance < MAX_DISTANCE_TO_MATCH) - //{ - float ratioThreshold = 0.75; - - // Check if both matches came from the same image. If they both came from the same image, score them slightly less harshly - if (matchesKnn[descInd][0].imgIdx == matchesKnn[descInd][1].imgIdx) - { - ratioThreshold = 0.85; - } - - if ((matchesKnn[descInd][0].distance / matchesKnn[descInd][1].distance) < ratioThreshold) - { - bool already_exists = false; - // Quickly run through the matches we've already added and make sure it's not a duplicate... - for (int q = 0; q < matches12.size(); q++) - { - if (matchesKnn[descInd][0].queryIdx == matches12[q].queryIdx) - { - already_exists = true; - break; - } - else if ((matchesKnn[descInd][0].trainIdx == matches12[q].trainIdx) && - (matchesKnn[descInd][0].imgIdx == matches12[q].imgIdx)) - { - already_exists = true; - break; - } - } - - // Good match. - if (already_exists == false) - matches12.push_back(matchesKnn[descInd][0]); - } - - - //} - } - else if (matchesKnn[descInd].size() == 1) - { - // Only match? Does this ever happen? - matches12.push_back(matchesKnn[descInd][0]); - } - // In the ratio test, we will compare the quality of a match with the next match that is not from the same object: - // we can accept several matches with similar scores as long as they are for the same object. Those should not be - // part of the model anyway as they are not discriminative enough - - //for (unsigned int first_index = 0; first_index < matches.size(); ++first_index) - //{ - - //matches12.push_back(match); - //} - - + ratioThreshold = 0.85; } + if ((matchesKnn[descInd][0].distance / matchesKnn[descInd][1].distance) < ratioThreshold) + { + bool already_exists = false; + // Quickly run through the matches we've already added and make sure it's not a duplicate... + for (int q = 0; q < matches12.size(); q++) + { + if (matchesKnn[descInd][0].queryIdx == matches12[q].queryIdx) + { + already_exists = true; + break; + } + else if ((matchesKnn[descInd][0].trainIdx == matches12[q].trainIdx) && + (matchesKnn[descInd][0].imgIdx == matches12[q].imgIdx)) + { + already_exists = true; + break; + } + } + // Good match. + if (already_exists == false) + matches12.push_back(matchesKnn[descInd][0]); + } + + //} + } + else if (matchesKnn[descInd].size() == 1) + { + // Only match? Does this ever happen? + matches12.push_back(matchesKnn[descInd][0]); + } + // In the ratio test, we will compare the quality of a match with the next match that is not from the same object: + // we can accept several matches with similar scores as long as they are for the same object. Those should not be + // part of the model anyway as they are not discriminative enough + + //for (unsigned int first_index = 0; first_index < matches.size(); ++first_index) + //{ + + //matches12.push_back(match); + //} + + } } @@ -176,8 +161,8 @@ void FeatureMatcher::crisscrossFiltering(const vector queryKeypoints, vector matchesForOnePlate; for (int j = 0; j < inputMatches.size(); j++) { - if (inputMatches[j].imgIdx == i) - matchesForOnePlate.push_back(inputMatches[j]); + if (inputMatches[j].imgIdx == i) + matchesForOnePlate.push_back(inputMatches[j]); } // For each plate, compare the lines for the keypoints (training image and query image) @@ -196,8 +181,6 @@ void FeatureMatcher::crisscrossFiltering(const vector queryKeypoints, matchIdx.push_back(j); } - - // Iterate through each line (n^2) removing the one with the most criss-crosses until there are none left. int mostIntersections = 1; while (mostIntersections > 0 && vlines.size() > 0) @@ -207,37 +190,37 @@ void FeatureMatcher::crisscrossFiltering(const vector queryKeypoints, for (int j = 0; j < vlines.size(); j++) { - int intrCount = 0; - for (int q = 0; q < vlines.size(); q++) - { - Point vintr = vlines[j].intersection(vlines[q]); - Point hintr = hlines[j].intersection(hlines[q]); - float vangleDiff = abs(vlines[j].angle - vlines[q].angle); - float hangleDiff = abs(hlines[j].angle - hlines[q].angle); - if (vintr.inside(crissCrossAreaVertical) && vangleDiff > 10) - { - intrCount++; - } - else if (hintr.inside(crissCrossAreaHorizontal) && hangleDiff > 10) - { - intrCount++; - } - } + int intrCount = 0; + for (int q = 0; q < vlines.size(); q++) + { + Point vintr = vlines[j].intersection(vlines[q]); + Point hintr = hlines[j].intersection(hlines[q]); + float vangleDiff = abs(vlines[j].angle - vlines[q].angle); + float hangleDiff = abs(hlines[j].angle - hlines[q].angle); + if (vintr.inside(crissCrossAreaVertical) && vangleDiff > 10) + { + intrCount++; + } + else if (hintr.inside(crissCrossAreaHorizontal) && hangleDiff > 10) + { + intrCount++; + } + } - if (intrCount > mostIntersections) - { - mostIntersections = intrCount; - mostIntersectionsIndex = j; - } + if (intrCount > mostIntersections) + { + mostIntersections = intrCount; + mostIntersectionsIndex = j; + } } if (mostIntersectionsIndex >= 0) { - if (this->config->debugStateId) - cout << "Filtered intersection! " << billMapping[i] << endl; - vlines.erase(vlines.begin() + mostIntersectionsIndex); - hlines.erase(hlines.begin() + mostIntersectionsIndex); - matchIdx.erase(matchIdx.begin() + mostIntersectionsIndex); + if (this->config->debugStateId) + cout << "Filtered intersection! " << billMapping[i] << endl; + vlines.erase(vlines.begin() + mostIntersectionsIndex); + hlines.erase(hlines.begin() + mostIntersectionsIndex); + matchIdx.erase(matchIdx.begin() + mostIntersectionsIndex); } } @@ -251,135 +234,124 @@ void FeatureMatcher::crisscrossFiltering(const vector queryKeypoints, } - // Returns true if successful, false otherwise bool FeatureMatcher::loadRecognitionSet(string country) { - std::ostringstream out; - out << config->getKeypointsRuntimeDir() << "/" << country << "/"; - string country_dir = out.str(); + std::ostringstream out; + out << config->getKeypointsRuntimeDir() << "/" << country << "/"; + string country_dir = out.str(); + if (DirectoryExists(country_dir.c_str())) + { + vector trainImages; + vector plateFiles = getFilesInDir(country_dir.c_str()); - if (DirectoryExists(country_dir.c_str())) + for (int i = 0; i < plateFiles.size(); i++) { - vector trainImages; - vector plateFiles = getFilesInDir(country_dir.c_str()); + if (hasEnding(plateFiles[i], ".jpg") == false) + continue; - for (int i = 0; i < plateFiles.size(); i++) + string fullpath = country_dir + plateFiles[i]; + Mat img = imread( fullpath ); + + // convert to gray and resize to the size of the templates + cvtColor(img, img, CV_BGR2GRAY); + resize(img, img, getSizeMaintainingAspect(img, config->stateIdImageWidthPx, config->stateIdimageHeightPx)); + + if( img.empty() ) { - if (hasEnding(plateFiles[i], ".jpg") == false) - continue; - - string fullpath = country_dir + plateFiles[i]; - Mat img = imread( fullpath ); - - // convert to gray and resize to the size of the templates - cvtColor(img, img, CV_BGR2GRAY); - resize(img, img, getSizeMaintainingAspect(img, config->stateIdImageWidthPx, config->stateIdimageHeightPx)); - - if( img.empty() ) - { - cout << "Can not read images" << endl; - return -1; - } - - - Mat descriptors; - - vector keypoints; - detector->detect( img, keypoints ); - extractor->compute(img, keypoints, descriptors); - - if (descriptors.cols > 0) - { - billMapping.push_back(plateFiles[i].substr(0, 2)); - trainImages.push_back(descriptors); - trainingImgKeypoints.push_back(keypoints); - } - + cout << "Can not read images" << endl; + return -1; } + Mat descriptors; - this->descriptorMatcher->add(trainImages); - this->descriptorMatcher->train(); + vector keypoints; + detector->detect( img, keypoints ); + extractor->compute(img, keypoints, descriptors); + + if (descriptors.cols > 0) + { + billMapping.push_back(plateFiles[i].substr(0, 2)); + trainImages.push_back(descriptors); + trainingImgKeypoints.push_back(keypoints); + } - return true; } - return false; + this->descriptorMatcher->add(trainImages); + this->descriptorMatcher->train(); + + return true; + } + + return false; } - - RecognitionResult FeatureMatcher::recognize( const Mat& queryImg, bool drawOnImage, Mat* outputImage, - bool debug_on, vector debug_matches_array - ) + bool debug_on, vector debug_matches_array + ) { - RecognitionResult result; + RecognitionResult result; - result.haswinner = false; + result.haswinner = false; - Mat queryDescriptors; - vector queryKeypoints; + Mat queryDescriptors; + vector queryKeypoints; - detector->detect( queryImg, queryKeypoints ); - extractor->compute(queryImg, queryKeypoints, queryDescriptors); + detector->detect( queryImg, queryKeypoints ); + extractor->compute(queryImg, queryKeypoints, queryDescriptors); - - - if (queryKeypoints.size() <= 5) + if (queryKeypoints.size() <= 5) + { + // Cut it loose if there's less than 5 keypoints... nothing would ever match anyway and it could crash the matcher. + if (drawOnImage) { - // Cut it loose if there's less than 5 keypoints... nothing would ever match anyway and it could crash the matcher. - if (drawOnImage) - { - drawKeypoints( queryImg, queryKeypoints, *outputImage, CV_RGB(0, 255, 0), DrawMatchesFlags::DEFAULT ); - } - return result; + drawKeypoints( queryImg, queryKeypoints, *outputImage, CV_RGB(0, 255, 0), DrawMatchesFlags::DEFAULT ); } + return result; + } + vector filteredMatches; + surfStyleMatching( queryDescriptors, queryKeypoints, filteredMatches ); - vector filteredMatches; + // Create and initialize the counts to 0 + std::vector bill_match_counts( billMapping.size() ); - surfStyleMatching( queryDescriptors, queryKeypoints, filteredMatches ); + for (int i = 0; i < billMapping.size(); i++) + { + bill_match_counts[i] = 0; + } + for (int i = 0; i < filteredMatches.size(); i++) + { + bill_match_counts[filteredMatches[i].imgIdx]++; + //if (filteredMatches[i].imgIdx + } - // Create and initialize the counts to 0 - std::vector bill_match_counts( billMapping.size() ); - - for (int i = 0; i < billMapping.size(); i++) { bill_match_counts[i] = 0; } - - for (int i = 0; i < filteredMatches.size(); i++) + float max_count = 0; // represented as a percent (0 to 100) + int secondmost_count = 0; + int maxcount_index = -1; + for (int i = 0; i < billMapping.size(); i++) + { + if (bill_match_counts[i] > max_count && bill_match_counts[i] >= 4) { - bill_match_counts[filteredMatches[i].imgIdx]++; - //if (filteredMatches[i].imgIdx + secondmost_count = max_count; + if (secondmost_count <= 2) // A value of 1 or 2 is effectively 0 + secondmost_count = 0; + + max_count = bill_match_counts[i]; + maxcount_index = i; } + } - - - float max_count = 0; // represented as a percent (0 to 100) - int secondmost_count = 0; - int maxcount_index = -1; - for (int i = 0; i < billMapping.size(); i++) - { - if (bill_match_counts[i] > max_count && bill_match_counts[i] >= 4) - { - secondmost_count = max_count; - if (secondmost_count <= 2) // A value of 1 or 2 is effectively 0 - secondmost_count = 0; - - max_count = bill_match_counts[i]; - maxcount_index = i; - } - } - - float score = ((max_count - secondmost_count - 3) / 10) * 100; - if (score < 0) - score = 0; - else if (score > 100) - score = 100; - + float score = ((max_count - secondmost_count - 3) / 10) * 100; + if (score < 0) + score = 0; + else if (score > 100) + score = 100; if (score > 0) { @@ -389,28 +361,28 @@ RecognitionResult FeatureMatcher::recognize( const Mat& queryImg, bool drawOnIma if (drawOnImage) { - vector positiveMatches; - for (int i = 0; i < filteredMatches.size(); i++) - { - if (filteredMatches[i].imgIdx == maxcount_index) - { - positiveMatches.push_back( queryKeypoints[filteredMatches[i].queryIdx] ); - } - } + vector positiveMatches; + for (int i = 0; i < filteredMatches.size(); i++) + { + if (filteredMatches[i].imgIdx == maxcount_index) + { + positiveMatches.push_back( queryKeypoints[filteredMatches[i].queryIdx] ); + } + } - Mat tmpImg; - drawKeypoints( queryImg, queryKeypoints, tmpImg, CV_RGB(185, 0, 0), DrawMatchesFlags::DEFAULT ); - drawKeypoints( tmpImg, positiveMatches, *outputImage, CV_RGB(0, 255, 0), DrawMatchesFlags::DEFAULT ); + Mat tmpImg; + drawKeypoints( queryImg, queryKeypoints, tmpImg, CV_RGB(185, 0, 0), DrawMatchesFlags::DEFAULT ); + drawKeypoints( tmpImg, positiveMatches, *outputImage, CV_RGB(0, 255, 0), DrawMatchesFlags::DEFAULT ); - if (result.haswinner == true) - { + if (result.haswinner == true) + { - std::ostringstream out; - out << result.winner << " (" << result.confidence << "%)"; + std::ostringstream out; + out << result.winner << " (" << result.confidence << "%)"; - // we detected a bill, let the people know! - //putText(*outputImage, out.str(), Point(15, 27), FONT_HERSHEY_DUPLEX, 1.1, CV_RGB(0, 0, 0), 2); - } + // we detected a bill, let the people know! + //putText(*outputImage, out.str(), Point(15, 27), FONT_HERSHEY_DUPLEX, 1.1, CV_RGB(0, 0, 0), 2); + } } } @@ -420,12 +392,11 @@ RecognitionResult FeatureMatcher::recognize( const Mat& queryImg, bool drawOnIma for (int i = 0; i < billMapping.size(); i++) { - cout << billMapping[i] << " : " << bill_match_counts[i] << endl; + cout << billMapping[i] << " : " << bill_match_counts[i] << endl; } } return result; - } diff --git a/src/openalpr/licenseplatecandidate.cpp b/src/openalpr/licenseplatecandidate.cpp index 5c92af4..08831ea 100644 --- a/src/openalpr/licenseplatecandidate.cpp +++ b/src/openalpr/licenseplatecandidate.cpp @@ -19,13 +19,12 @@ #include "licenseplatecandidate.h" - LicensePlateCandidate::LicensePlateCandidate(Mat frame, Rect regionOfInterest, Config* config) { - this->config = config; + this->config = config; - this->frame = frame; - this->plateRegion = regionOfInterest; + this->frame = frame; + this->plateRegion = regionOfInterest; } LicensePlateCandidate::~LicensePlateCandidate() @@ -38,7 +37,6 @@ void LicensePlateCandidate::recognize() { charSegmenter = NULL; - this->confidence = 0; int expandX = round(this->plateRegion.width * 0.15); @@ -46,19 +44,14 @@ void LicensePlateCandidate::recognize() // expand box by 15% in all directions Rect expandedRegion = expandRect( this->plateRegion, expandX, expandY, frame.cols, frame.rows) ; - - - Mat plate_bgr = Mat(frame, expandedRegion); resize(plate_bgr, plate_bgr, Size(config->templateWidthPx, config->templateHeightPx)); Mat plate_bgr_cleaned = Mat(plate_bgr.size(), plate_bgr.type()); this->cleanupColors(plate_bgr, plate_bgr_cleaned); - CharacterRegion charRegion(plate_bgr, config); - if (charRegion.confidence > 10) { @@ -75,15 +68,12 @@ void LicensePlateCandidate::recognize() { this->plateCorners = transformPointsToOriginalImage(frame, plate_bgr, expandedRegion, smallPlateCorners); - this->deskewed = deSkewPlate(frame, this->plateCorners); - charSegmenter = new CharacterSegmenter(deskewed, charRegion.thresholdsInverted(), config); - //this->recognizedText = ocr->recognizedText; - //strcpy(this->recognizedText, ocr.recognizedText); + //strcpy(this->recognizedText, ocr.recognizedText); this->confidence = 100; @@ -91,13 +81,8 @@ void LicensePlateCandidate::recognize() charRegion.confidence = 0; } - } - - - - // Re-maps the coordinates from the smallImage to the coordinate space of the bigImage. vector LicensePlateCandidate::transformPointsToOriginalImage(Mat bigImage, Mat smallImage, Rect region, vector corners) { @@ -116,7 +101,6 @@ vector LicensePlateCandidate::transformPointsToOriginalImage(Mat bigIma return cornerPoints; } - Mat LicensePlateCandidate::deSkewPlate(Mat inputImage, vector corners) { @@ -134,8 +118,8 @@ Mat LicensePlateCandidate::deSkewPlate(Mat inputImage, vector corners) int height = round(((float) width) / aspect); if (height > config->ocrImageHeightPx) { - height = config->ocrImageHeightPx; - width = round(((float) height) * aspect); + height = config->ocrImageHeightPx; + width = round(((float) height) * aspect); } Mat deskewed(height, width, frame.type()); @@ -159,7 +143,6 @@ Mat LicensePlateCandidate::deSkewPlate(Mat inputImage, vector corners) return deskewed; } - void LicensePlateCandidate::cleanupColors(Mat inputImage, Mat outputImage) { if (this->config->debugGeneral) @@ -174,26 +157,24 @@ void LicensePlateCandidate::cleanupColors(Mat inputImage, Mat outputImage) // Equalize intensity: if(intermediate.channels() >= 3) { - Mat ycrcb; + Mat ycrcb; - cvtColor(intermediate,ycrcb,CV_BGR2YCrCb); + cvtColor(intermediate,ycrcb,CV_BGR2YCrCb); - vector channels; - split(ycrcb,channels); + vector channels; + split(ycrcb,channels); - equalizeHist(channels[0], channels[0]); + equalizeHist(channels[0], channels[0]); - merge(channels,ycrcb); + merge(channels,ycrcb); - cvtColor(ycrcb,intermediate,CV_YCrCb2BGR); + cvtColor(ycrcb,intermediate,CV_YCrCb2BGR); - //ycrcb.release(); + //ycrcb.release(); } - bilateralFilter(intermediate, outputImage, 3, 25, 35); - if (this->config->debugGeneral) { displayImage(config, "After cleanup", outputImage); diff --git a/src/openalpr/ocr.cpp b/src/openalpr/ocr.cpp index ff7be2a..2be8c85 100644 --- a/src/openalpr/ocr.cpp +++ b/src/openalpr/ocr.cpp @@ -17,30 +17,26 @@ * along with this program. If not, see . */ - - - #include "ocr.h" OCR::OCR(Config* config) { this->config = config; - this->postProcessor = new PostProcess(config); + this->postProcessor = new PostProcess(config); - tesseract=new TessBaseAPI(); + tesseract=new TessBaseAPI(); - // Tesseract requires the prefix directory to be set as an env variable - vector tessdataPrefix(config->getTessdataPrefix().size() + 1); + // Tesseract requires the prefix directory to be set as an env variable + vector tessdataPrefix(config->getTessdataPrefix().size() + 1); - strcpy(tessdataPrefix.data(), config->getTessdataPrefix().c_str()); - putenv(tessdataPrefix.data()); + strcpy(tessdataPrefix.data(), config->getTessdataPrefix().c_str()); + putenv(tessdataPrefix.data()); - - tesseract->Init("", config->ocrLanguage.c_str() ); - tesseract->SetVariable("save_blob_choices", "T"); - //tesseract->SetVariable("tessedit_char_whitelist", "ABCDEFGHIJKLMNPQRSTUVWXYZ1234567890"); - tesseract->SetPageSegMode(PSM_SINGLE_CHAR); + tesseract->Init("", config->ocrLanguage.c_str() ); + tesseract->SetVariable("save_blob_choices", "T"); + //tesseract->SetVariable("tessedit_char_whitelist", "ABCDEFGHIJKLMNPQRSTUVWXYZ1234567890"); + tesseract->SetPageSegMode(PSM_SINGLE_CHAR); } OCR::~OCR() @@ -50,18 +46,14 @@ OCR::~OCR() delete tesseract; } - void OCR::performOCR(vector thresholds, vector charRegions) { - timespec startTime; getTime(&startTime); - postProcessor->clear(); - for (int i = 0; i < thresholds.size(); i++) { @@ -69,7 +61,6 @@ void OCR::performOCR(vector thresholds, vector charRegions) bitwise_not(thresholds[i], thresholds[i]); tesseract->SetImage((uchar*) thresholds[i].data, thresholds[i].size().width, thresholds[i].size().height, thresholds[i].channels(), thresholds[i].step1()); - for (int j = 0; j < charRegions.size(); j++) { Rect expandedRegion = expandRect( charRegions[j], 2, 2, thresholds[i].cols, thresholds[i].rows) ; @@ -79,52 +70,54 @@ void OCR::performOCR(vector thresholds, vector charRegions) tesseract::ResultIterator* ri = tesseract->GetIterator(); tesseract::PageIteratorLevel level = tesseract::RIL_SYMBOL; - do { - const char* symbol = ri->GetUTF8Text(level); - float conf = ri->Confidence(level); + do + { + const char* symbol = ri->GetUTF8Text(level); + float conf = ri->Confidence(level); - bool dontcare; - int fontindex = 0; - int pointsize = 0; - const char* fontName = ri->WordFontAttributes(&dontcare, &dontcare, &dontcare, &dontcare, &dontcare, &dontcare, &pointsize, &fontindex); + bool dontcare; + int fontindex = 0; + int pointsize = 0; + const char* fontName = ri->WordFontAttributes(&dontcare, &dontcare, &dontcare, &dontcare, &dontcare, &dontcare, &pointsize, &fontindex); - if(symbol != 0 && pointsize >= config->ocrMinFontSize) { - postProcessor->addLetter(*symbol, j, conf); - - - if (this->config->debugOcr) - printf("charpos%d: threshold %d: symbol %s, conf: %f font: %s (index %d) size %dpx", j, i, symbol, conf, fontName, fontindex, pointsize); - - bool indent = false; - tesseract::ChoiceIterator ci(*ri); - do { - const char* choice = ci.GetUTF8Text(); - - postProcessor->addLetter(*choice, j, ci.Confidence()); - - - //letterScores.addScore(*choice, j, ci.Confidence() - MIN_CONFIDENCE); - if (this->config->debugOcr) - { - if (indent) printf("\t\t "); - printf("\t- "); - printf("%s conf: %f\n", choice, ci.Confidence()); - } - - indent = true; - } while(ci.Next()); - } + if(symbol != 0 && pointsize >= config->ocrMinFontSize) + { + postProcessor->addLetter(*symbol, j, conf); if (this->config->debugOcr) - printf("---------------------------------------------\n"); + printf("charpos%d: threshold %d: symbol %s, conf: %f font: %s (index %d) size %dpx", j, i, symbol, conf, fontName, fontindex, pointsize); - delete[] symbol; - } while((ri->Next(level))); + bool indent = false; + tesseract::ChoiceIterator ci(*ri); + do + { + const char* choice = ci.GetUTF8Text(); + + postProcessor->addLetter(*choice, j, ci.Confidence()); + + //letterScores.addScore(*choice, j, ci.Confidence() - MIN_CONFIDENCE); + if (this->config->debugOcr) + { + if (indent) printf("\t\t "); + printf("\t- "); + printf("%s conf: %f\n", choice, ci.Confidence()); + } + + indent = true; + } + while(ci.Next()); + } + + if (this->config->debugOcr) + printf("---------------------------------------------\n"); + + delete[] symbol; + } + while((ri->Next(level))); delete ri; } - } if (config->debugTiming) @@ -134,7 +127,4 @@ void OCR::performOCR(vector thresholds, vector charRegions) cout << "OCR Time: " << diffclock(startTime, endTime) << "ms." << endl; } - - - } diff --git a/src/openalpr/platecorners.cpp b/src/openalpr/platecorners.cpp index f8d2c43..68a91f9 100644 --- a/src/openalpr/platecorners.cpp +++ b/src/openalpr/platecorners.cpp @@ -17,7 +17,6 @@ * along with this program. If not, see . */ - #include "platecorners.h" PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, CharacterRegion* charRegion, Config* config) @@ -27,8 +26,6 @@ PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, CharacterRegi if (this->config->debugPlateCorners) cout << "PlateCorners constructor" << endl; - - this->inputImage = inputImage; this->plateLines = plateLines; this->charRegion = charRegion; @@ -36,7 +33,6 @@ PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, CharacterRegi this->bestHorizontalScore = 9999999999999; this->bestVerticalScore = 9999999999999; - Point topPoint = charRegion->getTopLine().midpoint(); Point bottomPoint = charRegion->getBottomLine().closestPointOnSegmentTo(topPoint); this->charHeight = distanceBetweenPoints(topPoint, bottomPoint); @@ -57,7 +53,7 @@ PlateCorners::~PlateCorners() vector PlateCorners::findPlateCorners() { if (this->config->debugPlateCorners) - cout << "PlateCorners::findPlateCorners" << endl; + cout << "PlateCorners::findPlateCorners" << endl; timespec startTime; getTime(&startTime); @@ -65,15 +61,14 @@ vector PlateCorners::findPlateCorners() int horizontalLines = this->plateLines->horizontalLines.size(); int verticalLines = this->plateLines->verticalLines.size(); - // layout horizontal lines for (int h1 = NO_LINE; h1 < horizontalLines; h1++) { for (int h2 = NO_LINE; h2 < horizontalLines; h2++) { - if (h1 == h2 && h1 != NO_LINE) continue; + if (h1 == h2 && h1 != NO_LINE) continue; - this->scoreHorizontals(h1, h2); + this->scoreHorizontals(h1, h2); } } @@ -81,35 +76,30 @@ vector PlateCorners::findPlateCorners() // layout vertical lines for (int v1 = NO_LINE; v1 < verticalLines; v1++) { - for (int v2 = NO_LINE; v2 < verticalLines; v2++) - { - if (v1 == v2 && v1 != NO_LINE) continue; + for (int v2 = NO_LINE; v2 < verticalLines; v2++) + { + if (v1 == v2 && v1 != NO_LINE) continue; - this->scoreVerticals(v1, v2); - } + this->scoreVerticals(v1, v2); + } } - if (this->config->debugPlateCorners) { - cout << "Drawing debug stuff..." << endl; + cout << "Drawing debug stuff..." << endl; - Mat imgCorners = Mat(inputImage.size(), inputImage.type()); - inputImage.copyTo(imgCorners); - for (int i = 0; i < 4; i++) - circle(imgCorners, charRegion->getCharArea()[i], 2, Scalar(0, 0, 0)); + Mat imgCorners = Mat(inputImage.size(), inputImage.type()); + inputImage.copyTo(imgCorners); + for (int i = 0; i < 4; i++) + circle(imgCorners, charRegion->getCharArea()[i], 2, Scalar(0, 0, 0)); + line(imgCorners, this->bestTop.p1, this->bestTop.p2, Scalar(255, 0, 0), 1, CV_AA); + line(imgCorners, this->bestRight.p1, this->bestRight.p2, Scalar(0, 0, 255), 1, CV_AA); + line(imgCorners, this->bestBottom.p1, this->bestBottom.p2, Scalar(0, 0, 255), 1, CV_AA); + line(imgCorners, this->bestLeft.p1, this->bestLeft.p2, Scalar(255, 0, 0), 1, CV_AA); - line(imgCorners, this->bestTop.p1, this->bestTop.p2, Scalar(255, 0, 0), 1, CV_AA); - line(imgCorners, this->bestRight.p1, this->bestRight.p2, Scalar(0, 0, 255), 1, CV_AA); - line(imgCorners, this->bestBottom.p1, this->bestBottom.p2, Scalar(0, 0, 255), 1, CV_AA); - line(imgCorners, this->bestLeft.p1, this->bestLeft.p2, Scalar(255, 0, 0), 1, CV_AA); - - - - - displayImage(config, "Winning top/bottom Boundaries", imgCorners); + displayImage(config, "Winning top/bottom Boundaries", imgCorners); } @@ -127,8 +117,6 @@ vector PlateCorners::findPlateCorners() corners.push_back(bestBottom.intersection(bestRight)); corners.push_back(bestBottom.intersection(bestLeft)); - - if (config->debugTiming) { timespec endTime; @@ -139,7 +127,6 @@ vector PlateCorners::findPlateCorners() return corners; } - void PlateCorners::scoreVerticals(int v1, int v2) { @@ -148,8 +135,6 @@ void PlateCorners::scoreVerticals(int v1, int v2) LineSegment left; LineSegment right; - - float charHeightToPlateWidthRatio = config->plateWidthMM / config->charHeightMM; float idealPixelWidth = this->charHeight * (charHeightToPlateWidthRatio * 1.05); // Add 10% so we don't clip any characters @@ -183,8 +168,6 @@ void PlateCorners::scoreVerticals(int v1, int v2) score += SCORING_MISSING_SEGMENT_PENALTY_VERTICAL; } - - // Make sure this line is to the left of our license plate letters if (left.isPointBelowLine(charRegion->getCharBoxLeft().midpoint()) == false) return; @@ -193,7 +176,6 @@ void PlateCorners::scoreVerticals(int v1, int v2) if (right.isPointBelowLine(charRegion->getCharBoxRight().midpoint())) return; - ///////////////////////////////////////////////////////////////////////// // Score "Distance from the edge... ///////////////////////////////////////////////////////////////////////// @@ -204,7 +186,6 @@ void PlateCorners::scoreVerticals(int v1, int v2) float distanceFromEdge = leftDistanceFromEdge + rightDistanceFromEdge; score += distanceFromEdge * SCORING_VERTICALDISTANCE_FROMEDGE_WEIGHT; - ///////////////////////////////////////////////////////////////////////// // Score "Boxiness" of the 4 lines. How close is it to a parallelogram? ///////////////////////////////////////////////////////////////////////// @@ -213,12 +194,10 @@ void PlateCorners::scoreVerticals(int v1, int v2) score += (verticalAngleDiff) * SCORING_BOXINESS_WEIGHT; - ////////////////////////////////////////////////////////////////////////// // SCORE the shape wrt character position and height relative to position ////////////////////////////////////////////////////////////////////////// - Point leftMidLinePoint = left.closestPointOnSegmentTo(charRegion->getCharBoxLeft().midpoint()); Point rightMidLinePoint = right.closestPointOnSegmentTo(charRegion->getCharBoxRight().midpoint()); @@ -230,7 +209,6 @@ void PlateCorners::scoreVerticals(int v1, int v2) { float scorecomponent; - if (this->config->debugPlateCorners) { cout << "xx xx Score: charHeight " << this->charHeight << endl; @@ -277,7 +255,6 @@ void PlateCorners::scoreHorizontals(int h1, int h2) float charHeightToPlateHeightRatio = config->plateHeightMM / config->charHeightMM; float idealPixelHeight = this->charHeight * charHeightToPlateHeightRatio; - if (h1 == NO_LINE && h2 == NO_LINE) { // return; @@ -308,39 +285,26 @@ void PlateCorners::scoreHorizontals(int h1, int h2) score += SCORING_MISSING_SEGMENT_PENALTY_HORIZONTAL; } - - // Make sure this line is above our license plate letters if (top.isPointBelowLine(charRegion->getCharBoxTop().midpoint()) == false) return; - // Make sure this line is below our license plate letters if (bottom.isPointBelowLine(charRegion->getCharBoxBottom().midpoint())) return; - - - - - // We now have 4 possible lines. Let's put them to the test and score them... - - - ///////////////////////////////////////////////////////////////////////// // Score "Boxiness" of the 4 lines. How close is it to a parallelogram? ///////////////////////////////////////////////////////////////////////// float horizontalAngleDiff = abs(top.angle - bottom.angle); - score += (horizontalAngleDiff) * SCORING_BOXINESS_WEIGHT; // if (this->debug) // cout << "PlateCorners boxiness score: " << (horizontalAngleDiff + verticalAngleDiff) * SCORING_BOXINESS_WEIGHT << endl; - ////////////////////////////////////////////////////////////////////////// // SCORE the shape wrt character position and height relative to position ////////////////////////////////////////////////////////////////////////// @@ -351,7 +315,6 @@ void PlateCorners::scoreHorizontals(int h1, int h2) // Get the height difference - float heightRatio = charHeight / plateHeightPx; float idealHeightRatio = (config->charHeightMM / config->plateHeightMM); //if (leftRatio < MIN_CHAR_HEIGHT_RATIO || leftRatio > MAX_CHAR_HEIGHT_RATIO || rightRatio < MIN_CHAR_HEIGHT_RATIO || rightRatio > MAX_CHAR_HEIGHT_RATIO) @@ -367,7 +330,6 @@ void PlateCorners::scoreHorizontals(int h1, int h2) // float idealBottomDistance = charHeight * (BOTTOM_WHITESPACE_HEIGHT_MM / CHARACTER_HEIGHT_MM); // float distScore = abs(topDistance - idealTopDistance) + abs(bottomDistance - idealBottomDistance); - score += heightRatioDiff * SCORING_PLATEHEIGHT_WEIGHT; ////////////////////////////////////////////////////////////////////////// @@ -398,14 +360,11 @@ void PlateCorners::scoreHorizontals(int h1, int h2) float charanglediff = abs(charAngle - top.angle) + abs(charAngle - bottom.angle); - score += charanglediff * SCORING_ANGLE_MATCHES_LPCHARS_WEIGHT; // if (this->debug) // cout << "PlateCorners boxiness score: " << charanglediff * SCORING_ANGLE_MATCHES_LPCHARS_WEIGHT << endl; - - if (score < this->bestHorizontalScore) { float scorecomponent; @@ -442,6 +401,4 @@ void PlateCorners::scoreHorizontals(int h1, int h2) bestBottom = LineSegment(bottom.p1.x, bottom.p1.y, bottom.p2.x, bottom.p2.y); } - - } diff --git a/src/openalpr/platelines.cpp b/src/openalpr/platelines.cpp index f19e05b..9235d61 100644 --- a/src/openalpr/platelines.cpp +++ b/src/openalpr/platelines.cpp @@ -19,7 +19,6 @@ #include "platelines.h" - PlateLines::PlateLines(Config* config) { this->config = config; @@ -28,7 +27,6 @@ PlateLines::PlateLines(Config* config) if (debug) cout << "PlateLines constructor" << endl; - } PlateLines::~PlateLines() @@ -36,22 +34,18 @@ PlateLines::~PlateLines() } - - void PlateLines::processImage(Mat inputImage, float sensitivity) { if (this->debug) cout << "PlateLines findLines" << endl; - timespec startTime; getTime(&startTime); - Mat smoothed(inputImage.size(), inputImage.type()); inputImage.copyTo(smoothed); - int morph_elem = 2; - int morph_size = 2; + int morph_elem = 2; + int morph_size = 2; Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) ); morphologyEx( smoothed, smoothed, MORPH_CLOSE, element ); @@ -65,12 +59,9 @@ void PlateLines::processImage(Mat inputImage, float sensitivity) element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) ); morphologyEx( smoothed, smoothed, MORPH_OPEN, element ); - - Mat edges(inputImage.size(), inputImage.type()); Canny(smoothed, edges, 66, 133); - vector hlines = this->getLines(edges, sensitivity, false); vector vlines = this->getLines(edges, sensitivity, true); for (int i = 0; i < hlines.size(); i++) @@ -78,9 +69,6 @@ void PlateLines::processImage(Mat inputImage, float sensitivity) for (int i = 0; i < vlines.size(); i++) this->verticalLines.push_back(vlines[i]); - - - // if debug is enabled, draw the image if (this->debug) { @@ -109,8 +97,6 @@ void PlateLines::processImage(Mat inputImage, float sensitivity) displayImage(config, "Hough Lines", dashboard); } - - if (config->debugTiming) { timespec endTime; @@ -119,7 +105,6 @@ void PlateLines::processImage(Mat inputImage, float sensitivity) } //smoothed.release(); - //////////////// METHOD2!!!!!!!//////////////////// /* @@ -127,33 +112,28 @@ void PlateLines::processImage(Mat inputImage, float sensitivity) Mat imgCanny; GaussianBlur(inputImage, imgBlur, Size(9, 9), 1, 1); - - Canny(imgBlur, imgCanny, 10, 30, 3); - - //int morph_elem = 2; //int morph_size = 1; //Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) ); morphologyEx( imgCanny, imgCanny, MORPH_CLOSE, element ); - Mat imgShaped; imgCanny.copyTo(imgShaped); //Find contours of possibles characters vector< vector< Point> > biggestShapes; findContours(imgShaped, - biggestShapes, // a vector of contours - CV_RETR_EXTERNAL, // retrieve the external contours - CV_CHAIN_APPROX_SIMPLE ); // all pixels of each contours + biggestShapes, // a vector of contours + CV_RETR_EXTERNAL, // retrieve the external contours + CV_CHAIN_APPROX_SIMPLE ); // all pixels of each contours // Draw blue contours on a white image //cvtColor(imgShaped, imgShaped, CV_GRAY2RGB); cv::drawContours(imgShaped,biggestShapes, - -1, // draw all contours - cv::Scalar(255,255,255), // in blue - 1); // with a thickness of 1 + -1, // draw all contours + cv::Scalar(255,255,255), // in blue + 1); // with a thickness of 1 displayImage(config, "Blurred", imgCanny); displayImage(config, "Blurred Contours", imgShaped); @@ -187,7 +167,6 @@ vector PlateLines::getLines(Mat edges, bool vertical) vector errors; lswms.run(edges, lsegs, errors); - for( size_t i = 0; i < lsegs.size(); i++ ) { @@ -247,7 +226,6 @@ vector PlateLines::getLines(Mat edges, bool vertical) } */ - vector PlateLines::getLines(Mat edges, float sensitivityMultiplier, bool vertical) { if (this->debug) @@ -267,100 +245,92 @@ vector PlateLines::getLines(Mat edges, float sensitivityMultiplier, HoughLines( edges, allLines, 1, CV_PI/180, sensitivity, 0, 0 ); - for( size_t i = 0; i < allLines.size(); i++ ) { - float rho = allLines[i][0], theta = allLines[i][1]; - Point pt1, pt2; - double a = cos(theta), b = sin(theta); - double x0 = a*rho, y0 = b*rho; + float rho = allLines[i][0], theta = allLines[i][1]; + Point pt1, pt2; + double a = cos(theta), b = sin(theta); + double x0 = a*rho, y0 = b*rho; - double angle = theta * (180 / CV_PI); - pt1.x = cvRound(x0 + 1000*(-b)); - pt1.y = cvRound(y0 + 1000*(a)); - pt2.x = cvRound(x0 - 1000*(-b)); - pt2.y = cvRound(y0 - 1000*(a)); + double angle = theta * (180 / CV_PI); + pt1.x = cvRound(x0 + 1000*(-b)); + pt1.y = cvRound(y0 + 1000*(a)); + pt2.x = cvRound(x0 - 1000*(-b)); + pt2.y = cvRound(y0 - 1000*(a)); - if (vertical) - { - if (angle < 20 || angle > 340 || (angle > 160 && angle < 210)) - { - // good vertical + if (vertical) + { + if (angle < 20 || angle > 340 || (angle > 160 && angle < 210)) + { + // good vertical - LineSegment line; - if (pt1.y <= pt2.y) - line = LineSegment(pt2.x, pt2.y, pt1.x, pt1.y); - else - line = LineSegment(pt1.x, pt1.y, pt2.x, pt2.y); + LineSegment line; + if (pt1.y <= pt2.y) + line = LineSegment(pt2.x, pt2.y, pt1.x, pt1.y); + else + line = LineSegment(pt1.x, pt1.y, pt2.x, pt2.y); - // Get rid of the -1000, 1000 stuff. Terminate at the edges of the image - // Helps with debugging/rounding issues later - LineSegment top(0, 0, edges.cols, 0); - LineSegment bottom(0, edges.rows, edges.cols, edges.rows); - Point p1 = line.intersection(bottom); - Point p2 = line.intersection(top); - filteredLines.push_back(LineSegment(p1.x, p1.y, p2.x, p2.y)); - } - } - else - { + // Get rid of the -1000, 1000 stuff. Terminate at the edges of the image + // Helps with debugging/rounding issues later + LineSegment top(0, 0, edges.cols, 0); + LineSegment bottom(0, edges.rows, edges.cols, edges.rows); + Point p1 = line.intersection(bottom); + Point p2 = line.intersection(top); + filteredLines.push_back(LineSegment(p1.x, p1.y, p2.x, p2.y)); + } + } + else + { - if ( (angle > 70 && angle < 110) || (angle > 250 && angle < 290)) - { - // good horizontal + if ( (angle > 70 && angle < 110) || (angle > 250 && angle < 290)) + { + // good horizontal - LineSegment line; - if (pt1.x <= pt2.x) - line = LineSegment(pt1.x, pt1.y, pt2.x, pt2.y); - else - line =LineSegment(pt2.x, pt2.y, pt1.x, pt1.y); + LineSegment line; + if (pt1.x <= pt2.x) + line = LineSegment(pt1.x, pt1.y, pt2.x, pt2.y); + else + line =LineSegment(pt2.x, pt2.y, pt1.x, pt1.y); - // Get rid of the -1000, 1000 stuff. Terminate at the edges of the image - // Helps with debugging/ rounding issues later - int newY1 = line.getPointAt(0); - int newY2 = line.getPointAt(edges.cols); + // Get rid of the -1000, 1000 stuff. Terminate at the edges of the image + // Helps with debugging/ rounding issues later + int newY1 = line.getPointAt(0); + int newY2 = line.getPointAt(edges.cols); - filteredLines.push_back(LineSegment(0, newY1, edges.cols, newY2)); - } - } + filteredLines.push_back(LineSegment(0, newY1, edges.cols, newY2)); + } + } } - return filteredLines; } - - - - Mat PlateLines::customGrayscaleConversion(Mat src) { Mat img_hsv; cvtColor(src,img_hsv,CV_BGR2HSV); + Mat grayscale = Mat(img_hsv.size(), CV_8U ); + Mat hue(img_hsv.size(), CV_8U ); - Mat grayscale = Mat(img_hsv.size(), CV_8U ); - Mat hue(img_hsv.size(), CV_8U ); - - for (int row = 0; row < img_hsv.rows; row++) + for (int row = 0; row < img_hsv.rows; row++) + { + for (int col = 0; col < img_hsv.cols; col++) { - for (int col = 0; col < img_hsv.cols; col++) - { - int h = (int) img_hsv.at(row, col)[0]; - int s = (int) img_hsv.at(row, col)[1]; - int v = (int) img_hsv.at(row, col)[2]; + int h = (int) img_hsv.at(row, col)[0]; + int s = (int) img_hsv.at(row, col)[1]; + int v = (int) img_hsv.at(row, col)[2]; - int pixval = pow(v, 1.05); + int pixval = pow(v, 1.05); + if (pixval > 255) + pixval = 255; + grayscale.at(row, col) = pixval; - if (pixval > 255) - pixval = 255; - grayscale.at(row, col) = pixval; - - hue.at(row, col) = h * (255.0 / 180.0); - } + hue.at(row, col) = h * (255.0 / 180.0); } + } - //displayImage(config, "Hue", hue); - return grayscale; + //displayImage(config, "Hue", hue); + return grayscale; } diff --git a/src/openalpr/postprocess.cpp b/src/openalpr/postprocess.cpp index 6501810..bc18921 100644 --- a/src/openalpr/postprocess.cpp +++ b/src/openalpr/postprocess.cpp @@ -19,7 +19,6 @@ #include "postprocess.h" - PostProcess::PostProcess(Config* config) { this->config = config; @@ -29,12 +28,11 @@ PostProcess::PostProcess(Config* config) std::ifstream infile(filename.str().c_str()); - string region, pattern; while (infile >> region >> pattern) { RegexRule* rule = new RegexRule(region, pattern); - //cout << "REGION: " << region << " PATTERN: " << pattern << endl; + //cout << "REGION: " << region << " PATTERN: " << pattern << endl; if (rules.find(region) == rules.end()) { @@ -61,16 +59,16 @@ PostProcess::~PostProcess() // TODO: Delete all entries in rules vector map >::iterator iter; - for (iter = rules.begin(); iter != rules.end(); ++iter) { + for (iter = rules.begin(); iter != rules.end(); ++iter) + { for (int i = 0; i < iter->second.size(); i++) { - delete iter->second[i]; + delete iter->second[i]; } } } - void PostProcess::addLetter(char letter, int charposition, float score) { if (score < config->postProcessMinConfidence) @@ -80,8 +78,8 @@ void PostProcess::addLetter(char letter, int charposition, float score) if (score < config->postProcessConfidenceSkipLevel) { - float adjustedScore = abs(config->postProcessConfidenceSkipLevel - score) + config->postProcessMinConfidence; - insertLetter(SKIP_CHAR, charposition, adjustedScore ); + float adjustedScore = abs(config->postProcessConfidenceSkipLevel - score) + config->postProcessMinConfidence; + insertLetter(SKIP_CHAR, charposition, adjustedScore ); } //if (letter == '0') @@ -96,25 +94,24 @@ void PostProcess::insertLetter(char letter, int charposition, float score) score = score - config->postProcessMinConfidence; - int existingIndex = -1; if (letters.size() < charposition + 1) { - for (int i = letters.size(); i < charposition + 1; i++) - { - vector tmp; - letters.push_back(tmp); - } + for (int i = letters.size(); i < charposition + 1; i++) + { + vector tmp; + letters.push_back(tmp); + } } for (int i = 0; i < letters[charposition].size(); i++) { - if (letters[charposition][i].letter == letter && - letters[charposition][i].charposition == charposition) - { - existingIndex = i; - break; - } + if (letters[charposition][i].letter == letter && + letters[charposition][i].charposition == charposition) + { + existingIndex = i; + break; + } } if (existingIndex == -1) @@ -128,18 +125,17 @@ void PostProcess::insertLetter(char letter, int charposition, float score) } else { - letters[charposition][existingIndex].occurences = letters[charposition][existingIndex].occurences + 1; - letters[charposition][existingIndex].totalscore = letters[charposition][existingIndex].totalscore + score; + letters[charposition][existingIndex].occurences = letters[charposition][existingIndex].occurences + 1; + letters[charposition][existingIndex].totalscore = letters[charposition][existingIndex].totalscore + score; } } - void PostProcess::clear() { for (int i = 0; i < letters.size(); i++) { - letters[i].clear(); + letters[i].clear(); } letters.resize(0); @@ -157,22 +153,18 @@ void PostProcess::analyze(string templateregion, int topn) timespec startTime; getTime(&startTime); - - // Get a list of missing positions for (int i = letters.size() -1; i >= 0; i--) { if (letters[i].size() == 0) { - unknownCharPositions.push_back(i); + unknownCharPositions.push_back(i); } } - if (letters.size() == 0) return; - // Sort the letters as they are for (int i = 0; i < letters.size(); i++) { @@ -180,8 +172,6 @@ void PostProcess::analyze(string templateregion, int topn) sort(letters[i].begin(), letters[i].end(), letterCompare); } - - if (this->config->debugPostProcess) { @@ -189,7 +179,7 @@ void PostProcess::analyze(string templateregion, int topn) for (int i = 0; i < letters.size(); i++) { for (int j = 0; j < letters[i].size(); j++) - cout << "PostProcess Letter: " << letters[i][j].charposition << " " << letters[i][j].letter << " -- score: " << letters[i][j].totalscore << " -- occurences: " << letters[i][j].occurences << endl; + cout << "PostProcess Letter: " << letters[i][j].charposition << " " << letters[i][j].letter << " -- score: " << letters[i][j].totalscore << " -- occurences: " << letters[i][j].occurences << endl; } } @@ -211,7 +201,6 @@ void PostProcess::analyze(string templateregion, int topn) vector tmp; findAllPermutations(tmp, 0, config->postProcessMaxSubstitutions); - timespec sortStartTime; getTime(&sortStartTime); @@ -228,11 +217,8 @@ void PostProcess::analyze(string templateregion, int topn) cout << " -- PostProcess Sort Time: " << diffclock(sortStartTime, sortEndTime) << "ms." << endl; } - - matchesTemplate = false; - if (templateregion != "") { vector regionRules = rules[templateregion]; @@ -241,22 +227,20 @@ void PostProcess::analyze(string templateregion, int topn) { for (int j = 0; j < regionRules.size(); j++) { - allPossibilities[i].matchesTemplate = regionRules[j]->match(allPossibilities[i].letters); - if (allPossibilities[i].matchesTemplate) - { - allPossibilities[i].letters = regionRules[j]->filterSkips(allPossibilities[i].letters); - //bestChars = regionRules[j]->filterSkips(allPossibilities[i].letters); - matchesTemplate = true; - break; - } + allPossibilities[i].matchesTemplate = regionRules[j]->match(allPossibilities[i].letters); + if (allPossibilities[i].matchesTemplate) + { + allPossibilities[i].letters = regionRules[j]->filterSkips(allPossibilities[i].letters); + //bestChars = regionRules[j]->filterSkips(allPossibilities[i].letters); + matchesTemplate = true; + break; + } } - - if (i >= topn - 1) - break; + break; //if (matchesTemplate || i >= TOP_N - 1) - //break; + //break; } } @@ -264,11 +248,11 @@ void PostProcess::analyze(string templateregion, int topn) { for (int z = 0; z < allPossibilities.size(); z++) { - if (allPossibilities[z].matchesTemplate) - { - bestChars = allPossibilities[z].letters; - break; - } + if (allPossibilities[z].matchesTemplate) + { + bestChars = allPossibilities[z].letters; + break; + } } } else @@ -276,7 +260,7 @@ void PostProcess::analyze(string templateregion, int topn) bestChars = allPossibilities[0].letters; } - // Now adjust the confidence scores to a percentage value + // Now adjust the confidence scores to a percentage value if (allPossibilities.size() > 0) { float maxPercentScore = calculateMaxConfidenceScore(); @@ -289,28 +273,23 @@ void PostProcess::analyze(string templateregion, int topn) } - - if (this->config->debugPostProcess) { - // Print top words for (int i = 0; i < allPossibilities.size(); i++) { cout << "Top " << topn << " Possibilities: " << allPossibilities[i].letters << " :\t" << allPossibilities[i].totalscore; if (allPossibilities[i].letters == bestChars) - cout << " <--- "; + cout << " <--- "; cout << endl; if (i >= topn - 1) - break; + break; } cout << allPossibilities.size() << " total permutations" << endl; } - - if (config->debugTiming) { timespec endTime; @@ -333,8 +312,8 @@ float PostProcess::calculateMaxConfidenceScore() { if (letters[i].size() > 0) { - totalScore += (letters[i][0].totalscore / letters[i][0].occurences) + config->postProcessMinConfidence; - numScores++; + totalScore += (letters[i][0].totalscore / letters[i][0].occurences) + config->postProcessMinConfidence; + numScores++; } } @@ -372,19 +351,18 @@ vector PostProcess::getMaxDepth(int topn) nextLeastDropCharPos = getNextLeastDrop(depth); } - return depth; } int PostProcess::getPermutationCount(vector depth) { int permutationCount = 1; - for (int i = 0; i < depth.size(); i++) - { - permutationCount *= (depth[i] + 1); - } + for (int i = 0; i < depth.size(); i++) + { + permutationCount *= (depth[i] + 1); + } - return permutationCount; + return permutationCount; } int PostProcess::getNextLeastDrop(vector depth) @@ -392,21 +370,21 @@ int PostProcess::getNextLeastDrop(vector depth) int nextLeastDropCharPos = -1; float leastNextDrop = 99999999999; - for (int i = 0; i < letters.size(); i++) + for (int i = 0; i < letters.size(); i++) + { + if (depth[i] + 1 >= letters[i].size()) + continue; + + float drop = letters[i][depth[i]].totalscore - letters[i][depth[i]+1].totalscore; + + if (drop < leastNextDrop) { - if (depth[i] + 1 >= letters[i].size()) - continue; - - float drop = letters[i][depth[i]].totalscore - letters[i][depth[i]+1].totalscore; - - if (drop < leastNextDrop) - { - nextLeastDropCharPos = i; - leastNextDrop = drop; - } + nextLeastDropCharPos = i; + leastNextDrop = drop; } + } - return nextLeastDropCharPos; + return nextLeastDropCharPos; } const vector PostProcess::getResults() @@ -426,23 +404,23 @@ void PostProcess::findAllPermutations(vector prevletters, int charPos, i if (charPos == letters.size() - 1) { - // Last letter, add the word - PPResult possibility; - possibility.letters = ""; - possibility.totalscore = 0; - possibility.matchesTemplate = false; - for (int z = 0; z < prevletters.size(); z++) - { - if (prevletters[z].letter != SKIP_CHAR) - possibility.letters = possibility.letters + prevletters[z].letter; - possibility.totalscore = possibility.totalscore + prevletters[z].totalscore; - } + // Last letter, add the word + PPResult possibility; + possibility.letters = ""; + possibility.totalscore = 0; + possibility.matchesTemplate = false; + for (int z = 0; z < prevletters.size(); z++) + { + if (prevletters[z].letter != SKIP_CHAR) + possibility.letters = possibility.letters + prevletters[z].letter; + possibility.totalscore = possibility.totalscore + prevletters[z].totalscore; + } - if (letters[charPos][i].letter != SKIP_CHAR) - possibility.letters = possibility.letters + letters[charPos][i].letter; - possibility.totalscore = possibility.totalscore +letters[charPos][i].totalscore; + if (letters[charPos][i].letter != SKIP_CHAR) + possibility.letters = possibility.letters + letters[charPos][i].letter; + possibility.totalscore = possibility.totalscore +letters[charPos][i].totalscore; - allPossibilities.push_back(possibility); + allPossibilities.push_back(possibility); } else { @@ -450,9 +428,9 @@ void PostProcess::findAllPermutations(vector prevletters, int charPos, i float scorePercentDiff = abs( letters[charPos][0].totalscore - letters[charPos][i].totalscore ) / letters[charPos][0].totalscore; if (i != 0 && letters[charPos][i].letter != SKIP_CHAR && scorePercentDiff > 0.10f ) - findAllPermutations(prevletters, charPos + 1, substitutionsLeft - 1); + findAllPermutations(prevletters, charPos + 1, substitutionsLeft - 1); else - findAllPermutations(prevletters, charPos + 1, substitutionsLeft); + findAllPermutations(prevletters, charPos + 1, substitutionsLeft); prevletters.pop_back(); } @@ -460,19 +438,15 @@ void PostProcess::findAllPermutations(vector prevletters, int charPos, i if (letters[charPos].size() == 0) { - // No letters for this char position... - // Just pass it along - findAllPermutations(prevletters, charPos + 1, substitutionsLeft); + // No letters for this char position... + // Just pass it along + findAllPermutations(prevletters, charPos + 1, substitutionsLeft); } - - } - - - -bool wordCompare( const PPResult &left, const PPResult &right ){ +bool wordCompare( const PPResult &left, const PPResult &right ) +{ if (left.totalscore < right.totalscore) return false; return true; @@ -486,7 +460,6 @@ bool letterCompare( const Letter &left, const Letter &right ) return true; } - RegexRule::RegexRule(string region, string pattern) { this->original = pattern; @@ -495,31 +468,31 @@ RegexRule::RegexRule(string region, string pattern) numchars = 0; for (int i = 0; i < pattern.size(); i++) { - if (pattern.at(i) == '[') + if (pattern.at(i) == '[') + { + while (pattern.at(i) != ']' ) { - while (pattern.at(i) != ']' ) - { - this->regex = this->regex + pattern.at(i); - i++; - } - this->regex = this->regex + ']'; + this->regex = this->regex + pattern.at(i); + i++; + } + this->regex = this->regex + ']'; - } - else if (pattern.at(i) == '?') - { - this->regex = this->regex + '.'; - this->skipPositions.push_back(numchars); - } - else if (pattern.at(i) == '@') - { - this->regex = this->regex + "\\a"; - } - else if (pattern.at(i) == '#') - { - this->regex = this->regex + "\\d"; - } + } + else if (pattern.at(i) == '?') + { + this->regex = this->regex + '.'; + this->skipPositions.push_back(numchars); + } + else if (pattern.at(i) == '@') + { + this->regex = this->regex + "\\a"; + } + else if (pattern.at(i) == '#') + { + this->regex = this->regex + "\\d"; + } - numchars++; + numchars++; } trexp.Compile(this->regex.c_str()); @@ -529,7 +502,6 @@ RegexRule::RegexRule(string region, string pattern) // cout << "AA Skip position: " << skipPositions[z] << endl; } - bool RegexRule::match(string text) { if (text.length() != numchars) @@ -544,17 +516,17 @@ string RegexRule::filterSkips(string text) for (int i = 0; i < text.size(); i++) { bool skip = false; - for (int j = 0; j < skipPositions.size(); j++) + for (int j = 0; j < skipPositions.size(); j++) + { + if (skipPositions[j] == i) { - if (skipPositions[j] == i) - { - skip = true; - break; - } + skip = true; + break; } + } - if (skip == false) - response = response + text[i]; + if (skip == false) + response = response + text[i]; } return response; diff --git a/src/openalpr/regiondetector.cpp b/src/openalpr/regiondetector.cpp index 3394d8e..3f8f061 100644 --- a/src/openalpr/regiondetector.cpp +++ b/src/openalpr/regiondetector.cpp @@ -17,39 +17,33 @@ * along with this program. If not, see . */ - - #include "regiondetector.h" - RegionDetector::RegionDetector(Config* config) { this->config = config; // Don't scale. Can change this in the future (i.e., maximum resolution preference, or some such). - this->scale_factor = 1.0f; - - // Load either the regular or OpenCL version of the cascade classifier - if (config->opencl_enabled) - { - this->plate_cascade = new ocl::OclCascadeClassifier(); - } - else - { - this->plate_cascade = new CascadeClassifier(); - } - - - if( this->plate_cascade->load( config->getCascadeRuntimeDir() + config->country + ".xml" ) ) - { - this->loaded = true; - } - else - { - this->loaded = false; - printf("--(!)Error loading classifier\n"); - } + this->scale_factor = 1.0f; + // Load either the regular or OpenCL version of the cascade classifier + if (config->opencl_enabled) + { + this->plate_cascade = new ocl::OclCascadeClassifier(); + } + else + { + this->plate_cascade = new CascadeClassifier(); + } + if( this->plate_cascade->load( config->getCascadeRuntimeDir() + config->country + ".xml" ) ) + { + this->loaded = true; + } + else + { + this->loaded = false; + printf("--(!)Error loading classifier\n"); + } } @@ -58,14 +52,11 @@ RegionDetector::~RegionDetector() delete this->plate_cascade; } - - bool RegionDetector::isLoaded() { return this->loaded; } - vector RegionDetector::detect(Mat frame) { @@ -77,7 +68,6 @@ vector RegionDetector::detect(Mat frame) return regionsOfInterest; } - /** @function detectAndDisplay */ vector RegionDetector::doCascade(Mat frame) { @@ -91,44 +81,41 @@ vector RegionDetector::doCascade(Mat frame) resize(frame, frame, Size(w * this->scale_factor, h * this->scale_factor)); //-- Detect plates - timespec startTime; - getTime(&startTime); + timespec startTime; + getTime(&startTime); - Size minSize(config->minPlateSizeWidthPx * this->scale_factor, config->minPlateSizeHeightPx * this->scale_factor); - Size maxSize(w * config->maxPlateWidthPercent * this->scale_factor, h * config->maxPlateHeightPercent * this->scale_factor); + Size minSize(config->minPlateSizeWidthPx * this->scale_factor, config->minPlateSizeHeightPx * this->scale_factor); + Size maxSize(w * config->maxPlateWidthPercent * this->scale_factor, h * config->maxPlateHeightPercent * this->scale_factor); - if (config->opencl_enabled) - { - ocl::oclMat openclFrame(frame); - ((ocl::OclCascadeClassifier*) plate_cascade)->detectMultiScale(openclFrame, plates, 1.1, 3, 0, minSize, maxSize); - } - else - { + if (config->opencl_enabled) + { + ocl::oclMat openclFrame(frame); + ((ocl::OclCascadeClassifier*) plate_cascade)->detectMultiScale(openclFrame, plates, 1.1, 3, 0, minSize, maxSize); + } + else + { - plate_cascade->detectMultiScale( frame, plates, 1.1, 3, - 0, - //0|CV_HAAR_SCALE_IMAGE, - minSize, maxSize ); - } + plate_cascade->detectMultiScale( frame, plates, 1.1, 3, + 0, + //0|CV_HAAR_SCALE_IMAGE, + minSize, maxSize ); + } + if (config->debugTiming) + { + timespec endTime; + getTime(&endTime); + cout << "LBP Time: " << diffclock(startTime, endTime) << "ms." << endl; + } - if (config->debugTiming) - { - timespec endTime; - getTime(&endTime); - cout << "LBP Time: " << diffclock(startTime, endTime) << "ms." << endl; - } + for( int i = 0; i < plates.size(); i++ ) + { + plates[i].x = plates[i].x / scale_factor; + plates[i].y = plates[i].y / scale_factor; + plates[i].width = plates[i].width / scale_factor; + plates[i].height = plates[i].height / scale_factor; + } - - - for( int i = 0; i < plates.size(); i++ ) - { - plates[i].x = plates[i].x / scale_factor; - plates[i].y = plates[i].y / scale_factor; - plates[i].width = plates[i].width / scale_factor; - plates[i].height = plates[i].height / scale_factor; - } - - return plates; + return plates; } diff --git a/src/openalpr/simpleini/snippets.cpp b/src/openalpr/simpleini/snippets.cpp index e2295fe..e1425cd 100644 --- a/src/openalpr/simpleini/snippets.cpp +++ b/src/openalpr/simpleini/snippets.cpp @@ -19,105 +19,106 @@ bool snippets( - const char * a_pszFile, - bool a_bIsUtf8, - bool a_bUseMultiKey, - bool a_bUseMultiLine - ) + const char * a_pszFile, + bool a_bIsUtf8, + bool a_bUseMultiKey, + bool a_bUseMultiLine +) { - // LOADING DATA + // LOADING DATA - // load from a data file - CSimpleIniA ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine); - SI_Error rc = ini.LoadFile(a_pszFile); - if (rc < 0) return false; + // load from a data file + CSimpleIniA ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine); + SI_Error rc = ini.LoadFile(a_pszFile); + if (rc < 0) return false; - // load from a string - std::string strData; - rc = ini.LoadData(strData.c_str(), strData.size()); - if (rc < 0) return false; + // load from a string + std::string strData; + rc = ini.LoadData(strData.c_str(), strData.size()); + if (rc < 0) return false; - // GETTING SECTIONS AND KEYS + // GETTING SECTIONS AND KEYS - // get all sections - CSimpleIniA::TNamesDepend sections; - ini.GetAllSections(sections); + // get all sections + CSimpleIniA::TNamesDepend sections; + ini.GetAllSections(sections); - // get all keys in a section - CSimpleIniA::TNamesDepend keys; - ini.GetAllKeys("section-name", keys); + // get all keys in a section + CSimpleIniA::TNamesDepend keys; + ini.GetAllKeys("section-name", keys); - // GETTING VALUES + // GETTING VALUES - // get the value of a key - const char * pszValue = ini.GetValue("section-name", - "key-name", NULL /*default*/); + // get the value of a key + const char * pszValue = ini.GetValue("section-name", + "key-name", NULL /*default*/); - // get the value of a key which may have multiple - // values. If bHasMultipleValues is true, then just - // one value has been returned - bool bHasMultipleValues; - pszValue = ini.GetValue("section-name", "key-name", - NULL /*default*/, &bHasMultipleValues); + // get the value of a key which may have multiple + // values. If bHasMultipleValues is true, then just + // one value has been returned + bool bHasMultipleValues; + pszValue = ini.GetValue("section-name", "key-name", + NULL /*default*/, &bHasMultipleValues); - // get all values of a key with multiple values - CSimpleIniA::TNamesDepend values; - ini.GetAllValues("section-name", "key-name", values); + // get all values of a key with multiple values + CSimpleIniA::TNamesDepend values; + ini.GetAllValues("section-name", "key-name", values); - // sort the values into the original load order + // sort the values into the original load order #if defined(_MSC_VER) && _MSC_VER <= 1200 - /** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */ - values.sort(); + /** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */ + values.sort(); #else - values.sort(CSimpleIniA::Entry::LoadOrder()); + values.sort(CSimpleIniA::Entry::LoadOrder()); #endif - // output all of the items - CSimpleIniA::TNamesDepend::const_iterator i; - for (i = values.begin(); i != values.end(); ++i) { - printf("key-name = '%s'\n", i->pItem); - } + // output all of the items + CSimpleIniA::TNamesDepend::const_iterator i; + for (i = values.begin(); i != values.end(); ++i) + { + printf("key-name = '%s'\n", i->pItem); + } - // MODIFYING DATA + // MODIFYING DATA - // adding a new section - rc = ini.SetValue("new-section", NULL, NULL); - if (rc < 0) return false; - printf("section: %s\n", rc == SI_INSERTED ? - "inserted" : "updated"); + // adding a new section + rc = ini.SetValue("new-section", NULL, NULL); + if (rc < 0) return false; + printf("section: %s\n", rc == SI_INSERTED ? + "inserted" : "updated"); - // adding a new key ("new-section" will be added - // automatically if it doesn't already exist. - rc = ini.SetValue("new-section", "new-key", "value"); - if (rc < 0) return false; - printf("key: %s\n", rc == SI_INSERTED ? - "inserted" : "updated"); + // adding a new key ("new-section" will be added + // automatically if it doesn't already exist. + rc = ini.SetValue("new-section", "new-key", "value"); + if (rc < 0) return false; + printf("key: %s\n", rc == SI_INSERTED ? + "inserted" : "updated"); - // changing the value of a key - rc = ini.SetValue("section", "key", "updated-value"); - if (rc < 0) return false; - printf("key: %s\n", rc == SI_INSERTED ? - "inserted" : "updated"); + // changing the value of a key + rc = ini.SetValue("section", "key", "updated-value"); + if (rc < 0) return false; + printf("key: %s\n", rc == SI_INSERTED ? + "inserted" : "updated"); - // DELETING DATA + // DELETING DATA - // deleting a key from a section. Optionally the entire - // section may be deleted if it is now empty. - ini.Delete("section-name", "key-name", - true /*delete the section if empty*/); + // deleting a key from a section. Optionally the entire + // section may be deleted if it is now empty. + ini.Delete("section-name", "key-name", + true /*delete the section if empty*/); - // deleting an entire section and all keys in it - ini.Delete("section-name", NULL); + // deleting an entire section and all keys in it + ini.Delete("section-name", NULL); - // SAVING DATA + // SAVING DATA - // save the data to a string - rc = ini.Save(strData); - if (rc < 0) return false; + // save the data to a string + rc = ini.Save(strData); + if (rc < 0) return false; - // save the data back to the file - rc = ini.SaveFile(a_pszFile); - if (rc < 0) return false; + // save the data back to the file + rc = ini.SaveFile(a_pszFile); + if (rc < 0) return false; - return true; + return true; } diff --git a/src/openalpr/stateidentifier.cpp b/src/openalpr/stateidentifier.cpp index 82e15c6..c32a536 100644 --- a/src/openalpr/stateidentifier.cpp +++ b/src/openalpr/stateidentifier.cpp @@ -19,20 +19,19 @@ #include "stateidentifier.h" - StateIdentifier::StateIdentifier(Config* config) { this->config = config; - featureMatcher = new FeatureMatcher(config); + featureMatcher = new FeatureMatcher(config); - if (featureMatcher->isLoaded() == false) - { - cout << "Can not create detector or descriptor extractor or descriptor matcher of given types" << endl; - return; - } + if (featureMatcher->isLoaded() == false) + { + cout << "Can not create detector or descriptor extractor or descriptor matcher of given types" << endl; + return; + } - featureMatcher->loadRecognitionSet(config->country); + featureMatcher->loadRecognitionSet(config->country); } StateIdentifier::~StateIdentifier() @@ -66,19 +65,16 @@ int StateIdentifier::recognize(Mat img, char* stateCode) plateImg.copyTo(debugImg); vector matchesArray(featureMatcher->numTrainingElements()); - RecognitionResult result = featureMatcher->recognize(plateImg, true, &debugImg, true, matchesArray ); if (this->config->debugStateId) { - displayImage(config, "State Identifier1", plateImg); displayImage(config, "State Identifier", debugImg); cout << result.haswinner << " : " << result.confidence << " : " << result.winner << endl; } - if (config->debugTiming) { timespec endTime; @@ -86,12 +82,10 @@ int StateIdentifier::recognize(Mat img, char* stateCode) cout << "State Identification Time: " << diffclock(startTime, endTime) << "ms." << endl; } - if (result.haswinner == false) return 0; strcpy(stateCode, result.winner.c_str()); - return result.confidence; } diff --git a/src/openalpr/support/filesystem.cpp b/src/openalpr/support/filesystem.cpp index e3cd1ee..4757d5f 100644 --- a/src/openalpr/support/filesystem.cpp +++ b/src/openalpr/support/filesystem.cpp @@ -1,43 +1,44 @@ #include "filesystem.h" - - bool hasEnding (std::string const &fullString, std::string const &ending) { - if (fullString.length() >= ending.length()) { - return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); - } else { - return false; - } + if (fullString.length() >= ending.length()) + { + return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); + } + else + { + return false; + } } bool DirectoryExists( const char* pzPath ) { - if ( pzPath == NULL) return false; + if ( pzPath == NULL) return false; - DIR *pDir; - bool bExists = false; + DIR *pDir; + bool bExists = false; - pDir = opendir (pzPath); + pDir = opendir (pzPath); - if (pDir != NULL) - { - bExists = true; - (void) closedir (pDir); - } + if (pDir != NULL) + { + bExists = true; + (void) closedir (pDir); + } - return bExists; + return bExists; } bool fileExists( const char* pzPath ) { - if (pzPath == NULL) return false; + if (pzPath == NULL) return false; - bool fExists = false; - std::ifstream f(pzPath); - fExists = f.is_open(); - f.close(); - return fExists; + bool fExists = false; + std::ifstream f(pzPath); + fExists = f.is_open(); + f.close(); + return fExists; } std::vector getFilesInDir(const char* dirPath) @@ -47,14 +48,18 @@ std::vector getFilesInDir(const char* dirPath) std::vector files; struct dirent *ent; - if ((dir = opendir (dirPath)) != NULL) { + if ((dir = opendir (dirPath)) != NULL) + { /* print all the files and directories within directory */ - while ((ent = readdir (dir)) != NULL) { + while ((ent = readdir (dir)) != NULL) + { if (strcmp(ent->d_name, ".") != 0 && strcmp(ent->d_name, "..") != 0) - files.push_back(ent->d_name); + files.push_back(ent->d_name); } closedir (dir); - } else { + } + else + { /* could not open directory */ perror (""); return files; @@ -63,14 +68,14 @@ std::vector getFilesInDir(const char* dirPath) return files; } - -bool stringCompare( const std::string &left, const std::string &right ){ - for( std::string::const_iterator lit = left.begin(), rit = right.begin(); lit != left.end() && rit != right.end(); ++lit, ++rit ) - if( tolower( *lit ) < tolower( *rit ) ) - return true; - else if( tolower( *lit ) > tolower( *rit ) ) - return false; - if( left.size() < right.size() ) +bool stringCompare( const std::string &left, const std::string &right ) +{ + for( std::string::const_iterator lit = left.begin(), rit = right.begin(); lit != left.end() && rit != right.end(); ++lit, ++rit ) + if( tolower( *lit ) < tolower( *rit ) ) return true; - return false; + else if( tolower( *lit ) > tolower( *rit ) ) + return false; + if( left.size() < right.size() ) + return true; + return false; } diff --git a/src/openalpr/support/timing.cpp b/src/openalpr/support/timing.cpp index 066af2a..dbf1c7a 100644 --- a/src/openalpr/support/timing.cpp +++ b/src/openalpr/support/timing.cpp @@ -1,143 +1,144 @@ #include "timing.h" - timespec diff(timespec start, timespec end); - #ifdef WINDOWS // Windows timing code LARGE_INTEGER getFILETIMEoffset() { - SYSTEMTIME s; - FILETIME f; - LARGE_INTEGER t; + SYSTEMTIME s; + FILETIME f; + LARGE_INTEGER t; - s.wYear = 1970; - s.wMonth = 1; - s.wDay = 1; - s.wHour = 0; - s.wMinute = 0; - s.wSecond = 0; - s.wMilliseconds = 0; - SystemTimeToFileTime(&s, &f); - t.QuadPart = f.dwHighDateTime; - t.QuadPart <<= 32; - t.QuadPart |= f.dwLowDateTime; - return (t); + s.wYear = 1970; + s.wMonth = 1; + s.wDay = 1; + s.wHour = 0; + s.wMinute = 0; + s.wSecond = 0; + s.wMilliseconds = 0; + SystemTimeToFileTime(&s, &f); + t.QuadPart = f.dwHighDateTime; + t.QuadPart <<= 32; + t.QuadPart |= f.dwLowDateTime; + return (t); } int clock_gettime(int X, timespec *tv) { - LARGE_INTEGER t; - FILETIME f; - double microseconds; - static LARGE_INTEGER offset; - static double frequencyToMicroseconds; - static int initialized = 0; - static BOOL usePerformanceCounter = 0; + LARGE_INTEGER t; + FILETIME f; + double microseconds; + static LARGE_INTEGER offset; + static double frequencyToMicroseconds; + static int initialized = 0; + static BOOL usePerformanceCounter = 0; - if (!initialized) { - LARGE_INTEGER performanceFrequency; - initialized = 1; - usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency); - if (usePerformanceCounter) { - QueryPerformanceCounter(&offset); - frequencyToMicroseconds = (double)performanceFrequency.QuadPart / 1000000.; - } else { - offset = getFILETIMEoffset(); - frequencyToMicroseconds = 10.; - } + if (!initialized) + { + LARGE_INTEGER performanceFrequency; + initialized = 1; + usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency); + if (usePerformanceCounter) + { + QueryPerformanceCounter(&offset); + frequencyToMicroseconds = (double)performanceFrequency.QuadPart / 1000000.; } - if (usePerformanceCounter) QueryPerformanceCounter(&t); - else { - GetSystemTimeAsFileTime(&f); - t.QuadPart = f.dwHighDateTime; - t.QuadPart <<= 32; - t.QuadPart |= f.dwLowDateTime; + else + { + offset = getFILETIMEoffset(); + frequencyToMicroseconds = 10.; } + } + if (usePerformanceCounter) QueryPerformanceCounter(&t); + else + { + GetSystemTimeAsFileTime(&f); + t.QuadPart = f.dwHighDateTime; + t.QuadPart <<= 32; + t.QuadPart |= f.dwLowDateTime; + } - t.QuadPart -= offset.QuadPart; - microseconds = (double)t.QuadPart / frequencyToMicroseconds; - t.QuadPart = microseconds; - tv->tv_sec = t.QuadPart / 1000000; - tv->tv_usec = t.QuadPart % 1000000; - return (0); + t.QuadPart -= offset.QuadPart; + microseconds = (double)t.QuadPart / frequencyToMicroseconds; + t.QuadPart = microseconds; + tv->tv_sec = t.QuadPart / 1000000; + tv->tv_usec = t.QuadPart % 1000000; + return (0); } - - void getTime(timespec* time) { - clock_gettime(0, time); + clock_gettime(0, time); } double diffclock(timespec time1,timespec time2) { - timespec delta = diff(time1,time2); - double milliseconds = (delta.tv_sec * 1000) + (((double) delta.tv_usec) / 1000.0); + timespec delta = diff(time1,time2); + double milliseconds = (delta.tv_sec * 1000) + (((double) delta.tv_usec) / 1000.0); - - return milliseconds; + return milliseconds; } timespec diff(timespec start, timespec end) { - timespec temp; - if ((end.tv_usec-start.tv_usec)<0) { - temp.tv_sec = end.tv_sec-start.tv_sec-1; - temp.tv_usec = 1000000+end.tv_usec-start.tv_usec; - } else { - temp.tv_sec = end.tv_sec-start.tv_sec; - temp.tv_usec = end.tv_usec-start.tv_usec; - } - return temp; + timespec temp; + if ((end.tv_usec-start.tv_usec)<0) + { + temp.tv_sec = end.tv_sec-start.tv_sec-1; + temp.tv_usec = 1000000+end.tv_usec-start.tv_usec; + } + else + { + temp.tv_sec = end.tv_sec-start.tv_sec; + temp.tv_usec = end.tv_usec-start.tv_usec; + } + return temp; } #else - - void getTime(timespec* time) { #ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time - clock_serv_t cclock; - mach_timespec_t mts; - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); - clock_get_time(cclock, &mts); - mach_port_deallocate(mach_task_self(), cclock); - time->tv_sec = mts.tv_sec; - time->tv_nsec = mts.tv_nsec; + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + time->tv_sec = mts.tv_sec; + time->tv_nsec = mts.tv_nsec; #else - clock_gettime(CLOCK_PROCESS_CPUTIME_ID, time); + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, time); #endif } double diffclock(timespec time1,timespec time2) { - timespec delta = diff(time1,time2); - double milliseconds = (delta.tv_sec * 1000) + (((double) delta.tv_nsec) / 1000000.0); - - - return milliseconds; + timespec delta = diff(time1,time2); + double milliseconds = (delta.tv_sec * 1000) + (((double) delta.tv_nsec) / 1000000.0); + return milliseconds; } timespec diff(timespec start, timespec end) { - timespec temp; - if ((end.tv_nsec-start.tv_nsec)<0) { - temp.tv_sec = end.tv_sec-start.tv_sec-1; - temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; - } else { - temp.tv_sec = end.tv_sec-start.tv_sec; - temp.tv_nsec = end.tv_nsec-start.tv_nsec; - } - return temp; + timespec temp; + if ((end.tv_nsec-start.tv_nsec)<0) + { + temp.tv_sec = end.tv_sec-start.tv_sec-1; + temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; + } + else + { + temp.tv_sec = end.tv_sec-start.tv_sec; + temp.tv_nsec = end.tv_nsec-start.tv_nsec; + } + return temp; } - #endif diff --git a/src/openalpr/utility.cpp b/src/openalpr/utility.cpp index 595e314..96b0323 100644 --- a/src/openalpr/utility.cpp +++ b/src/openalpr/utility.cpp @@ -17,12 +17,8 @@ * along with this program. If not, see . */ - - - #include "utility.h" - Rect expandRect(Rect original, int expandXPixels, int expandYPixels, int maxX, int maxY) { Rect expandedRegion = Rect(original); @@ -48,22 +44,22 @@ Rect expandRect(Rect original, int expandXPixels, int expandYPixels, int maxX, i Mat drawImageDashboard(vector images, int imageType, int numColumns) { - int numRows = ceil((float) images.size() / (float) numColumns); + int numRows = ceil((float) images.size() / (float) numColumns); - Mat dashboard(Size(images[0].cols * numColumns, images[0].rows * numRows), imageType); + Mat dashboard(Size(images[0].cols * numColumns, images[0].rows * numRows), imageType); - for (int i = 0; i < numColumns * numRows; i++) - { - if (i < images.size()) - images[i].copyTo(dashboard(Rect((i%numColumns) * images[i].cols, floor((float) i/numColumns) * images[i].rows, images[i].cols, images[i].rows))); - else - { - Mat black = Mat::zeros(images[0].size(), imageType); - black.copyTo(dashboard(Rect((i%numColumns) * images[0].cols, floor((float) i/numColumns) * images[0].rows, images[0].cols, images[0].rows))); - } - } + for (int i = 0; i < numColumns * numRows; i++) + { + if (i < images.size()) + images[i].copyTo(dashboard(Rect((i%numColumns) * images[i].cols, floor((float) i/numColumns) * images[i].rows, images[i].cols, images[i].rows))); + else + { + Mat black = Mat::zeros(images[0].size(), imageType); + black.copyTo(dashboard(Rect((i%numColumns) * images[0].cols, floor((float) i/numColumns) * images[0].rows, images[0].cols, images[0].rows))); + } + } - return dashboard; + return dashboard; } Mat addLabel(Mat input, string label) @@ -90,15 +86,13 @@ Mat addLabel(Mat input, string label) return newImage; } - - void drawAndWait(cv::Mat* frame) { cv::imshow("Temp Window", *frame); while (cv::waitKey(50) == -1) { - // loop + // loop } cv::destroyWindow("Temp Window"); @@ -137,7 +131,8 @@ vector produceThresholds(const Mat img_gray, Config* config) NiblackSauvolaWolfJolion (img_gray, thresholds[i++], WOLFJOLION, win, win, 0.05 + (k * 0.35)); bitwise_not(thresholds[i-1], thresholds[i-1]); - k = 1; win = 22; + k = 1; + win = 22; NiblackSauvolaWolfJolion (img_gray, thresholds[i++], WOLFJOLION, win, win, 0.05 + (k * 0.35)); bitwise_not(thresholds[i-1], thresholds[i-1]); //NiblackSauvolaWolfJolion (img_gray, thresholds[i++], WOLFJOLION, win, win, 0.05 + (k * 0.35)); @@ -151,11 +146,6 @@ vector produceThresholds(const Mat img_gray, Config* config) NiblackSauvolaWolfJolion (img_gray, thresholds[i++], SAUVOLA, 12, 12, 0.18 * k); bitwise_not(thresholds[i-1], thresholds[i-1]); - - - - - if (config->debugTiming) { timespec endTime; @@ -180,7 +170,6 @@ double median(int array[], int arraySize) return arraySize % 2 ? array[arraySize / 2] : (array[arraySize / 2 - 1] + array[arraySize / 2]) / 2; } - Mat equalizeBrightness(Mat img) { @@ -194,7 +183,6 @@ Mat equalizeBrightness(Mat img) normalize(img, img, 0, 255, NORM_MINMAX); img.convertTo(img, CV_8U); // convert back to unsigned int - return img; } @@ -218,18 +206,17 @@ void fillMask(Mat img, const Mat mask, Scalar color) if (m) { - for (int z = 0; z < 3; z++) - { - int prevVal = img.at(row, col)[z]; - img.at(row, col)[z] = ((int) color[z]) | prevVal; - } + for (int z = 0; z < 3; z++) + { + int prevVal = img.at(row, col)[z]; + img.at(row, col)[z] = ((int) color[z]) | prevVal; + } } } } } - void drawX(Mat img, Rect rect, Scalar color, int thickness) { Point tl(rect.x, rect.y); @@ -251,28 +238,26 @@ double distanceBetweenPoints(Point p1, Point p2) float angleBetweenPoints(Point p1, Point p2) { - int deltaY = p2.y - p1.y; - int deltaX = p2.x - p1.x; + int deltaY = p2.y - p1.y; + int deltaX = p2.x - p1.x; - return atan2((float) deltaY, (float) deltaX) * (180 / CV_PI); + return atan2((float) deltaY, (float) deltaX) * (180 / CV_PI); } - Size getSizeMaintainingAspect(Mat inputImg, int maxWidth, int maxHeight) { - float aspect = ((float) inputImg.cols) / ((float) inputImg.rows); + float aspect = ((float) inputImg.cols) / ((float) inputImg.rows); - if (maxWidth / aspect > maxHeight) - { - return Size(maxHeight * aspect, maxHeight); - } - else - { - return Size(maxWidth, maxWidth / aspect); - } + if (maxWidth / aspect > maxHeight) + { + return Size(maxHeight * aspect, maxHeight); + } + else + { + return Size(maxWidth, maxWidth / aspect); + } } - LineSegment::LineSegment() { init(0, 0, 0, 0); @@ -292,18 +277,19 @@ void LineSegment::init(int x1, int y1, int x2, int y2) this->p1 = Point(x1, y1); this->p2 = Point(x2, y2); - if (p2.x - p1.x == 0) - this->slope = 0.00000000001; - else - this->slope = (float) (p2.y - p1.y) / (float) (p2.x - p1.x); + if (p2.x - p1.x == 0) + this->slope = 0.00000000001; + else + this->slope = (float) (p2.y - p1.y) / (float) (p2.x - p1.x); - this->length = distanceBetweenPoints(p1, p2); + this->length = distanceBetweenPoints(p1, p2); - this->angle = angleBetweenPoints(p1, p2); + this->angle = angleBetweenPoints(p1, p2); } -bool LineSegment::isPointBelowLine( Point tp ){ - return ((p2.x - p1.x)*(tp.y - p1.y) - (p2.y - p1.y)*(tp.x - p1.x)) > 0; +bool LineSegment::isPointBelowLine( Point tp ) +{ + return ((p2.x - p1.x)*(tp.y - p1.y) - (p2.y - p1.y)*(tp.x - p1.x)) > 0; } float LineSegment::getPointAt(float x) @@ -328,41 +314,38 @@ Point LineSegment::closestPointOnSegmentTo(Point p) Point LineSegment::intersection(LineSegment line) { - float c1, c2; - float intersection_X = -1, intersection_Y= -1; + float c1, c2; + float intersection_X = -1, intersection_Y= -1; + c1 = p1.y - slope * p1.x; // which is same as y2 - slope * x2 - c1 = p1.y - slope * p1.x; // which is same as y2 - slope * x2 + c2 = line.p2.y - line.slope * line.p2.x; // which is same as y2 - slope * x2 - c2 = line.p2.y - line.slope * line.p2.x; // which is same as y2 - slope * x2 + if( (slope - line.slope) == 0) + { + //std::cout << "No Intersection between the lines" << endl; + } + else if (p1.x == p2.x) + { + // Line1 is vertical + return Point(p1.x, line.getPointAt(p1.x)); + } + else if (line.p1.x == line.p2.x) + { + // Line2 is vertical + return Point(line.p1.x, getPointAt(line.p1.x)); + } + else + { + intersection_X = (c2 - c1) / (slope - line.slope); + intersection_Y = slope * intersection_X + c1; - if( (slope - line.slope) == 0) - { - //std::cout << "No Intersection between the lines" << endl; - } - else if (p1.x == p2.x) - { - // Line1 is vertical - return Point(p1.x, line.getPointAt(p1.x)); - } - else if (line.p1.x == line.p2.x) - { - // Line2 is vertical - return Point(line.p1.x, getPointAt(line.p1.x)); - } - else - { - intersection_X = (c2 - c1) / (slope - line.slope); - intersection_Y = slope * intersection_X + c1; + } - } - - return Point(intersection_X, intersection_Y); + return Point(intersection_X, intersection_Y); } - - Point LineSegment::midpoint() { // Handle the case where the line is vertical @@ -381,17 +364,17 @@ Point LineSegment::midpoint() LineSegment LineSegment::getParallelLine(float distance) { - float diff_x = p2.x - p1.x; - float diff_y = p2.y - p1.y; - float angle = atan2( diff_x, diff_y); - float dist_x = distance * cos(angle); - float dist_y = -distance * sin(angle); + float diff_x = p2.x - p1.x; + float diff_y = p2.y - p1.y; + float angle = atan2( diff_x, diff_y); + float dist_x = distance * cos(angle); + float dist_y = -distance * sin(angle); - int offsetX = (int)round(dist_x); - int offsetY = (int)round(dist_y); + int offsetX = (int)round(dist_x); + int offsetY = (int)round(dist_y); - LineSegment result(p1.x + offsetX, p1.y + offsetY, - p2.x + offsetX, p2.y + offsetY); + LineSegment result(p1.x + offsetX, p1.y + offsetY, + p2.x + offsetX, p2.y + offsetY); - return result; + return result; } diff --git a/src/openalpr/verticalhistogram.cpp b/src/openalpr/verticalhistogram.cpp index ebe97b1..a125fac 100644 --- a/src/openalpr/verticalhistogram.cpp +++ b/src/openalpr/verticalhistogram.cpp @@ -23,8 +23,6 @@ VerticalHistogram::VerticalHistogram(Mat inputImage, Mat mask) { analyzeImage(inputImage, mask); - - } VerticalHistogram::~VerticalHistogram() @@ -33,42 +31,38 @@ VerticalHistogram::~VerticalHistogram() colHeights.clear(); } - void VerticalHistogram::analyzeImage(Mat inputImage, Mat mask) { - highestPeak = 0; - lowestValley = inputImage.rows; + highestPeak = 0; + lowestValley = inputImage.rows; + histoImg = Mat::zeros(inputImage.size(), CV_8U); - histoImg = Mat::zeros(inputImage.size(), CV_8U); + int columnCount; - int columnCount; + for (int col = 0; col < inputImage.cols; col++) + { + columnCount = 0; - for (int col = 0; col < inputImage.cols; col++) + for (int row = 0; row < inputImage.rows; row++) { - columnCount = 0; - - for (int row = 0; row < inputImage.rows; row++) - { - - if (inputImage.at(row, col) > 0 && mask.at(row, col) > 0) - columnCount++; - } - - - this->colHeights.push_back(columnCount); - - if (columnCount < lowestValley) - lowestValley = columnCount; - if (columnCount > highestPeak) - highestPeak = columnCount; - - - for (; columnCount > 0; columnCount--) - histoImg.at(inputImage.rows - columnCount, col) = 255; + if (inputImage.at(row, col) > 0 && mask.at(row, col) > 0) + columnCount++; } + this->colHeights.push_back(columnCount); + + if (columnCount < lowestValley) + lowestValley = columnCount; + if (columnCount > highestPeak) + highestPeak = columnCount; + + for (; columnCount > 0; columnCount--) + histoImg.at(inputImage.rows - columnCount, col) = 255; + + } + } int VerticalHistogram::getLocalMinimum(int leftX, int rightX) @@ -107,7 +101,7 @@ int VerticalHistogram::getLocalMaximum(int leftX, int rightX) int VerticalHistogram::getHeightAt(int x) { - return colHeights[x]; + return colHeights[x]; } void VerticalHistogram::findValleys() @@ -130,8 +124,7 @@ void VerticalHistogram::findValleys() if (aboveMidpoint) { if (colHeights[i] > relativePeakHeight) - relativePeakHeight = colHeights[i]; - + relativePeakHeight = colHeights[i]; prevDirection = FLAT; @@ -171,7 +164,7 @@ HistogramDirection VerticalHistogram::getHistogramDirection(int index) for (int i = index; i >= trailStartIndex; i--) { - trailingAverage += colHeights[i]; + trailingAverage += colHeights[i]; } trailingAverage = trailingAverage / ((float) (1 + index - trailStartIndex)); @@ -181,7 +174,6 @@ HistogramDirection VerticalHistogram::getHistogramDirection(int index) } forwardAverage = forwardAverage / ((float) (1 + forwardEndIndex - index)); - float diff = forwardAverage - trailingAverage; float minDiff = ((float) (highestPeak - lowestValley)) * 0.10;