mirror of
https://github.com/kerberos-io/openalpr-base.git
synced 2025-10-06 04:36:50 +08:00
Cleanup & reindent .cpp files
This commit is contained in:
244
src/main.cpp
244
src/main.cpp
@@ -17,90 +17,83 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "opencv2/highgui/highgui.hpp"
|
||||||
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include "tclap/CmdLine.h"
|
||||||
#include <stdio.h>
|
#include "support/filesystem.h"
|
||||||
|
#include "support/timing.h"
|
||||||
|
#include "alpr.h"
|
||||||
|
|
||||||
#include "opencv2/highgui/highgui.hpp"
|
const std::string MAIN_WINDOW_NAME = "ALPR main window";
|
||||||
#include "opencv2/imgproc/imgproc.hpp"
|
|
||||||
|
|
||||||
|
const bool SAVE_LAST_VIDEO_STILL = false;
|
||||||
|
const std::string LAST_VIDEO_STILL_LOCATION = "/tmp/laststill.jpg";
|
||||||
|
|
||||||
#include "tclap/CmdLine.h"
|
/** Function Headers */
|
||||||
#include "support/filesystem.h"
|
bool detectandshow(Alpr* alpr, cv::Mat frame, std::string region, bool writeJson);
|
||||||
#include "support/timing.h"
|
|
||||||
#include "alpr.h"
|
|
||||||
|
|
||||||
|
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;
|
TCLAP::UnlabeledValueArg<std::string> fileArg( "image_file", "Image containing license plates", true, "", "image_file_path" );
|
||||||
const std::string LAST_VIDEO_STILL_LOCATION = "/tmp/laststill.jpg";
|
|
||||||
|
|
||||||
|
TCLAP::ValueArg<std::string> countryCodeArg("c","country","Country code to identify (either us for USA or eu for Europe). Default=us",false, "us" ,"country_code");
|
||||||
|
TCLAP::ValueArg<int> seekToMsArg("","seek","Seek to the specied millisecond in a video file. Default=0",false, 0 ,"integer_ms");
|
||||||
|
TCLAP::ValueArg<std::string> runtimeDirArg("r","runtime_dir","Path to the OpenAlpr runtime data directory",false, "" ,"runtime_dir");
|
||||||
|
TCLAP::ValueArg<std::string> 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<int> topNArg("n","topn","Max number of possible plate numbers to return. Default=10",false, 10 ,"topN");
|
||||||
|
|
||||||
/** Function Headers */
|
TCLAP::SwitchArg jsonSwitch("j","json","Output recognition results in JSON format. Default=off", cmd, false);
|
||||||
bool detectandshow(Alpr* alpr, cv::Mat frame, std::string region, bool writeJson);
|
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 )
|
filename = fileArg.getValue();
|
||||||
{
|
|
||||||
std::string filename;
|
|
||||||
std::string runtimePath = "";
|
|
||||||
bool outputJson = false;
|
|
||||||
int seektoms = 0;
|
|
||||||
bool detectRegion = false;
|
|
||||||
std::string templateRegion;
|
|
||||||
std::string country;
|
|
||||||
int topn;
|
|
||||||
|
|
||||||
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);
|
}
|
||||||
|
catch (TCLAP::ArgException &e) // catch any exceptions
|
||||||
TCLAP::UnlabeledValueArg<std::string> fileArg( "image_file", "Image containing license plates", true, "", "image_file_path" );
|
{
|
||||||
|
std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl;
|
||||||
TCLAP::ValueArg<std::string> countryCodeArg("c","country","Country code to identify (either us for USA or eu for Europe). Default=us",false, "us" ,"country_code");
|
return 1;
|
||||||
TCLAP::ValueArg<int> seekToMsArg("","seek","Seek to the specied millisecond in a video file. Default=0",false, 0 ,"integer_ms");
|
}
|
||||||
TCLAP::ValueArg<std::string> runtimeDirArg("r","runtime_dir","Path to the OpenAlpr runtime data directory",false, "" ,"runtime_dir");
|
|
||||||
TCLAP::ValueArg<std::string> 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<int> 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;
|
|
||||||
|
|
||||||
|
cv::Mat frame;
|
||||||
|
|
||||||
Alpr alpr(country, runtimePath);
|
Alpr alpr(country, runtimePath);
|
||||||
alpr.setTopN(topn);
|
alpr.setTopN(topn);
|
||||||
@@ -125,15 +118,15 @@
|
|||||||
cv::VideoCapture cap(0);
|
cv::VideoCapture cap(0);
|
||||||
if (!cap.isOpened())
|
if (!cap.isOpened())
|
||||||
{
|
{
|
||||||
std::cout << "Error opening webcam" << std::endl;
|
std::cout << "Error opening webcam" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (cap.read(frame) == true)
|
while (cap.read(frame) == true)
|
||||||
{
|
{
|
||||||
detectandshow(&alpr, frame, "", outputJson);
|
detectandshow(&alpr, frame, "", outputJson);
|
||||||
cv::waitKey(1);
|
cv::waitKey(1);
|
||||||
framenum++;
|
framenum++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (hasEnding(filename, ".avi") || hasEnding(filename, ".mp4") || hasEnding(filename, ".webm") || hasEnding(filename, ".flv"))
|
else if (hasEnding(filename, ".avi") || hasEnding(filename, ".mp4") || hasEnding(filename, ".webm") || hasEnding(filename, ".flv"))
|
||||||
@@ -148,16 +141,16 @@
|
|||||||
|
|
||||||
while (cap.read(frame) == true)
|
while (cap.read(frame) == true)
|
||||||
{
|
{
|
||||||
if (SAVE_LAST_VIDEO_STILL == true)
|
if (SAVE_LAST_VIDEO_STILL == true)
|
||||||
{
|
{
|
||||||
cv::imwrite(LAST_VIDEO_STILL_LOCATION, frame);
|
cv::imwrite(LAST_VIDEO_STILL_LOCATION, frame);
|
||||||
}
|
}
|
||||||
std::cout << "Frame: " << framenum << std::endl;
|
std::cout << "Frame: " << framenum << std::endl;
|
||||||
|
|
||||||
detectandshow( &alpr, frame, "", outputJson);
|
detectandshow( &alpr, frame, "", outputJson);
|
||||||
//create a 1ms delay
|
//create a 1ms delay
|
||||||
cv::waitKey(1);
|
cv::waitKey(1);
|
||||||
framenum++;
|
framenum++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -179,32 +172,31 @@
|
|||||||
std::cerr << "Image file not found: " << filename << std::endl;
|
std::cerr << "Image file not found: " << filename << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (DirectoryExists(filename.c_str()))
|
else if (DirectoryExists(filename.c_str()))
|
||||||
{
|
{
|
||||||
std::vector<std::string> files = getFilesInDir(filename.c_str());
|
std::vector<std::string> 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;
|
||||||
std::string fullpath = filename + "/" + files[i];
|
frame = cv::imread( fullpath.c_str() );
|
||||||
std::cout << fullpath << std::endl;
|
if (detectandshow( &alpr, frame, "", outputJson))
|
||||||
frame = cv::imread( fullpath.c_str() );
|
{
|
||||||
if (detectandshow( &alpr, frame, "", outputJson))
|
//while ((char) cv::waitKey(50) != 'c') { }
|
||||||
{
|
}
|
||||||
//while ((char) cv::waitKey(50) != 'c') { }
|
else
|
||||||
}
|
{
|
||||||
else
|
//cv::waitKey(50);
|
||||||
{
|
}
|
||||||
//cv::waitKey(50);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -212,53 +204,45 @@
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
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<uchar> buffer;
|
||||||
{
|
cv::imencode(".bmp", frame, buffer );
|
||||||
|
|
||||||
|
timespec startTime;
|
||||||
|
getTime(&startTime);
|
||||||
|
|
||||||
std::vector<uchar> buffer;
|
std::vector<AlprResult> results = alpr->recognize(buffer);
|
||||||
cv::imencode(".bmp", frame, buffer );
|
|
||||||
|
|
||||||
|
if (writeJson)
|
||||||
timespec startTime;
|
{
|
||||||
getTime(&startTime);
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<AlprResult> results = alpr->recognize(buffer);
|
|
||||||
|
|
||||||
|
|
||||||
if (writeJson)
|
|
||||||
{
|
|
||||||
std::cout << alpr->toJson(results) << std::endl;
|
std::cout << alpr->toJson(results) << std::endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < results.size(); i++)
|
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;
|
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++)
|
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;
|
timespec endTime;
|
||||||
getTime(&endTime);
|
getTime(&endTime);
|
||||||
if (measureProcessingTime)
|
if (measureProcessingTime)
|
||||||
std::cout << "Total Time to process image: " << diffclock(startTime, endTime) << "ms." << std::endl;
|
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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@@ -17,12 +17,12 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "opencv2/highgui/highgui.hpp"
|
#include "opencv2/highgui/highgui.hpp"
|
||||||
#include "opencv2/imgproc/imgproc.hpp"
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <numeric> // std::accumulate
|
#include <numeric> // std::accumulate
|
||||||
|
|
||||||
@@ -34,8 +34,8 @@
|
|||||||
//#include "utility.h"
|
//#include "utility.h"
|
||||||
#include "support/filesystem.h"
|
#include "support/filesystem.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
// Given a directory full of lp images (named [statecode]#.png) crop out the alphanumeric characters.
|
// Given a directory full of lp images (named [statecode]#.png) crop out the alphanumeric characters.
|
||||||
// These will be used to train the OCR
|
// These will be used to train the OCR
|
||||||
@@ -44,47 +44,44 @@ void outputStats(vector<double> datapoints);
|
|||||||
|
|
||||||
int main( int argc, const char** argv )
|
int main( int argc, const char** argv )
|
||||||
{
|
{
|
||||||
string country;
|
string country;
|
||||||
string benchmarkName;
|
string benchmarkName;
|
||||||
string inDir;
|
string inDir;
|
||||||
string outDir;
|
string outDir;
|
||||||
Mat frame;
|
Mat frame;
|
||||||
|
|
||||||
|
|
||||||
//Check if user specify image to process
|
//Check if user specify image to process
|
||||||
if(argc == 5)
|
if(argc == 5)
|
||||||
{
|
{
|
||||||
country = argv[1];
|
country = argv[1];
|
||||||
benchmarkName = argv[2];
|
benchmarkName = argv[2];
|
||||||
inDir = argv[3];
|
inDir = argv[3];
|
||||||
outDir = argv[4];
|
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)
|
if (DirectoryExists(inDir.c_str()) == false)
|
||||||
{
|
{
|
||||||
printf("Input dir does not exist\n");
|
printf("Input dir does not exist\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (DirectoryExists(outDir.c_str()) == false)
|
if (DirectoryExists(outDir.c_str()) == false)
|
||||||
{
|
{
|
||||||
printf("Output dir does not exist\n");
|
printf("Output dir does not exist\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
vector<string> files = getFilesInDir(inDir.c_str());
|
vector<string> files = getFilesInDir(inDir.c_str());
|
||||||
sort( files.begin(), files.end(), stringCompare );
|
sort( files.begin(), files.end(), stringCompare );
|
||||||
|
|
||||||
|
|
||||||
if (benchmarkName.compare("segocr") == 0)
|
if (benchmarkName.compare("segocr") == 0)
|
||||||
{
|
{
|
||||||
Config* config = new Config(country);
|
Config* config = new Config(country);
|
||||||
@@ -92,55 +89,50 @@ int main( int argc, const char** argv )
|
|||||||
|
|
||||||
OCR* ocr = new OCR(config);
|
OCR* ocr = new OCR(config);
|
||||||
|
|
||||||
|
|
||||||
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];
|
string fullpath = inDir + "/" + files[i];
|
||||||
|
|
||||||
frame = imread( fullpath.c_str() );
|
frame = imread( fullpath.c_str() );
|
||||||
resize(frame, frame, Size(config->ocrImageWidthPx, config->ocrImageHeightPx));
|
resize(frame, frame, Size(config->ocrImageWidthPx, config->ocrImageHeightPx));
|
||||||
|
|
||||||
Rect plateCoords;
|
Rect plateCoords;
|
||||||
plateCoords.x = 0;
|
plateCoords.x = 0;
|
||||||
plateCoords.y = 0;
|
plateCoords.y = 0;
|
||||||
plateCoords.width = frame.cols;
|
plateCoords.width = frame.cols;
|
||||||
plateCoords.height = frame.rows;
|
plateCoords.height = frame.rows;
|
||||||
|
|
||||||
char statecode[3];
|
char statecode[3];
|
||||||
statecode[0] = files[i][0];
|
statecode[0] = files[i][0];
|
||||||
statecode[1] = files[i][1];
|
statecode[1] = files[i][1];
|
||||||
statecode[2] = '\0';
|
statecode[2] = '\0';
|
||||||
string statecodestr(statecode);
|
string statecodestr(statecode);
|
||||||
|
|
||||||
CharacterRegion charRegion(frame, config);
|
CharacterRegion charRegion(frame, config);
|
||||||
|
|
||||||
if (abs(charRegion.getTopLine().angle) > 4)
|
if (abs(charRegion.getTopLine().angle) > 4)
|
||||||
{
|
{
|
||||||
// Rotate image:
|
// Rotate image:
|
||||||
Mat rotated(frame.size(), frame.type());
|
Mat rotated(frame.size(), frame.type());
|
||||||
Mat rot_mat( 2, 3, CV_32FC1 );
|
Mat rot_mat( 2, 3, CV_32FC1 );
|
||||||
Point center = Point( frame.cols/2, frame.rows/2 );
|
Point center = Point( frame.cols/2, frame.rows/2 );
|
||||||
|
|
||||||
rot_mat = getRotationMatrix2D( center, charRegion.getTopLine().angle, 1.0 );
|
rot_mat = getRotationMatrix2D( center, charRegion.getTopLine().angle, 1.0 );
|
||||||
warpAffine( frame, rotated, rot_mat, frame.size() );
|
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);
|
imshow("Current LP", frame);
|
||||||
ocr->performOCR(charSegmenter.getThresholds(), charSegmenter.characters);
|
waitKey(5);
|
||||||
ocr->postProcessor->analyze(statecode, 25);
|
|
||||||
|
|
||||||
cout << files[i] << "," << statecode << "," << ocr->postProcessor->bestChars << endl;
|
|
||||||
|
|
||||||
|
|
||||||
imshow("Current LP", frame);
|
|
||||||
waitKey(5);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,14 +150,13 @@ int main( int argc, const char** argv )
|
|||||||
{
|
{
|
||||||
if (hasEnding(files[i], ".png"))
|
if (hasEnding(files[i], ".png"))
|
||||||
{
|
{
|
||||||
string fullpath = inDir + "/" + files[i];
|
string fullpath = inDir + "/" + files[i];
|
||||||
frame = imread( fullpath.c_str() );
|
frame = imread( fullpath.c_str() );
|
||||||
|
|
||||||
vector<Rect> regions = plateDetector.detect(frame);
|
vector<Rect> regions = plateDetector.detect(frame);
|
||||||
|
|
||||||
imshow("Current LP", frame);
|
|
||||||
waitKey(5);
|
|
||||||
|
|
||||||
|
imshow("Current LP", frame);
|
||||||
|
waitKey(5);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +164,7 @@ int main( int argc, const char** argv )
|
|||||||
}
|
}
|
||||||
else if (benchmarkName.compare("speed") == 0)
|
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 startTime;
|
||||||
timespec endTime;
|
timespec endTime;
|
||||||
@@ -197,75 +188,72 @@ int main( int argc, const char** argv )
|
|||||||
vector<double> ocrTimes;
|
vector<double> ocrTimes;
|
||||||
vector<double> postProcessTimes;
|
vector<double> postProcessTimes;
|
||||||
|
|
||||||
|
|
||||||
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"))
|
||||||
{
|
{
|
||||||
cout << "Image: " << files[i] << endl;
|
cout << "Image: " << files[i] << endl;
|
||||||
|
|
||||||
string fullpath = inDir + "/" + files[i];
|
string fullpath = inDir + "/" + files[i];
|
||||||
frame = imread( fullpath.c_str() );
|
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);
|
getTime(&startTime);
|
||||||
alpr.recognize(frame);
|
vector<Rect> regions = plateDetector.detect(frame);
|
||||||
getTime(&endTime);
|
getTime(&endTime);
|
||||||
double endToEndTime = diffclock(startTime, endTime);
|
|
||||||
cout << " -- End to End recognition time: " << endToEndTime << "ms." << endl;
|
|
||||||
endToEndTimes.push_back(endToEndTime);
|
|
||||||
|
|
||||||
getTime(&startTime);
|
double regionDetectionTime = diffclock(startTime, endTime);
|
||||||
vector<Rect> regions = plateDetector.detect(frame);
|
cout << " -- Region detection time: " << regionDetectionTime << "ms." << endl;
|
||||||
getTime(&endTime);
|
regionDetectionTimes.push_back(regionDetectionTime);
|
||||||
|
|
||||||
double regionDetectionTime = diffclock(startTime, endTime);
|
for (int z = 0; z < regions.size(); z++)
|
||||||
cout << " -- Region detection time: " << regionDetectionTime << "ms." << endl;
|
{
|
||||||
regionDetectionTimes.push_back(regionDetectionTime);
|
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);
|
||||||
{
|
LicensePlateCandidate lp(frame, regions[z], &config);
|
||||||
getTime(&startTime);
|
lp.recognize();
|
||||||
char temp[5];
|
getTime(&endTime);
|
||||||
stateIdentifier.recognize(frame, regions[z], temp);
|
double analysisTime = diffclock(startTime, endTime);
|
||||||
getTime(&endTime);
|
cout << "\tRegion " << z << ": Analysis time: " << analysisTime << "ms." << endl;
|
||||||
double stateidTime = diffclock(startTime, endTime);
|
|
||||||
cout << "\tRegion " << z << ": State ID time: " << stateidTime << "ms." << endl;
|
|
||||||
stateIdTimes.push_back(stateidTime);
|
|
||||||
|
|
||||||
getTime(&startTime);
|
if (lp.confidence > 10)
|
||||||
LicensePlateCandidate lp(frame, regions[z], &config);
|
{
|
||||||
lp.recognize();
|
lpAnalysisPositiveTimes.push_back(analysisTime);
|
||||||
getTime(&endTime);
|
|
||||||
double analysisTime = diffclock(startTime, endTime);
|
|
||||||
cout << "\tRegion " << z << ": Analysis time: " << analysisTime << "ms." << endl;
|
|
||||||
|
|
||||||
if (lp.confidence > 10)
|
getTime(&startTime);
|
||||||
{
|
ocr.performOCR(lp.charSegmenter->getThresholds(), lp.charSegmenter->characters);
|
||||||
lpAnalysisPositiveTimes.push_back(analysisTime);
|
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);
|
waitKey(5);
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +261,6 @@ int main( int argc, const char** argv )
|
|||||||
|
|
||||||
cout << endl << "---------------------" << endl;
|
cout << endl << "---------------------" << endl;
|
||||||
|
|
||||||
|
|
||||||
cout << "End to End Time Statistics:" << endl;
|
cout << "End to End Time Statistics:" << endl;
|
||||||
outputStats(endToEndTimes);
|
outputStats(endToEndTimes);
|
||||||
cout << endl;
|
cout << endl;
|
||||||
@@ -315,24 +302,23 @@ int main( int argc, const char** argv )
|
|||||||
{
|
{
|
||||||
if (hasEnding(files[i], ".png"))
|
if (hasEnding(files[i], ".png"))
|
||||||
{
|
{
|
||||||
string fullpath = inDir + "/" + files[i];
|
string fullpath = inDir + "/" + files[i];
|
||||||
frame = imread( fullpath.c_str() );
|
frame = imread( fullpath.c_str() );
|
||||||
|
|
||||||
vector<uchar> buffer;
|
vector<uchar> buffer;
|
||||||
imencode(".bmp", frame, buffer );
|
imencode(".bmp", frame, buffer );
|
||||||
|
|
||||||
vector<AlprResult> results = alpr.recognize(buffer);
|
vector<AlprResult> results = alpr.recognize(buffer);
|
||||||
|
|
||||||
outputdatafile << files[i] << ": ";
|
outputdatafile << files[i] << ": ";
|
||||||
for (int z = 0; z < results.size(); z++)
|
for (int z = 0; z < results.size(); z++)
|
||||||
{
|
{
|
||||||
outputdatafile << results[z].bestPlate.characters << ", ";
|
outputdatafile << results[z].bestPlate.characters << ", ";
|
||||||
}
|
}
|
||||||
outputdatafile << endl;
|
outputdatafile << endl;
|
||||||
|
|
||||||
imshow("Current LP", frame);
|
|
||||||
waitKey(5);
|
|
||||||
|
|
||||||
|
imshow("Current LP", frame);
|
||||||
|
waitKey(5);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,7 +336,7 @@ void outputStats(vector<double> datapoints)
|
|||||||
|
|
||||||
std::vector<double> diff(datapoints.size());
|
std::vector<double> diff(datapoints.size());
|
||||||
std::transform(datapoints.begin(), datapoints.end(), diff.begin(),
|
std::transform(datapoints.begin(), datapoints.end(), diff.begin(),
|
||||||
std::bind2nd(std::minus<double>(), mean));
|
std::bind2nd(std::minus<double>(), mean));
|
||||||
double sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0);
|
double sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0);
|
||||||
double stdev = std::sqrt(sq_sum / datapoints.size());
|
double stdev = std::sqrt(sq_sum / datapoints.size());
|
||||||
|
|
||||||
|
@@ -31,8 +31,8 @@
|
|||||||
#include "support/filesystem.h"
|
#include "support/filesystem.h"
|
||||||
#include "ocr.h"
|
#include "ocr.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
// Given a directory full of lp images (named [statecode]#.png) crop out the alphanumeric characters.
|
// Given a directory full of lp images (named [statecode]#.png) crop out the alphanumeric characters.
|
||||||
// These will be used to train the OCR
|
// These will be used to train the OCR
|
||||||
@@ -61,36 +61,34 @@ const int DASHBOARD_COLUMNS = 3;
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void showDashboard(vector<Mat> images, vector<bool> selectedImages, int selectedIndex);
|
void showDashboard(vector<Mat> images, vector<bool> selectedImages, int selectedIndex);
|
||||||
vector<char> showCharSelection(Mat image, vector<Rect> charRegions, string state);
|
vector<char> showCharSelection(Mat image, vector<Rect> charRegions, string state);
|
||||||
|
|
||||||
int main( int argc, const char** argv )
|
int main( int argc, const char** argv )
|
||||||
{
|
{
|
||||||
|
|
||||||
string inDir;
|
string inDir;
|
||||||
string outDir;
|
string outDir;
|
||||||
Mat frame;
|
Mat frame;
|
||||||
|
|
||||||
|
|
||||||
//Check if user specify image to process
|
//Check if user specify image to process
|
||||||
if(argc == 3)
|
if(argc == 3)
|
||||||
{
|
{
|
||||||
inDir = argv[1];
|
inDir = argv[1];
|
||||||
outDir = argv[2];
|
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)
|
if (DirectoryExists(outDir.c_str()) == false)
|
||||||
{
|
{
|
||||||
printf("Output dir does not exist\n");
|
printf("Output dir does not exist\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
cout << "Usage: " << endl;
|
cout << "Usage: " << endl;
|
||||||
@@ -110,172 +108,166 @@ int main( int argc, const char** argv )
|
|||||||
|
|
||||||
if (DirectoryExists(inDir.c_str()))
|
if (DirectoryExists(inDir.c_str()))
|
||||||
{
|
{
|
||||||
vector<string> files = getFilesInDir(inDir.c_str());
|
vector<string> 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;
|
||||||
string fullpath = inDir + "/" + files[i];
|
frame = imread( fullpath.c_str() );
|
||||||
cout << fullpath << endl;
|
resize(frame, frame, Size(config->ocrImageWidthPx, config->ocrImageHeightPx));
|
||||||
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];
|
CharacterRegion regionizer(frame, config);
|
||||||
statecode[0] = files[i][0];
|
|
||||||
statecode[1] = files[i][1];
|
|
||||||
statecode[2] = '\0';
|
|
||||||
string statecodestr(statecode);
|
|
||||||
|
|
||||||
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)
|
rot_mat = getRotationMatrix2D( center, regionizer.getTopLine().angle, 1.0 );
|
||||||
{
|
warpAffine( frame, rotated, rot_mat, frame.size() );
|
||||||
// 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 );
|
rotated.copyTo(frame);
|
||||||
warpAffine( frame, rotated, rot_mat, frame.size() );
|
}
|
||||||
|
|
||||||
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);
|
vector<bool> selectedBoxes(charSegmenter.getThresholds().size());
|
||||||
ocr.postProcessor->analyze(statecodestr, 25);
|
for (int z = 0; z < charSegmenter.getThresholds().size(); z++)
|
||||||
cout << "OCR results: " << ocr.postProcessor->bestChars << endl;
|
selectedBoxes[z] = false;
|
||||||
|
|
||||||
|
int curDashboardSelection = 0;
|
||||||
|
|
||||||
vector<bool> selectedBoxes(charSegmenter.getThresholds().size());
|
vector<char> humanInputs(charSegmenter.characters.size());
|
||||||
for (int z = 0; z < charSegmenter.getThresholds().size(); z++)
|
|
||||||
selectedBoxes[z] = false;
|
|
||||||
|
|
||||||
int curDashboardSelection = 0;
|
for (int z = 0; z < charSegmenter.characters.size(); z++)
|
||||||
|
humanInputs[z] = ' ';
|
||||||
|
|
||||||
vector<char> humanInputs(charSegmenter.characters.size());
|
showDashboard(charSegmenter.getThresholds(), selectedBoxes, 0);
|
||||||
|
|
||||||
for (int z = 0; z < charSegmenter.characters.size(); z++)
|
char waitkey = (char) waitKey(50);
|
||||||
humanInputs[z] = ' ';
|
|
||||||
|
|
||||||
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<char> 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
|
waitkey = (char) waitKey(50);
|
||||||
{
|
|
||||||
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<char> 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 (waitkey == 'p')
|
||||||
{
|
i = i - 2;
|
||||||
if (selectedBoxes[t] == false)
|
if (i < -1)
|
||||||
continue;
|
i = -1;
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void showDashboard(vector<Mat> images, vector<bool> selectedImages, int selectedIndex)
|
void showDashboard(vector<Mat> images, vector<bool> selectedImages, int selectedIndex)
|
||||||
{
|
{
|
||||||
vector<Mat> vecCopy;
|
vector<Mat> vecCopy;
|
||||||
@@ -309,62 +301,60 @@ void showDashboard(vector<Mat> images, vector<bool> selectedImages, int selected
|
|||||||
|
|
||||||
vector<char> showCharSelection(Mat image, vector<Rect> charRegions, string state)
|
vector<char> showCharSelection(Mat image, vector<Rect> charRegions, string state)
|
||||||
{
|
{
|
||||||
int curCharIdx = 0;
|
int curCharIdx = 0;
|
||||||
|
|
||||||
vector<char> humanInputs(charRegions.size());
|
vector<char> humanInputs(charRegions.size());
|
||||||
for (int i = 0; i < charRegions.size(); i++)
|
for (int i = 0; i < charRegions.size(); i++)
|
||||||
humanInputs[i] = (char) SPACE_KEY;
|
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);
|
rectangle(imgCopy, charRegions[curCharIdx], Scalar(0, 255, 0), 1);
|
||||||
while (waitkey != ENTER_KEY && waitkey != ESCAPE_KEY)
|
|
||||||
|
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());
|
// Save the character to disk
|
||||||
image.copyTo(imgCopy);
|
humanInputs[curCharIdx] = toupper((char) waitkey);
|
||||||
cvtColor(imgCopy, imgCopy, CV_GRAY2BGR);
|
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())
|
if (curCharIdx >= charRegions.size())
|
||||||
curCharIdx = charRegions.size() -1;
|
{
|
||||||
|
waitkey = (char) ENTER_KEY;
|
||||||
waitkey = (char) waitKey(50);
|
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
|
if (humanInputs[i] != (char) SPACE_KEY)
|
||||||
for (int i = 0; i < charRegions.size(); i++)
|
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;
|
||||||
}
|
}
|
||||||
|
@@ -17,49 +17,46 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "opencv2/highgui/highgui.hpp"
|
#include "opencv2/highgui/highgui.hpp"
|
||||||
#include "opencv2/imgproc/imgproc.hpp"
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include "support/filesystem.h"
|
#include "support/filesystem.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Takes a directory full of single char images, and plops them on a big tif files
|
// 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
|
// Also creates a box file so Tesseract can recognize it
|
||||||
int main( int argc, const char** argv )
|
int main( int argc, const char** argv )
|
||||||
{
|
{
|
||||||
|
|
||||||
string inDir;
|
string inDir;
|
||||||
|
|
||||||
|
|
||||||
//Check if user specify image to process
|
//Check if user specify image to process
|
||||||
if(argc == 2)
|
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)
|
if (DirectoryExists(inDir.c_str()) == false)
|
||||||
{
|
{
|
||||||
printf("Output dir does not exist\n");
|
printf("Output dir does not exist\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
cout << "Usage: " << endl;
|
cout << "Usage: " << endl;
|
||||||
cout << "\tinputdir -- input dir for benchmark data" << endl;
|
cout << "\tinputdir -- input dir for benchmark data" << endl;
|
||||||
|
|
||||||
|
|
||||||
if (DirectoryExists(inDir.c_str()))
|
if (DirectoryExists(inDir.c_str()))
|
||||||
{
|
{
|
||||||
const int X_OFFSET = 10;
|
const int X_OFFSET = 10;
|
||||||
@@ -74,97 +71,92 @@ int main( int argc, const char** argv )
|
|||||||
const int TILE_HEIGHT = 70;
|
const int TILE_HEIGHT = 70;
|
||||||
const int CHAR_VERT_OFFSET = 48;
|
const int CHAR_VERT_OFFSET = 48;
|
||||||
|
|
||||||
vector<string> files = getFilesInDir(inDir.c_str());
|
vector<string> 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);
|
Mat bigTif = Mat::zeros(Size(HORIZONTAL_RESOLUTION, vertical_resolution), CV_8U);
|
||||||
int lines = files.size() / (tiles_per_row);
|
bitwise_not(bigTif, bigTif);
|
||||||
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);
|
stringstream boxFileOut;
|
||||||
bitwise_not(bigTif, bigTif);
|
|
||||||
|
|
||||||
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;
|
string fullpath = inDir + "/" + files[i];
|
||||||
int line = i / tiles_per_row;
|
|
||||||
|
|
||||||
int xPos = (col * TILE_WIDTH) + PAGE_MARGIN_X;
|
cout << "Processing file: " << (i + 1) << " of " << files.size() << endl;
|
||||||
int yPos = (line * TILE_HEIGHT) + PAGE_MARGIN_Y;
|
|
||||||
|
|
||||||
if (hasEnding(files[i], ".png") || hasEnding(files[i], ".jpg"))
|
char charcode = files[i][0];
|
||||||
{
|
|
||||||
string fullpath = inDir + "/" + files[i];
|
|
||||||
|
|
||||||
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);
|
vector<vector<Point> > contours;
|
||||||
Mat charImgCopy = Mat::zeros(Size(150, 150), characterImg.type());
|
|
||||||
bitwise_not(charImgCopy, charImgCopy);
|
|
||||||
|
|
||||||
characterImg.copyTo(charImgCopy(Rect(X_OFFSET, Y_OFFSET, characterImg.cols, characterImg.rows)));
|
//imshow("copy", charImgCopy);
|
||||||
cvtColor(charImgCopy, charImgCopy, CV_BGR2GRAY);
|
findContours(charImgCopy, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
|
||||||
bitwise_not(charImgCopy, charImgCopy);
|
|
||||||
|
|
||||||
vector<vector<Point> > 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);
|
//cout << tallestRect.x << ":" << tallestRect.y << " -- " << tallestRect.width << ":" << tallestRect.height << endl;
|
||||||
findContours(charImgCopy, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
|
|
||||||
|
|
||||||
Rect tallestRect(0, 0, 0, 0);
|
Rect cropRect(0, tallestRect.y - Y_OFFSET, tallestRect.width, tallestRect.height);
|
||||||
for (int c = 0; c < contours.size(); c++)
|
|
||||||
{
|
|
||||||
Rect tmpRect = boundingRect(contours[c]);
|
|
||||||
if (tmpRect.height > tallestRect.height)
|
|
||||||
tallestRect = tmpRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
//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;
|
cropped.copyTo(bigTif(destinationRect));
|
||||||
Mat cropped(characterImg, cropRect);
|
|
||||||
cvtColor(cropped, cropped, CV_BGR2GRAY);
|
|
||||||
|
|
||||||
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));
|
waitKey(2);
|
||||||
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
imwrite("combined.tif", bigTif);
|
imwrite("combined.tif", bigTif);
|
||||||
ofstream boxFile("combined.box", std::ios::out);
|
ofstream boxFile("combined.box", std::ios::out);
|
||||||
boxFile << boxFileOut.str();
|
boxFile << boxFileOut.str();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -17,11 +17,11 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "opencv2/highgui/highgui.hpp"
|
#include "opencv2/highgui/highgui.hpp"
|
||||||
#include "opencv2/imgproc/imgproc.hpp"
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "regiondetector.h"
|
#include "regiondetector.h"
|
||||||
@@ -30,34 +30,34 @@
|
|||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
#include "support/filesystem.h"
|
#include "support/filesystem.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
// Given a directory full of pre-cropped images, identify the state that each image belongs to.
|
// 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.
|
// 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 )
|
int main( int argc, const char** argv )
|
||||||
{
|
{
|
||||||
|
|
||||||
string inDir;
|
string inDir;
|
||||||
string outDir;
|
string outDir;
|
||||||
Mat frame;
|
Mat frame;
|
||||||
|
|
||||||
|
|
||||||
//Check if user specify image to process
|
//Check if user specify image to process
|
||||||
if(argc == 3 )
|
if(argc == 3 )
|
||||||
{
|
{
|
||||||
inDir = argv[1];
|
inDir = argv[1];
|
||||||
outDir = argv[2];
|
outDir = argv[2];
|
||||||
outDir = outDir + "/";
|
outDir = outDir + "/";
|
||||||
|
|
||||||
|
}
|
||||||
}else{
|
else
|
||||||
printf("Use:\n\t%s directory \n",argv[0]);
|
{
|
||||||
printf("Ex: \n\t%s ./pics/ \n",argv[0]);
|
printf("Use:\n\t%s directory \n",argv[0]);
|
||||||
return 0;
|
printf("Ex: \n\t%s ./pics/ \n",argv[0]);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Config config("us");
|
Config config("us");
|
||||||
@@ -65,53 +65,52 @@ int main( int argc, const char** argv )
|
|||||||
|
|
||||||
if (DirectoryExists(outDir.c_str()) == false)
|
if (DirectoryExists(outDir.c_str()) == false)
|
||||||
{
|
{
|
||||||
printf("Output dir does not exist\n");
|
printf("Output dir does not exist\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DirectoryExists(inDir.c_str()))
|
if (DirectoryExists(inDir.c_str()))
|
||||||
{
|
{
|
||||||
vector<string> files = getFilesInDir(inDir.c_str());
|
vector<string> 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;
|
||||||
string fullpath = inDir + "/" + files[i];
|
frame = imread( fullpath.c_str() );
|
||||||
cout << fullpath << endl;
|
|
||||||
frame = imread( fullpath.c_str() );
|
|
||||||
|
|
||||||
char code[4];
|
char code[4];
|
||||||
int confidence = identifier.recognize(frame, code);
|
int confidence = identifier.recognize(frame, code);
|
||||||
|
|
||||||
if (confidence <= 20)
|
if (confidence <= 20)
|
||||||
{
|
{
|
||||||
code[0] = 'z';
|
code[0] = 'z';
|
||||||
code[1] = 'z';
|
code[1] = 'z';
|
||||||
confidence = 100;
|
confidence = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
//imshow("Plate", frame);
|
//imshow("Plate", frame);
|
||||||
if (confidence > 20)
|
if (confidence > 20)
|
||||||
{
|
{
|
||||||
cout << confidence << " : " << code;
|
cout << confidence << " : " << code;
|
||||||
|
|
||||||
ostringstream convert; // stream used for the conversion
|
ostringstream convert; // stream used for the conversion
|
||||||
convert << i; // insert the textual representation of 'Number' in the characters in the stream
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
@@ -20,9 +20,6 @@
|
|||||||
#include "alpr.h"
|
#include "alpr.h"
|
||||||
#include "alpr_impl.h"
|
#include "alpr_impl.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ALPR code
|
// ALPR code
|
||||||
|
|
||||||
Alpr::Alpr(const std::string country, const std::string runtimeDir)
|
Alpr::Alpr(const std::string country, const std::string runtimeDir)
|
||||||
@@ -40,22 +37,19 @@ std::vector<AlprResult> Alpr::recognize(std::string filepath)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<AlprResult> Alpr::recognize(std::vector<unsigned char> imageBuffer)
|
std::vector<AlprResult> Alpr::recognize(std::vector<unsigned char> imageBuffer)
|
||||||
{
|
{
|
||||||
// Not sure if this actually works
|
// Not sure if this actually works
|
||||||
cv::Mat img = cv::imdecode(Mat(imageBuffer), 1);
|
cv::Mat img = cv::imdecode(Mat(imageBuffer), 1);
|
||||||
|
|
||||||
return impl->recognize(img);
|
return impl->recognize(img);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string Alpr::toJson(const vector< AlprResult > results)
|
string Alpr::toJson(const vector< AlprResult > results)
|
||||||
{
|
{
|
||||||
return impl->toJson(results);
|
return impl->toJson(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Alpr::setDetectRegion(bool detectRegion)
|
void Alpr::setDetectRegion(bool detectRegion)
|
||||||
{
|
{
|
||||||
impl->setDetectRegion(detectRegion);
|
impl->setDetectRegion(detectRegion);
|
||||||
@@ -74,11 +68,8 @@ bool Alpr::isLoaded()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Results code
|
// Results code
|
||||||
|
|
||||||
|
|
||||||
AlprResult::AlprResult()
|
AlprResult::AlprResult()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
#include "alpr_impl.h"
|
#include "alpr_impl.h"
|
||||||
|
|
||||||
|
|
||||||
AlprImpl::AlprImpl(const std::string country, const std::string runtimeDir)
|
AlprImpl::AlprImpl(const std::string country, const std::string runtimeDir)
|
||||||
{
|
{
|
||||||
config = new Config(country, 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++)
|
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;
|
cv::ocl::DevicesInfo devices;
|
||||||
@@ -68,7 +67,6 @@ AlprImpl::~AlprImpl()
|
|||||||
delete ocr;
|
delete ocr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<AlprResult> AlprImpl::recognize(cv::Mat img)
|
std::vector<AlprResult> AlprImpl::recognize(cv::Mat img)
|
||||||
{
|
{
|
||||||
timespec startTime;
|
timespec startTime;
|
||||||
@@ -76,100 +74,95 @@ std::vector<AlprResult> AlprImpl::recognize(cv::Mat img)
|
|||||||
|
|
||||||
vector<AlprResult> response;
|
vector<AlprResult> response;
|
||||||
|
|
||||||
|
|
||||||
vector<Rect> plateRegions = plateDetector->detect(img);
|
vector<Rect> plateRegions = plateDetector->detect(img);
|
||||||
|
|
||||||
|
|
||||||
// Recognize.
|
// Recognize.
|
||||||
|
|
||||||
for (int i = 0; i < plateRegions.size(); i++)
|
for (int i = 0; i < plateRegions.size(); i++)
|
||||||
{
|
{
|
||||||
timespec platestarttime;
|
timespec platestarttime;
|
||||||
getTime(&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.plate_points[pointidx].x = (int) lp.plateCorners[pointidx].x;
|
||||||
plateResult.region = defaultRegion;
|
plateResult.plate_points[pointidx].y = (int) lp.plateCorners[pointidx].y;
|
||||||
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<PPResult> 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 (detectRegion)
|
||||||
{
|
{
|
||||||
if (config->debugGeneral)
|
char statecode[4];
|
||||||
rectangle(img, plateRegions[i], Scalar(0, 0, 255), 2);
|
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<PPResult> 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)
|
if (config->debugTiming)
|
||||||
@@ -210,8 +203,6 @@ string AlprImpl::toJson(const vector< AlprResult > results)
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cJSON* AlprImpl::createJsonObj(const AlprResult* result)
|
cJSON* AlprImpl::createJsonObj(const AlprResult* result)
|
||||||
{
|
{
|
||||||
cJSON *root, *coords, *candidates;
|
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_AddNumberToObject(root,"processing_time_ms", result->processing_time_ms);
|
||||||
|
|
||||||
cJSON_AddItemToObject(root, "coordinates", coords=cJSON_CreateArray());
|
cJSON_AddItemToObject(root, "coordinates", coords=cJSON_CreateArray());
|
||||||
for (int i=0;i<4;i++)
|
for (int i=0; i<4; i++)
|
||||||
{
|
{
|
||||||
cJSON *coords_object;
|
cJSON *coords_object;
|
||||||
coords_object = cJSON_CreateObject();
|
coords_object = cJSON_CreateObject();
|
||||||
@@ -238,7 +229,6 @@ cJSON* AlprImpl::createJsonObj(const AlprResult* result)
|
|||||||
cJSON_AddItemToArray(coords, coords_object);
|
cJSON_AddItemToArray(coords, coords_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
cJSON_AddItemToObject(root, "candidates", candidates=cJSON_CreateArray());
|
cJSON_AddItemToObject(root, "candidates", candidates=cJSON_CreateArray());
|
||||||
for (int i = 0; i < result->topNPlates.size(); i++)
|
for (int i = 0; i < result->topNPlates.size(); i++)
|
||||||
{
|
{
|
||||||
@@ -254,7 +244,6 @@ cJSON* AlprImpl::createJsonObj(const AlprResult* result)
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AlprImpl::setDetectRegion(bool detectRegion)
|
void AlprImpl::setDetectRegion(bool detectRegion)
|
||||||
{
|
{
|
||||||
this->detectRegion = detectRegion;
|
this->detectRegion = detectRegion;
|
||||||
|
@@ -33,190 +33,192 @@
|
|||||||
|
|
||||||
#include "binarize_wolf.h"
|
#include "binarize_wolf.h"
|
||||||
|
|
||||||
|
|
||||||
// *************************************************************
|
// *************************************************************
|
||||||
// glide a window across the image and
|
// glide a window across the image and
|
||||||
// create two maps: mean and standard deviation.
|
// 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;
|
max_s = 0;
|
||||||
long sum, sum_sq;
|
for (int j = y_firstth ; j<=y_lastth; j++)
|
||||||
uchar foo;
|
{
|
||||||
int wxh = winx/2;
|
// Calculate the initial window at the beginning of the line
|
||||||
int wyh = winy/2;
|
sum = sum_sq = 0;
|
||||||
int x_firstth= wxh;
|
for (int wy=0 ; wy<winy; wy++)
|
||||||
int y_lastth = im.rows-wyh-1;
|
for (int wx=0 ; wx<winx; wx++)
|
||||||
int y_firstth= wyh;
|
{
|
||||||
float winarea = winx*winy;
|
foo = im.uget(wx,j-wyh+wy);
|
||||||
|
sum += foo;
|
||||||
|
sum_sq += foo*foo;
|
||||||
|
}
|
||||||
|
m = ((float)sum) / winarea;
|
||||||
|
s = sqrt ((((float)sum_sq) - ((float)(sum*sum))/winarea)/winarea);
|
||||||
|
if (s > 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;
|
// Remove the left old column and add the right new column
|
||||||
for (int j = y_firstth ; j<=y_lastth; j++)
|
for (int wy=0; wy<winy; ++wy)
|
||||||
{
|
{
|
||||||
// Calculate the initial window at the beginning of the line
|
foo = im.uget(i-1,j-wyh+wy);
|
||||||
sum = sum_sq = 0;
|
sum -= foo;
|
||||||
for (int wy=0 ; wy<winy; wy++)
|
sum_sq -= foo*foo;
|
||||||
for (int wx=0 ; wx<winx; wx++) {
|
foo = im.uget(i+winx-1,j-wyh+wy);
|
||||||
foo = im.uget(wx,j-wyh+wy);
|
sum += foo;
|
||||||
sum += foo;
|
sum_sq += foo*foo;
|
||||||
sum_sq += foo*foo;
|
}
|
||||||
}
|
m = ((float)sum) / winarea;
|
||||||
m = ((float)sum) / winarea;
|
s = sqrt ((((float)sum_sq) - ((float) (sum*sum))/winarea)/winarea);
|
||||||
s = sqrt ((((float)sum_sq) - ((float)(sum*sum))/winarea)/winarea);
|
if (s > max_s)
|
||||||
if (s > max_s)
|
max_s = s;
|
||||||
max_s = s;
|
map_m.fset(i+wxh, j, m);
|
||||||
map_m.fset(x_firstth, j, m);
|
map_s.fset(i+wxh, j, s);
|
||||||
map_s.fset(x_firstth, j, s);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Shift the window, add and remove new/old values to the histogram
|
return max_s;
|
||||||
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<winy; ++wy) {
|
|
||||||
foo = im.uget(i-1,j-wyh+wy);
|
|
||||||
sum -= foo;
|
|
||||||
sum_sq -= foo*foo;
|
|
||||||
foo = im.uget(i+winx-1,j-wyh+wy);
|
|
||||||
sum += foo;
|
|
||||||
sum_sq += foo*foo;
|
|
||||||
}
|
|
||||||
m = ((float)sum) / winarea;
|
|
||||||
s = sqrt ((((float)sum_sq) - ((float) (sum*sum))/winarea)/winarea);
|
|
||||||
if (s > max_s)
|
|
||||||
max_s = s;
|
|
||||||
map_m.fset(i+wxh, j, m);
|
|
||||||
map_s.fset(i+wxh, j, s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return max_s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************
|
/**********************************************************
|
||||||
* The binarization routine
|
* The binarization routine
|
||||||
**********************************************************/
|
**********************************************************/
|
||||||
|
|
||||||
|
|
||||||
void NiblackSauvolaWolfJolion (Mat im, Mat output, NiblackVersion version,
|
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 m, s, max_s;
|
||||||
float th=0;
|
float th=0;
|
||||||
double min_I, max_I;
|
double min_I, max_I;
|
||||||
int wxh = winx/2;
|
int wxh = winx/2;
|
||||||
int wyh = winy/2;
|
int wyh = winy/2;
|
||||||
int x_firstth= wxh;
|
int x_firstth= wxh;
|
||||||
int x_lastth = im.cols-wxh-1;
|
int x_lastth = im.cols-wxh-1;
|
||||||
int y_lastth = im.rows-wyh-1;
|
int y_lastth = im.rows-wyh-1;
|
||||||
int y_firstth= wyh;
|
int y_firstth= wyh;
|
||||||
int mx, my;
|
int mx, my;
|
||||||
|
|
||||||
// Create local statistics and store them in a float matrices
|
// Create local statistics and store them in a float matrices
|
||||||
Mat map_m = Mat::zeros (im.rows, im.cols, CV_32F);
|
Mat map_m = Mat::zeros (im.rows, im.cols, CV_32F);
|
||||||
Mat map_s = 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);
|
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:
|
// NORMAL, NON-BORDER AREA IN THE MIDDLE OF THE WINDOW:
|
||||||
for (int i=0 ; i <= im.cols-winx; i++) {
|
for (int i=0 ; i <= im.cols-winx; i++)
|
||||||
|
{
|
||||||
|
|
||||||
m = map_m.fget(i+wxh, j);
|
m = map_m.fget(i+wxh, j);
|
||||||
s = map_s.fget(i+wxh, j);
|
s = map_s.fget(i+wxh, j);
|
||||||
|
|
||||||
// Calculate the threshold
|
// Calculate the threshold
|
||||||
switch (version) {
|
switch (version)
|
||||||
|
{
|
||||||
|
|
||||||
case NIBLACK:
|
case NIBLACK:
|
||||||
th = m + k*s;
|
th = m + k*s;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SAUVOLA:
|
case SAUVOLA:
|
||||||
th = m * (1 + k*(s/dR-1));
|
th = m * (1 + k*(s/dR-1));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WOLFJOLION:
|
case WOLFJOLION:
|
||||||
th = m + k * (s/max_s-1) * (m-min_I);
|
th = m + k * (s/max_s-1) * (m-min_I);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
cerr << "Unknown threshold type in ImageThresholder::surfaceNiblackImproved()\n";
|
cerr << "Unknown threshold type in ImageThresholder::surfaceNiblackImproved()\n";
|
||||||
exit (1);
|
exit (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
thsurf.fset(i+wxh,j,th);
|
thsurf.fset(i+wxh,j,th);
|
||||||
|
|
||||||
if (i==0) {
|
if (i==0)
|
||||||
// LEFT BORDER
|
{
|
||||||
for (int i=0; i<=x_firstth; ++i)
|
// LEFT BORDER
|
||||||
thsurf.fset(i,j,th);
|
for (int i=0; i<=x_firstth; ++i)
|
||||||
|
thsurf.fset(i,j,th);
|
||||||
|
|
||||||
// LEFT-UPPER CORNER
|
// LEFT-UPPER CORNER
|
||||||
if (j==y_firstth)
|
if (j==y_firstth)
|
||||||
for (int u=0; u<y_firstth; ++u)
|
for (int u=0; u<y_firstth; ++u)
|
||||||
for (int i=0; i<=x_firstth; ++i)
|
for (int i=0; i<=x_firstth; ++i)
|
||||||
thsurf.fset(i,u,th);
|
thsurf.fset(i,u,th);
|
||||||
|
|
||||||
// LEFT-LOWER CORNER
|
// LEFT-LOWER CORNER
|
||||||
if (j==y_lastth)
|
if (j==y_lastth)
|
||||||
for (int u=y_lastth+1; u<im.rows; ++u)
|
for (int u=y_lastth+1; u<im.rows; ++u)
|
||||||
for (int i=0; i<=x_firstth; ++i)
|
for (int i=0; i<=x_firstth; ++i)
|
||||||
thsurf.fset(i,u,th);
|
thsurf.fset(i,u,th);
|
||||||
}
|
}
|
||||||
|
|
||||||
// UPPER BORDER
|
// UPPER BORDER
|
||||||
if (j==y_firstth)
|
if (j==y_firstth)
|
||||||
for (int u=0; u<y_firstth; ++u)
|
for (int u=0; u<y_firstth; ++u)
|
||||||
thsurf.fset(i+wxh,u,th);
|
thsurf.fset(i+wxh,u,th);
|
||||||
|
|
||||||
// LOWER BORDER
|
// LOWER BORDER
|
||||||
if (j==y_lastth)
|
if (j==y_lastth)
|
||||||
for (int u=y_lastth+1; u<im.rows; ++u)
|
for (int u=y_lastth+1; u<im.rows; ++u)
|
||||||
thsurf.fset(i+wxh,u,th);
|
thsurf.fset(i+wxh,u,th);
|
||||||
}
|
}
|
||||||
|
|
||||||
// RIGHT BORDER
|
// RIGHT BORDER
|
||||||
for (int i=x_lastth; i<im.cols; ++i)
|
for (int i=x_lastth; i<im.cols; ++i)
|
||||||
thsurf.fset(i,j,th);
|
thsurf.fset(i,j,th);
|
||||||
|
|
||||||
// RIGHT-UPPER CORNER
|
// RIGHT-UPPER CORNER
|
||||||
if (j==y_firstth)
|
if (j==y_firstth)
|
||||||
for (int u=0; u<y_firstth; ++u)
|
for (int u=0; u<y_firstth; ++u)
|
||||||
for (int i=x_lastth; i<im.cols; ++i)
|
for (int i=x_lastth; i<im.cols; ++i)
|
||||||
thsurf.fset(i,u,th);
|
thsurf.fset(i,u,th);
|
||||||
|
|
||||||
// RIGHT-LOWER CORNER
|
// RIGHT-LOWER CORNER
|
||||||
if (j==y_lastth)
|
if (j==y_lastth)
|
||||||
for (int u=y_lastth+1; u<im.rows; ++u)
|
for (int u=y_lastth+1; u<im.rows; ++u)
|
||||||
for (int i=x_lastth; i<im.cols; ++i)
|
for (int i=x_lastth; i<im.cols; ++i)
|
||||||
thsurf.fset(i,u,th);
|
thsurf.fset(i,u,th);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int y=0; y<im.rows; ++y)
|
||||||
|
for (int x=0; x<im.cols; ++x)
|
||||||
for (int y=0; y<im.rows; ++y)
|
{
|
||||||
for (int x=0; x<im.cols; ++x)
|
if (im.uget(x,y) >= thsurf.fget(x,y))
|
||||||
{
|
{
|
||||||
if (im.uget(x,y) >= thsurf.fget(x,y))
|
output.uset(x,y,255);
|
||||||
{
|
}
|
||||||
output.uset(x,y,255);
|
else
|
||||||
}
|
{
|
||||||
else
|
output.uset(x,y,0);
|
||||||
{
|
}
|
||||||
output.uset(x,y,0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -28,19 +28,15 @@ CharacterRegion::CharacterRegion(Mat img, Config* config)
|
|||||||
|
|
||||||
this->confidence = 0;
|
this->confidence = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (this->debug)
|
if (this->debug)
|
||||||
cout << "Starting CharacterRegion identification" << endl;
|
cout << "Starting CharacterRegion identification" << endl;
|
||||||
|
|
||||||
timespec startTime;
|
timespec startTime;
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
|
|
||||||
|
|
||||||
charAnalysis = new CharacterAnalysis(img, config);
|
charAnalysis = new CharacterAnalysis(img, config);
|
||||||
charAnalysis->analyze();
|
charAnalysis->analyze();
|
||||||
|
|
||||||
|
|
||||||
if (this->debug)
|
if (this->debug)
|
||||||
{
|
{
|
||||||
vector<Mat> tempDash;
|
vector<Mat> tempDash;
|
||||||
@@ -53,7 +49,6 @@ CharacterRegion::CharacterRegion(Mat img, Config* config)
|
|||||||
tempDash.push_back(tmp);
|
tempDash.push_back(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Mat bestVal(charAnalysis->bestThreshold.size(), charAnalysis->bestThreshold.type());
|
Mat bestVal(charAnalysis->bestThreshold.size(), charAnalysis->bestThreshold.type());
|
||||||
charAnalysis->bestThreshold.copyTo(bestVal);
|
charAnalysis->bestThreshold.copyTo(bestVal);
|
||||||
cvtColor(bestVal, bestVal, CV_GRAY2BGR);
|
cvtColor(bestVal, bestVal, CV_GRAY2BGR);
|
||||||
@@ -62,18 +57,16 @@ CharacterRegion::CharacterRegion(Mat img, Config* config)
|
|||||||
{
|
{
|
||||||
Scalar dcolor(255,0,0);
|
Scalar dcolor(255,0,0);
|
||||||
if (charAnalysis->bestCharSegments[z])
|
if (charAnalysis->bestCharSegments[z])
|
||||||
dcolor = Scalar(0,255,0);
|
dcolor = Scalar(0,255,0);
|
||||||
drawContours(bestVal, charAnalysis->bestContours, z, dcolor, 1);
|
drawContours(bestVal, charAnalysis->bestContours, z, dcolor, 1);
|
||||||
}
|
}
|
||||||
tempDash.push_back(bestVal);
|
tempDash.push_back(bestVal);
|
||||||
displayImage(config, "Character Region Step 1 Thresholds", drawImageDashboard(tempDash, bestVal.type(), 3));
|
displayImage(config, "Character Region Step 1 Thresholds", drawImageDashboard(tempDash, bestVal.type(), 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (this->debug)
|
if (this->debug)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Mat img_contours(img_threshold.size(), CV_8U);
|
Mat img_contours(img_threshold.size(), CV_8U);
|
||||||
img_threshold.copyTo(img_contours);
|
img_threshold.copyTo(img_contours);
|
||||||
cvtColor(img_contours, img_contours, CV_GRAY2RGB);
|
cvtColor(img_contours, img_contours, CV_GRAY2RGB);
|
||||||
@@ -81,29 +74,26 @@ CharacterRegion::CharacterRegion(Mat img, Config* config)
|
|||||||
vector<vector<Point> > allowedContours;
|
vector<vector<Point> > allowedContours;
|
||||||
for (int i = 0; i < contours.size(); i++)
|
for (int i = 0; i < contours.size(); i++)
|
||||||
{
|
{
|
||||||
if (charSegments[i])
|
if (charSegments[i])
|
||||||
allowedContours.push_back(contours[i]);
|
allowedContours.push_back(contours[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawContours(img_contours, contours,
|
drawContours(img_contours, contours,
|
||||||
-1, // draw all contours
|
-1, // draw all contours
|
||||||
cv::Scalar(255,0,0), // in blue
|
cv::Scalar(255,0,0), // in blue
|
||||||
1); // with a thickness of 1
|
1); // with a thickness of 1
|
||||||
|
|
||||||
drawContours(img_contours, allowedContours,
|
drawContours(img_contours, allowedContours,
|
||||||
-1, // draw all contours
|
-1, // draw all contours
|
||||||
cv::Scalar(0,255,0), // in green
|
cv::Scalar(0,255,0), // in green
|
||||||
1); // with a thickness of 1
|
1); // with a thickness of 1
|
||||||
|
|
||||||
|
|
||||||
displayImage(config, "Matching Contours", img_contours);
|
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));
|
//charsegments = this->getPossibleCharRegions(img_threshold, allContours, allHierarchy, STARTING_MIN_HEIGHT + (bestFitIndex * HEIGHT_STEP), STARTING_MAX_HEIGHT + (bestFitIndex * HEIGHT_STEP));
|
||||||
|
|
||||||
|
|
||||||
if (charAnalysis->linePolygon.size() > 0)
|
if (charAnalysis->linePolygon.size() > 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -120,7 +110,6 @@ CharacterRegion::CharacterRegion(Mat img, Config* config)
|
|||||||
else if (absangle > 1)
|
else if (absangle > 1)
|
||||||
confidenceDrainers += (10 - absangle) * 5;
|
confidenceDrainers += (10 - absangle) * 5;
|
||||||
|
|
||||||
|
|
||||||
if (confidenceDrainers >= 100)
|
if (confidenceDrainers >= 100)
|
||||||
this->confidence=1;
|
this->confidence=1;
|
||||||
else
|
else
|
||||||
@@ -128,8 +117,6 @@ CharacterRegion::CharacterRegion(Mat img, Config* config)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (config->debugTiming)
|
if (config->debugTiming)
|
||||||
{
|
{
|
||||||
timespec endTime;
|
timespec endTime;
|
||||||
@@ -144,49 +131,47 @@ CharacterRegion::~CharacterRegion()
|
|||||||
delete(charAnalysis);
|
delete(charAnalysis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Mat CharacterRegion::getPlateMask()
|
Mat CharacterRegion::getPlateMask()
|
||||||
{
|
{
|
||||||
return charAnalysis->plateMask;
|
return charAnalysis->plateMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
LineSegment CharacterRegion::getTopLine()
|
LineSegment CharacterRegion::getTopLine()
|
||||||
{
|
{
|
||||||
return charAnalysis->topLine;
|
return charAnalysis->topLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
LineSegment CharacterRegion::getBottomLine()
|
LineSegment CharacterRegion::getBottomLine()
|
||||||
{
|
{
|
||||||
return charAnalysis->bottomLine;
|
return charAnalysis->bottomLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<Point> CharacterRegion::getCharArea()
|
vector<Point> CharacterRegion::getCharArea()
|
||||||
{
|
{
|
||||||
return charAnalysis->charArea;
|
return charAnalysis->charArea;
|
||||||
}
|
}
|
||||||
|
|
||||||
LineSegment CharacterRegion::getCharBoxTop()
|
LineSegment CharacterRegion::getCharBoxTop()
|
||||||
{
|
{
|
||||||
return charAnalysis->charBoxTop;
|
return charAnalysis->charBoxTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
LineSegment CharacterRegion::getCharBoxBottom()
|
LineSegment CharacterRegion::getCharBoxBottom()
|
||||||
{
|
{
|
||||||
return charAnalysis->charBoxBottom;
|
return charAnalysis->charBoxBottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
LineSegment CharacterRegion::getCharBoxLeft()
|
LineSegment CharacterRegion::getCharBoxLeft()
|
||||||
{
|
{
|
||||||
return charAnalysis->charBoxLeft;
|
return charAnalysis->charBoxLeft;
|
||||||
}
|
}
|
||||||
|
|
||||||
LineSegment CharacterRegion::getCharBoxRight()
|
LineSegment CharacterRegion::getCharBoxRight()
|
||||||
{
|
{
|
||||||
return charAnalysis->charBoxRight;
|
return charAnalysis->charBoxRight;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterRegion::thresholdsInverted()
|
bool CharacterRegion::thresholdsInverted()
|
||||||
{
|
{
|
||||||
return charAnalysis->thresholdsInverted;
|
return charAnalysis->thresholdsInverted;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -17,11 +17,8 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include "colorfilter.h"
|
#include "colorfilter.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config)
|
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->debug = config->debugColorFiler;
|
||||||
|
|
||||||
|
|
||||||
this->grayscale = imageIsGrayscale(image);
|
this->grayscale = imageIsGrayscale(image);
|
||||||
|
|
||||||
if (this->debug)
|
if (this->debug)
|
||||||
@@ -48,7 +44,6 @@ ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config)
|
|||||||
|
|
||||||
findCharColors();
|
findCharColors();
|
||||||
|
|
||||||
|
|
||||||
if (config->debugTiming)
|
if (config->debugTiming)
|
||||||
{
|
{
|
||||||
timespec endTime;
|
timespec endTime;
|
||||||
@@ -75,12 +70,12 @@ bool ColorFilter::imageIsGrayscale(Mat image)
|
|||||||
|
|
||||||
if (r == g == b)
|
if (r == g == b)
|
||||||
{
|
{
|
||||||
// So far so good
|
// So far so good
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Image is color.
|
// Image is color.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,16 +107,14 @@ void ColorFilter::findCharColors()
|
|||||||
|
|
||||||
Mat erodedCharMask(charMask.size(), CV_8U);
|
Mat erodedCharMask(charMask.size(), CV_8U);
|
||||||
Mat element = getStructuringElement( 1,
|
Mat element = getStructuringElement( 1,
|
||||||
Size( 2 + 1, 2+1 ),
|
Size( 2 + 1, 2+1 ),
|
||||||
Point( 1, 1 ) );
|
Point( 1, 1 ) );
|
||||||
erode(charMask, erodedCharMask, element);
|
erode(charMask, erodedCharMask, element);
|
||||||
|
|
||||||
vector<vector<Point> > contours;
|
vector<vector<Point> > contours;
|
||||||
vector<Vec4i> hierarchy;
|
vector<Vec4i> hierarchy;
|
||||||
findContours(erodedCharMask, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
|
findContours(erodedCharMask, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
vector<float> hMeans, sMeans, vMeans;
|
vector<float> hMeans, sMeans, vMeans;
|
||||||
vector<float> hStdDevs, sStdDevs, vStdDevs;
|
vector<float> hStdDevs, sStdDevs, vStdDevs;
|
||||||
|
|
||||||
@@ -130,50 +123,46 @@ void ColorFilter::findCharColors()
|
|||||||
if (hierarchy[i][3] != -1)
|
if (hierarchy[i][3] != -1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Mat singleCharMask = Mat::zeros(hsv.size(), CV_8U);
|
Mat singleCharMask = Mat::zeros(hsv.size(), CV_8U);
|
||||||
|
|
||||||
drawContours(singleCharMask, contours,
|
drawContours(singleCharMask, contours,
|
||||||
i, // draw this contour
|
i, // draw this contour
|
||||||
cv::Scalar(255,255,255), // in
|
cv::Scalar(255,255,255), // in
|
||||||
CV_FILLED,
|
CV_FILLED,
|
||||||
8,
|
8,
|
||||||
hierarchy
|
hierarchy
|
||||||
);
|
);
|
||||||
|
|
||||||
// get rid of the outline by drawing a 1 pixel width black line
|
// get rid of the outline by drawing a 1 pixel width black line
|
||||||
drawContours(singleCharMask, contours,
|
drawContours(singleCharMask, contours,
|
||||||
i, // draw this contour
|
i, // draw this contour
|
||||||
cv::Scalar(0,0,0), // in
|
cv::Scalar(0,0,0), // in
|
||||||
1,
|
1,
|
||||||
8,
|
8,
|
||||||
hierarchy
|
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) <<mean[1] << " v: " << setw(7) << mean[2]
|
||||||
|
<< " | Std: h: " << setw(7) <<stddev[0] << " s: " << setw(7) <<stddev[1] << " v: " << stddev[2] << endl;
|
||||||
|
}
|
||||||
|
|
||||||
//drawAndWait(&singleCharMask);
|
if (mean[0] == 0 && mean[1] == 0 && mean[2] == 0)
|
||||||
|
continue;
|
||||||
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) <<mean[1] << " v: " << setw(7) << mean[2]
|
|
||||||
<< " | Std: h: " << setw(7) <<stddev[0] << " s: " << setw(7) <<stddev[1] << " v: " << stddev[2] << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mean[0] == 0 && mean[1] == 0 && mean[2] == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
hMeans.push_back(mean[0]);
|
|
||||||
sMeans.push_back(mean[1]);
|
|
||||||
vMeans.push_back(mean[2]);
|
|
||||||
hStdDevs.push_back(stddev[0]);
|
|
||||||
sStdDevs.push_back(stddev[1]);
|
|
||||||
vStdDevs.push_back(stddev[2]);
|
|
||||||
|
|
||||||
|
hMeans.push_back(mean[0]);
|
||||||
|
sMeans.push_back(mean[1]);
|
||||||
|
vMeans.push_back(mean[2]);
|
||||||
|
hStdDevs.push_back(stddev[0]);
|
||||||
|
sStdDevs.push_back(stddev[1]);
|
||||||
|
vStdDevs.push_back(stddev[2]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,11 +173,9 @@ void ColorFilter::findCharColors()
|
|||||||
int bestSatIndex = this->getMajorityOpinion(sMeans, .65, 35);
|
int bestSatIndex = this->getMajorityOpinion(sMeans, .65, 35);
|
||||||
int bestValIndex = this->getMajorityOpinion(vMeans, .65, 30);
|
int bestValIndex = this->getMajorityOpinion(vMeans, .65, 30);
|
||||||
|
|
||||||
|
|
||||||
if (sMeans[bestSatIndex] < MINIMUM_SATURATION)
|
if (sMeans[bestSatIndex] < MINIMUM_SATURATION)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
bool doHueFilter = false, doSatFilter = false, doValFilter = false;
|
bool doHueFilter = false, doSatFilter = false, doValFilter = false;
|
||||||
float hueMin, hueMax;
|
float hueMin, hueMax;
|
||||||
float satMin, satMax;
|
float satMin, satMax;
|
||||||
@@ -259,8 +246,6 @@ void ColorFilter::findCharColors()
|
|||||||
cout << "ColorFilter Val: " << bestValIndex << " : " << setw(7) << vMeans[bestValIndex] << " -- " << valMin << "-" << valMax << endl;
|
cout << "ColorFilter Val: " << bestValIndex << " : " << setw(7) << vMeans[bestValIndex] << " -- " << valMin << "-" << valMax << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Mat imgDebugHueOnly = Mat::zeros(hsv.size(), hsv.type());
|
Mat imgDebugHueOnly = Mat::zeros(hsv.size(), hsv.type());
|
||||||
Mat imgDebug = Mat::zeros(hsv.size(), hsv.type());
|
Mat imgDebug = Mat::zeros(hsv.size(), hsv.type());
|
||||||
Mat imgDistanceFromCenter = Mat::zeros(hsv.size(), CV_8U);
|
Mat imgDistanceFromCenter = Mat::zeros(hsv.size(), CV_8U);
|
||||||
@@ -291,19 +276,19 @@ void ColorFilter::findCharColors()
|
|||||||
|
|
||||||
if (doHueFilter && (h < hueMin || h > hueMax))
|
if (doHueFilter && (h < hueMin || h > hueMax))
|
||||||
{
|
{
|
||||||
hPasses = false;
|
hPasses = false;
|
||||||
imgDebug.at<Vec3b>(row, col)[0] = 0;
|
imgDebug.at<Vec3b>(row, col)[0] = 0;
|
||||||
debugMask.at<uchar>(row, col) = 0;
|
debugMask.at<uchar>(row, col) = 0;
|
||||||
}
|
}
|
||||||
if (doSatFilter && (s < satMin || s > satMax))
|
if (doSatFilter && (s < satMin || s > satMax))
|
||||||
{
|
{
|
||||||
sPasses = false;
|
sPasses = false;
|
||||||
imgDebug.at<Vec3b>(row, col)[1] = 0;
|
imgDebug.at<Vec3b>(row, col)[1] = 0;
|
||||||
}
|
}
|
||||||
if (doValFilter && (v < valMin || v > valMax))
|
if (doValFilter && (v < valMin || v > valMax))
|
||||||
{
|
{
|
||||||
vPasses = false;
|
vPasses = false;
|
||||||
imgDebug.at<Vec3b>(row, col)[2] = 0;
|
imgDebug.at<Vec3b>(row, col)[2] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (pixelPasses)
|
//if (pixelPasses)
|
||||||
@@ -314,27 +299,24 @@ void ColorFilter::findCharColors()
|
|||||||
//imgDebug.at<Vec3b>(row, col)[2] = vPasses & 255;
|
//imgDebug.at<Vec3b>(row, col)[2] = vPasses & 255;
|
||||||
|
|
||||||
if ((hPasses) || (hPasses && sPasses))//(hPasses && vPasses) || (sPasses && vPasses) ||
|
if ((hPasses) || (hPasses && sPasses))//(hPasses && vPasses) || (sPasses && vPasses) ||
|
||||||
this->colorMask.at<uchar>(row, col) = 255;
|
this->colorMask.at<uchar>(row, col) = 255;
|
||||||
else
|
else
|
||||||
this->colorMask.at<uchar>(row, col) = 0;
|
this->colorMask.at<uchar>(row, col) = 0;
|
||||||
|
|
||||||
|
|
||||||
if ((hPasses && sPasses) || (hPasses && vPasses) || (sPasses && vPasses))
|
if ((hPasses && sPasses) || (hPasses && vPasses) || (sPasses && vPasses))
|
||||||
{
|
{
|
||||||
vDistance = pow(vDistance, 0.9);
|
vDistance = pow(vDistance, 0.9);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
vDistance = pow(vDistance, 1.1);
|
vDistance = pow(vDistance, 1.1);
|
||||||
}
|
}
|
||||||
if (vDistance > 255)
|
if (vDistance > 255)
|
||||||
vDistance = 255;
|
vDistance = 255;
|
||||||
imgDistanceFromCenter.at<uchar>(row, col) = vDistance;
|
imgDistanceFromCenter.at<uchar>(row, col) = vDistance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
vector<Mat> debugImagesSet;
|
vector<Mat> debugImagesSet;
|
||||||
|
|
||||||
if (this->debug)
|
if (this->debug)
|
||||||
@@ -346,14 +328,13 @@ void ColorFilter::findCharColors()
|
|||||||
debugImagesSet.push_back(addLabel(maskCopy, "color Mask Before"));
|
debugImagesSet.push_back(addLabel(maskCopy, "color Mask Before"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Mat bigElement = getStructuringElement( 1,
|
Mat bigElement = getStructuringElement( 1,
|
||||||
Size( 3 + 1, 3+1 ),
|
Size( 3 + 1, 3+1 ),
|
||||||
Point( 1, 1 ) );
|
Point( 1, 1 ) );
|
||||||
|
|
||||||
Mat smallElement = getStructuringElement( 1,
|
Mat smallElement = getStructuringElement( 1,
|
||||||
Size( 1 + 1, 1+1 ),
|
Size( 1 + 1, 1+1 ),
|
||||||
Point( 1, 1 ) );
|
Point( 1, 1 ) );
|
||||||
|
|
||||||
morphologyEx(this->colorMask, this->colorMask, MORPH_CLOSE, bigElement);
|
morphologyEx(this->colorMask, this->colorMask, MORPH_CLOSE, bigElement);
|
||||||
//dilate(this->colorMask, this->colorMask, bigElement);
|
//dilate(this->colorMask, this->colorMask, bigElement);
|
||||||
@@ -378,15 +359,12 @@ void ColorFilter::findCharColors()
|
|||||||
|
|
||||||
debugImagesSet.push_back(addLabel(debugMask, "COLOR Hues off"));
|
debugImagesSet.push_back(addLabel(debugMask, "COLOR Hues off"));
|
||||||
|
|
||||||
|
|
||||||
Mat dashboard = drawImageDashboard(debugImagesSet, imgDebugHueOnly.type(), 3);
|
Mat dashboard = drawImageDashboard(debugImagesSet, imgDebugHueOnly.type(), 3);
|
||||||
displayImage(config, "Color Filter Images", dashboard);
|
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
|
// 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.
|
// Return -1 if it fails.
|
||||||
int ColorFilter::getMajorityOpinion(vector<float> values, float minPercentAgreement, float maxValDifference)
|
int ColorFilter::getMajorityOpinion(vector<float> values, float minPercentAgreement, float maxValDifference)
|
||||||
@@ -403,7 +381,7 @@ int ColorFilter::getMajorityOpinion(vector<float> values, float minPercentAgreem
|
|||||||
{
|
{
|
||||||
float diff = abs(values[i] - values[j]);
|
float diff = abs(values[i] - values[j]);
|
||||||
if (diff < maxValDifference)
|
if (diff < maxValDifference)
|
||||||
valuesInRange++;
|
valuesInRange++;
|
||||||
|
|
||||||
overallDiff += diff;
|
overallDiff += diff;
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
|
||||||
Config::Config(const std::string country, const std::string runtimeBaseDir)
|
Config::Config(const std::string country, const std::string runtimeBaseDir)
|
||||||
{
|
{
|
||||||
this->runtimeBaseDir = runtimeBaseDir;
|
this->runtimeBaseDir = runtimeBaseDir;
|
||||||
@@ -30,7 +29,7 @@ Config::Config(const std::string country, const std::string runtimeBaseDir)
|
|||||||
envRuntimeDir = getenv (ENV_VARIABLE_RUNTIME_DIR);
|
envRuntimeDir = getenv (ENV_VARIABLE_RUNTIME_DIR);
|
||||||
if (runtimeBaseDir.compare("") != 0)
|
if (runtimeBaseDir.compare("") != 0)
|
||||||
{
|
{
|
||||||
// User has supplied a runtime directory. Use that.
|
// User has supplied a runtime directory. Use that.
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (envRuntimeDir!=NULL)
|
else if (envRuntimeDir!=NULL)
|
||||||
@@ -93,12 +92,10 @@ void Config::loadValues(string country)
|
|||||||
ocrImageWidthPx = round(((float) templateWidthPx) * ocrImagePercent);
|
ocrImageWidthPx = round(((float) templateWidthPx) * ocrImagePercent);
|
||||||
ocrImageHeightPx = round(((float)templateHeightPx) * ocrImagePercent);
|
ocrImageHeightPx = round(((float)templateHeightPx) * ocrImagePercent);
|
||||||
|
|
||||||
|
|
||||||
float stateIdImagePercent = getFloat("common", "state_id_img_size_percent", 100);
|
float stateIdImagePercent = getFloat("common", "state_id_img_size_percent", 100);
|
||||||
stateIdImageWidthPx = round(((float)templateWidthPx) * ocrImagePercent);
|
stateIdImageWidthPx = round(((float)templateWidthPx) * ocrImagePercent);
|
||||||
stateIdimageHeightPx = round(((float)templateHeightPx) * ocrImagePercent);
|
stateIdimageHeightPx = round(((float)templateHeightPx) * ocrImagePercent);
|
||||||
|
|
||||||
|
|
||||||
charAnalysisMinPercent = getFloat(country, "char_analysis_min_pct", 0);
|
charAnalysisMinPercent = getFloat(country, "char_analysis_min_pct", 0);
|
||||||
charAnalysisHeightRange = getFloat(country, "char_analysis_height_range", 0);
|
charAnalysisHeightRange = getFloat(country, "char_analysis_height_range", 0);
|
||||||
charAnalysisHeightStepSize = getFloat(country, "char_analysis_height_step_size", 0);
|
charAnalysisHeightStepSize = getFloat(country, "char_analysis_height_step_size", 0);
|
||||||
@@ -150,7 +147,6 @@ void Config::debugOff()
|
|||||||
debugPostProcess = false;
|
debugPostProcess = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string Config::getCascadeRuntimeDir()
|
string Config::getCascadeRuntimeDir()
|
||||||
{
|
{
|
||||||
return this->runtimeBaseDir + CASCADE_DIR;
|
return this->runtimeBaseDir + CASCADE_DIR;
|
||||||
@@ -168,9 +164,6 @@ string Config::getTessdataPrefix()
|
|||||||
return "TESSDATA_PREFIX=" + this->runtimeBaseDir + "/ocr/";
|
return "TESSDATA_PREFIX=" + this->runtimeBaseDir + "/ocr/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
float Config::getFloat(string section, string key, float defaultValue)
|
float Config::getFloat(string section, string key, float defaultValue)
|
||||||
{
|
{
|
||||||
const char * pszValue = ini->GetValue(section.c_str(), key.c_str(), NULL /*default*/);
|
const char * pszValue = ini->GetValue(section.c_str(), key.c_str(), NULL /*default*/);
|
||||||
|
@@ -17,15 +17,12 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include "featurematcher.h"
|
#include "featurematcher.h"
|
||||||
|
|
||||||
|
|
||||||
//const int DEFAULT_QUERY_FEATURES = 305;
|
//const int DEFAULT_QUERY_FEATURES = 305;
|
||||||
//const int DEFAULT_TRAINING_FEATURES = 305;
|
//const int DEFAULT_TRAINING_FEATURES = 305;
|
||||||
const float MAX_DISTANCE_TO_MATCH = 100.0f;
|
const float MAX_DISTANCE_TO_MATCH = 100.0f;
|
||||||
|
|
||||||
|
|
||||||
FeatureMatcher::FeatureMatcher(Config* config)
|
FeatureMatcher::FeatureMatcher(Config* config)
|
||||||
{
|
{
|
||||||
this->config = config;
|
this->config = config;
|
||||||
@@ -35,7 +32,6 @@ FeatureMatcher::FeatureMatcher(Config* config)
|
|||||||
|
|
||||||
//this->descriptorMatcher = DescriptorMatcher::create( "FlannBased" );
|
//this->descriptorMatcher = DescriptorMatcher::create( "FlannBased" );
|
||||||
|
|
||||||
|
|
||||||
this->detector = new FastFeatureDetector(10, true);
|
this->detector = new FastFeatureDetector(10, true);
|
||||||
this->extractor = new BRISK(10, 1, 0.9);
|
this->extractor = new BRISK(10, 1, 0.9);
|
||||||
}
|
}
|
||||||
@@ -52,114 +48,103 @@ FeatureMatcher::~FeatureMatcher()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool FeatureMatcher::isLoaded()
|
bool FeatureMatcher::isLoaded()
|
||||||
{
|
{
|
||||||
if( detector.empty() || extractor.empty() || descriptorMatcher.empty() )
|
if( detector.empty() || extractor.empty() || descriptorMatcher.empty() )
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FeatureMatcher::numTrainingElements()
|
int FeatureMatcher::numTrainingElements()
|
||||||
{
|
{
|
||||||
return billMapping.size();
|
return billMapping.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void FeatureMatcher::surfStyleMatching( const Mat& queryDescriptors, vector<KeyPoint> queryKeypoints,
|
void FeatureMatcher::surfStyleMatching( const Mat& queryDescriptors, vector<KeyPoint> queryKeypoints,
|
||||||
vector<DMatch>& matches12 )
|
vector<DMatch>& matches12 )
|
||||||
{
|
{
|
||||||
vector<vector<DMatch> > matchesKnn;
|
vector<vector<DMatch> > matchesKnn;
|
||||||
|
|
||||||
this->descriptorMatcher->radiusMatch(queryDescriptors, matchesKnn, MAX_DISTANCE_TO_MATCH);
|
this->descriptorMatcher->radiusMatch(queryDescriptors, matchesKnn, MAX_DISTANCE_TO_MATCH);
|
||||||
|
|
||||||
|
vector<DMatch> tempMatches;
|
||||||
|
_surfStyleMatching(queryDescriptors, matchesKnn, tempMatches);
|
||||||
|
|
||||||
vector<DMatch> tempMatches;
|
crisscrossFiltering(queryKeypoints, tempMatches, matches12);
|
||||||
_surfStyleMatching(queryDescriptors, matchesKnn, tempMatches);
|
|
||||||
|
|
||||||
crisscrossFiltering(queryKeypoints, tempMatches, matches12);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void FeatureMatcher::_surfStyleMatching(const Mat& queryDescriptors, vector<vector<DMatch> > matchesKnn, vector<DMatch>& matches12)
|
void FeatureMatcher::_surfStyleMatching(const Mat& queryDescriptors, vector<vector<DMatch> > matchesKnn, vector<DMatch>& matches12)
|
||||||
{
|
{
|
||||||
|
|
||||||
//objectMatches.clear();
|
//objectMatches.clear();
|
||||||
//objectMatches.resize(objectIds.size());
|
//objectMatches.resize(objectIds.size());
|
||||||
//cout << "starting matcher" << matchesKnn.size() << endl;
|
//cout << "starting matcher" << matchesKnn.size() << endl;
|
||||||
for (int descInd = 0; descInd < queryDescriptors.rows; descInd++)
|
for (int descInd = 0; descInd < queryDescriptors.rows; descInd++)
|
||||||
|
{
|
||||||
|
const std::vector<DMatch> & 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<DMatch> & matches = matchesKnn[descInd];
|
ratioThreshold = 0.85;
|
||||||
//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);
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<KeyPoint> queryKeypoints,
|
|||||||
vector<DMatch> matchesForOnePlate;
|
vector<DMatch> matchesForOnePlate;
|
||||||
for (int j = 0; j < inputMatches.size(); j++)
|
for (int j = 0; j < inputMatches.size(); j++)
|
||||||
{
|
{
|
||||||
if (inputMatches[j].imgIdx == i)
|
if (inputMatches[j].imgIdx == i)
|
||||||
matchesForOnePlate.push_back(inputMatches[j]);
|
matchesForOnePlate.push_back(inputMatches[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each plate, compare the lines for the keypoints (training image and query image)
|
// For each plate, compare the lines for the keypoints (training image and query image)
|
||||||
@@ -196,8 +181,6 @@ void FeatureMatcher::crisscrossFiltering(const vector<KeyPoint> queryKeypoints,
|
|||||||
matchIdx.push_back(j);
|
matchIdx.push_back(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Iterate through each line (n^2) removing the one with the most criss-crosses until there are none left.
|
// Iterate through each line (n^2) removing the one with the most criss-crosses until there are none left.
|
||||||
int mostIntersections = 1;
|
int mostIntersections = 1;
|
||||||
while (mostIntersections > 0 && vlines.size() > 0)
|
while (mostIntersections > 0 && vlines.size() > 0)
|
||||||
@@ -207,37 +190,37 @@ void FeatureMatcher::crisscrossFiltering(const vector<KeyPoint> queryKeypoints,
|
|||||||
|
|
||||||
for (int j = 0; j < vlines.size(); j++)
|
for (int j = 0; j < vlines.size(); j++)
|
||||||
{
|
{
|
||||||
int intrCount = 0;
|
int intrCount = 0;
|
||||||
for (int q = 0; q < vlines.size(); q++)
|
for (int q = 0; q < vlines.size(); q++)
|
||||||
{
|
{
|
||||||
Point vintr = vlines[j].intersection(vlines[q]);
|
Point vintr = vlines[j].intersection(vlines[q]);
|
||||||
Point hintr = hlines[j].intersection(hlines[q]);
|
Point hintr = hlines[j].intersection(hlines[q]);
|
||||||
float vangleDiff = abs(vlines[j].angle - vlines[q].angle);
|
float vangleDiff = abs(vlines[j].angle - vlines[q].angle);
|
||||||
float hangleDiff = abs(hlines[j].angle - hlines[q].angle);
|
float hangleDiff = abs(hlines[j].angle - hlines[q].angle);
|
||||||
if (vintr.inside(crissCrossAreaVertical) && vangleDiff > 10)
|
if (vintr.inside(crissCrossAreaVertical) && vangleDiff > 10)
|
||||||
{
|
{
|
||||||
intrCount++;
|
intrCount++;
|
||||||
}
|
}
|
||||||
else if (hintr.inside(crissCrossAreaHorizontal) && hangleDiff > 10)
|
else if (hintr.inside(crissCrossAreaHorizontal) && hangleDiff > 10)
|
||||||
{
|
{
|
||||||
intrCount++;
|
intrCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (intrCount > mostIntersections)
|
if (intrCount > mostIntersections)
|
||||||
{
|
{
|
||||||
mostIntersections = intrCount;
|
mostIntersections = intrCount;
|
||||||
mostIntersectionsIndex = j;
|
mostIntersectionsIndex = j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mostIntersectionsIndex >= 0)
|
if (mostIntersectionsIndex >= 0)
|
||||||
{
|
{
|
||||||
if (this->config->debugStateId)
|
if (this->config->debugStateId)
|
||||||
cout << "Filtered intersection! " << billMapping[i] << endl;
|
cout << "Filtered intersection! " << billMapping[i] << endl;
|
||||||
vlines.erase(vlines.begin() + mostIntersectionsIndex);
|
vlines.erase(vlines.begin() + mostIntersectionsIndex);
|
||||||
hlines.erase(hlines.begin() + mostIntersectionsIndex);
|
hlines.erase(hlines.begin() + mostIntersectionsIndex);
|
||||||
matchIdx.erase(matchIdx.begin() + mostIntersectionsIndex);
|
matchIdx.erase(matchIdx.begin() + mostIntersectionsIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -251,135 +234,124 @@ void FeatureMatcher::crisscrossFiltering(const vector<KeyPoint> queryKeypoints,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Returns true if successful, false otherwise
|
// Returns true if successful, false otherwise
|
||||||
bool FeatureMatcher::loadRecognitionSet(string country)
|
bool FeatureMatcher::loadRecognitionSet(string country)
|
||||||
{
|
{
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
out << config->getKeypointsRuntimeDir() << "/" << country << "/";
|
out << config->getKeypointsRuntimeDir() << "/" << country << "/";
|
||||||
string country_dir = out.str();
|
string country_dir = out.str();
|
||||||
|
|
||||||
|
if (DirectoryExists(country_dir.c_str()))
|
||||||
|
{
|
||||||
|
vector<Mat> trainImages;
|
||||||
|
vector<string> plateFiles = getFilesInDir(country_dir.c_str());
|
||||||
|
|
||||||
if (DirectoryExists(country_dir.c_str()))
|
for (int i = 0; i < plateFiles.size(); i++)
|
||||||
{
|
{
|
||||||
vector<Mat> trainImages;
|
if (hasEnding(plateFiles[i], ".jpg") == false)
|
||||||
vector<string> plateFiles = getFilesInDir(country_dir.c_str());
|
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)
|
cout << "Can not read images" << endl;
|
||||||
continue;
|
return -1;
|
||||||
|
|
||||||
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<KeyPoint> 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Mat descriptors;
|
||||||
|
|
||||||
this->descriptorMatcher->add(trainImages);
|
vector<KeyPoint> keypoints;
|
||||||
this->descriptorMatcher->train();
|
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,
|
RecognitionResult FeatureMatcher::recognize( const Mat& queryImg, bool drawOnImage, Mat* outputImage,
|
||||||
bool debug_on, vector<int> debug_matches_array
|
bool debug_on, vector<int> debug_matches_array
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
RecognitionResult result;
|
RecognitionResult result;
|
||||||
|
|
||||||
result.haswinner = false;
|
result.haswinner = false;
|
||||||
|
|
||||||
Mat queryDescriptors;
|
Mat queryDescriptors;
|
||||||
vector<KeyPoint> queryKeypoints;
|
vector<KeyPoint> queryKeypoints;
|
||||||
|
|
||||||
detector->detect( queryImg, queryKeypoints );
|
detector->detect( queryImg, queryKeypoints );
|
||||||
extractor->compute(queryImg, queryKeypoints, queryDescriptors);
|
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.
|
drawKeypoints( queryImg, queryKeypoints, *outputImage, CV_RGB(0, 255, 0), DrawMatchesFlags::DEFAULT );
|
||||||
if (drawOnImage)
|
|
||||||
{
|
|
||||||
drawKeypoints( queryImg, queryKeypoints, *outputImage, CV_RGB(0, 255, 0), DrawMatchesFlags::DEFAULT );
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<DMatch> filteredMatches;
|
||||||
|
|
||||||
|
surfStyleMatching( queryDescriptors, queryKeypoints, filteredMatches );
|
||||||
|
|
||||||
vector<DMatch> filteredMatches;
|
// Create and initialize the counts to 0
|
||||||
|
std::vector<int> 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
|
float max_count = 0; // represented as a percent (0 to 100)
|
||||||
std::vector<int> bill_match_counts( billMapping.size() );
|
int secondmost_count = 0;
|
||||||
|
int maxcount_index = -1;
|
||||||
for (int i = 0; i < billMapping.size(); i++) { bill_match_counts[i] = 0; }
|
for (int i = 0; i < billMapping.size(); i++)
|
||||||
|
{
|
||||||
for (int i = 0; i < filteredMatches.size(); i++)
|
if (bill_match_counts[i] > max_count && bill_match_counts[i] >= 4)
|
||||||
{
|
{
|
||||||
bill_match_counts[filteredMatches[i].imgIdx]++;
|
secondmost_count = max_count;
|
||||||
//if (filteredMatches[i].imgIdx
|
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)
|
||||||
float max_count = 0; // represented as a percent (0 to 100)
|
score = 0;
|
||||||
int secondmost_count = 0;
|
else if (score > 100)
|
||||||
int maxcount_index = -1;
|
score = 100;
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
if (score > 0)
|
if (score > 0)
|
||||||
{
|
{
|
||||||
@@ -389,28 +361,28 @@ RecognitionResult FeatureMatcher::recognize( const Mat& queryImg, bool drawOnIma
|
|||||||
|
|
||||||
if (drawOnImage)
|
if (drawOnImage)
|
||||||
{
|
{
|
||||||
vector<KeyPoint> positiveMatches;
|
vector<KeyPoint> positiveMatches;
|
||||||
for (int i = 0; i < filteredMatches.size(); i++)
|
for (int i = 0; i < filteredMatches.size(); i++)
|
||||||
{
|
{
|
||||||
if (filteredMatches[i].imgIdx == maxcount_index)
|
if (filteredMatches[i].imgIdx == maxcount_index)
|
||||||
{
|
{
|
||||||
positiveMatches.push_back( queryKeypoints[filteredMatches[i].queryIdx] );
|
positiveMatches.push_back( queryKeypoints[filteredMatches[i].queryIdx] );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat tmpImg;
|
Mat tmpImg;
|
||||||
drawKeypoints( queryImg, queryKeypoints, tmpImg, CV_RGB(185, 0, 0), DrawMatchesFlags::DEFAULT );
|
drawKeypoints( queryImg, queryKeypoints, tmpImg, CV_RGB(185, 0, 0), DrawMatchesFlags::DEFAULT );
|
||||||
drawKeypoints( tmpImg, positiveMatches, *outputImage, CV_RGB(0, 255, 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;
|
std::ostringstream out;
|
||||||
out << result.winner << " (" << result.confidence << "%)";
|
out << result.winner << " (" << result.confidence << "%)";
|
||||||
|
|
||||||
// we detected a bill, let the people know!
|
// 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);
|
//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++)
|
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;
|
return result;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -19,13 +19,12 @@
|
|||||||
|
|
||||||
#include "licenseplatecandidate.h"
|
#include "licenseplatecandidate.h"
|
||||||
|
|
||||||
|
|
||||||
LicensePlateCandidate::LicensePlateCandidate(Mat frame, Rect regionOfInterest, Config* config)
|
LicensePlateCandidate::LicensePlateCandidate(Mat frame, Rect regionOfInterest, Config* config)
|
||||||
{
|
{
|
||||||
this->config = config;
|
this->config = config;
|
||||||
|
|
||||||
this->frame = frame;
|
this->frame = frame;
|
||||||
this->plateRegion = regionOfInterest;
|
this->plateRegion = regionOfInterest;
|
||||||
}
|
}
|
||||||
|
|
||||||
LicensePlateCandidate::~LicensePlateCandidate()
|
LicensePlateCandidate::~LicensePlateCandidate()
|
||||||
@@ -38,7 +37,6 @@ void LicensePlateCandidate::recognize()
|
|||||||
{
|
{
|
||||||
charSegmenter = NULL;
|
charSegmenter = NULL;
|
||||||
|
|
||||||
|
|
||||||
this->confidence = 0;
|
this->confidence = 0;
|
||||||
|
|
||||||
int expandX = round(this->plateRegion.width * 0.15);
|
int expandX = round(this->plateRegion.width * 0.15);
|
||||||
@@ -46,19 +44,14 @@ void LicensePlateCandidate::recognize()
|
|||||||
// expand box by 15% in all directions
|
// expand box by 15% in all directions
|
||||||
Rect expandedRegion = expandRect( this->plateRegion, expandX, expandY, frame.cols, frame.rows) ;
|
Rect expandedRegion = expandRect( this->plateRegion, expandX, expandY, frame.cols, frame.rows) ;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Mat plate_bgr = Mat(frame, expandedRegion);
|
Mat plate_bgr = Mat(frame, expandedRegion);
|
||||||
resize(plate_bgr, plate_bgr, Size(config->templateWidthPx, config->templateHeightPx));
|
resize(plate_bgr, plate_bgr, Size(config->templateWidthPx, config->templateHeightPx));
|
||||||
|
|
||||||
Mat plate_bgr_cleaned = Mat(plate_bgr.size(), plate_bgr.type());
|
Mat plate_bgr_cleaned = Mat(plate_bgr.size(), plate_bgr.type());
|
||||||
this->cleanupColors(plate_bgr, plate_bgr_cleaned);
|
this->cleanupColors(plate_bgr, plate_bgr_cleaned);
|
||||||
|
|
||||||
|
|
||||||
CharacterRegion charRegion(plate_bgr, config);
|
CharacterRegion charRegion(plate_bgr, config);
|
||||||
|
|
||||||
|
|
||||||
if (charRegion.confidence > 10)
|
if (charRegion.confidence > 10)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -75,15 +68,12 @@ void LicensePlateCandidate::recognize()
|
|||||||
{
|
{
|
||||||
this->plateCorners = transformPointsToOriginalImage(frame, plate_bgr, expandedRegion, smallPlateCorners);
|
this->plateCorners = transformPointsToOriginalImage(frame, plate_bgr, expandedRegion, smallPlateCorners);
|
||||||
|
|
||||||
|
|
||||||
this->deskewed = deSkewPlate(frame, this->plateCorners);
|
this->deskewed = deSkewPlate(frame, this->plateCorners);
|
||||||
|
|
||||||
|
|
||||||
charSegmenter = new CharacterSegmenter(deskewed, charRegion.thresholdsInverted(), config);
|
charSegmenter = new CharacterSegmenter(deskewed, charRegion.thresholdsInverted(), config);
|
||||||
|
|
||||||
|
|
||||||
//this->recognizedText = ocr->recognizedText;
|
//this->recognizedText = ocr->recognizedText;
|
||||||
//strcpy(this->recognizedText, ocr.recognizedText);
|
//strcpy(this->recognizedText, ocr.recognizedText);
|
||||||
|
|
||||||
this->confidence = 100;
|
this->confidence = 100;
|
||||||
|
|
||||||
@@ -91,13 +81,8 @@ void LicensePlateCandidate::recognize()
|
|||||||
charRegion.confidence = 0;
|
charRegion.confidence = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Re-maps the coordinates from the smallImage to the coordinate space of the bigImage.
|
// Re-maps the coordinates from the smallImage to the coordinate space of the bigImage.
|
||||||
vector<Point2f> LicensePlateCandidate::transformPointsToOriginalImage(Mat bigImage, Mat smallImage, Rect region, vector<Point> corners)
|
vector<Point2f> LicensePlateCandidate::transformPointsToOriginalImage(Mat bigImage, Mat smallImage, Rect region, vector<Point> corners)
|
||||||
{
|
{
|
||||||
@@ -116,7 +101,6 @@ vector<Point2f> LicensePlateCandidate::transformPointsToOriginalImage(Mat bigIma
|
|||||||
return cornerPoints;
|
return cornerPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Mat LicensePlateCandidate::deSkewPlate(Mat inputImage, vector<Point2f> corners)
|
Mat LicensePlateCandidate::deSkewPlate(Mat inputImage, vector<Point2f> corners)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -134,8 +118,8 @@ Mat LicensePlateCandidate::deSkewPlate(Mat inputImage, vector<Point2f> corners)
|
|||||||
int height = round(((float) width) / aspect);
|
int height = round(((float) width) / aspect);
|
||||||
if (height > config->ocrImageHeightPx)
|
if (height > config->ocrImageHeightPx)
|
||||||
{
|
{
|
||||||
height = config->ocrImageHeightPx;
|
height = config->ocrImageHeightPx;
|
||||||
width = round(((float) height) * aspect);
|
width = round(((float) height) * aspect);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat deskewed(height, width, frame.type());
|
Mat deskewed(height, width, frame.type());
|
||||||
@@ -159,7 +143,6 @@ Mat LicensePlateCandidate::deSkewPlate(Mat inputImage, vector<Point2f> corners)
|
|||||||
return deskewed;
|
return deskewed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LicensePlateCandidate::cleanupColors(Mat inputImage, Mat outputImage)
|
void LicensePlateCandidate::cleanupColors(Mat inputImage, Mat outputImage)
|
||||||
{
|
{
|
||||||
if (this->config->debugGeneral)
|
if (this->config->debugGeneral)
|
||||||
@@ -174,26 +157,24 @@ void LicensePlateCandidate::cleanupColors(Mat inputImage, Mat outputImage)
|
|||||||
// Equalize intensity:
|
// Equalize intensity:
|
||||||
if(intermediate.channels() >= 3)
|
if(intermediate.channels() >= 3)
|
||||||
{
|
{
|
||||||
Mat ycrcb;
|
Mat ycrcb;
|
||||||
|
|
||||||
cvtColor(intermediate,ycrcb,CV_BGR2YCrCb);
|
cvtColor(intermediate,ycrcb,CV_BGR2YCrCb);
|
||||||
|
|
||||||
vector<Mat> channels;
|
vector<Mat> channels;
|
||||||
split(ycrcb,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);
|
bilateralFilter(intermediate, outputImage, 3, 25, 35);
|
||||||
|
|
||||||
|
|
||||||
if (this->config->debugGeneral)
|
if (this->config->debugGeneral)
|
||||||
{
|
{
|
||||||
displayImage(config, "After cleanup", outputImage);
|
displayImage(config, "After cleanup", outputImage);
|
||||||
|
@@ -17,30 +17,26 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "ocr.h"
|
#include "ocr.h"
|
||||||
|
|
||||||
OCR::OCR(Config* config)
|
OCR::OCR(Config* config)
|
||||||
{
|
{
|
||||||
this->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
|
// Tesseract requires the prefix directory to be set as an env variable
|
||||||
vector<char> tessdataPrefix(config->getTessdataPrefix().size() + 1);
|
vector<char> tessdataPrefix(config->getTessdataPrefix().size() + 1);
|
||||||
|
|
||||||
strcpy(tessdataPrefix.data(), config->getTessdataPrefix().c_str());
|
strcpy(tessdataPrefix.data(), config->getTessdataPrefix().c_str());
|
||||||
putenv(tessdataPrefix.data());
|
putenv(tessdataPrefix.data());
|
||||||
|
|
||||||
|
tesseract->Init("", config->ocrLanguage.c_str() );
|
||||||
tesseract->Init("", config->ocrLanguage.c_str() );
|
tesseract->SetVariable("save_blob_choices", "T");
|
||||||
tesseract->SetVariable("save_blob_choices", "T");
|
//tesseract->SetVariable("tessedit_char_whitelist", "ABCDEFGHIJKLMNPQRSTUVWXYZ1234567890");
|
||||||
//tesseract->SetVariable("tessedit_char_whitelist", "ABCDEFGHIJKLMNPQRSTUVWXYZ1234567890");
|
tesseract->SetPageSegMode(PSM_SINGLE_CHAR);
|
||||||
tesseract->SetPageSegMode(PSM_SINGLE_CHAR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OCR::~OCR()
|
OCR::~OCR()
|
||||||
@@ -50,18 +46,14 @@ OCR::~OCR()
|
|||||||
delete tesseract;
|
delete tesseract;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void OCR::performOCR(vector<Mat> thresholds, vector<Rect> charRegions)
|
void OCR::performOCR(vector<Mat> thresholds, vector<Rect> charRegions)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
timespec startTime;
|
timespec startTime;
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
|
|
||||||
|
|
||||||
postProcessor->clear();
|
postProcessor->clear();
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < thresholds.size(); i++)
|
for (int i = 0; i < thresholds.size(); i++)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -69,7 +61,6 @@ void OCR::performOCR(vector<Mat> thresholds, vector<Rect> charRegions)
|
|||||||
bitwise_not(thresholds[i], thresholds[i]);
|
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());
|
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++)
|
for (int j = 0; j < charRegions.size(); j++)
|
||||||
{
|
{
|
||||||
Rect expandedRegion = expandRect( charRegions[j], 2, 2, thresholds[i].cols, thresholds[i].rows) ;
|
Rect expandedRegion = expandRect( charRegions[j], 2, 2, thresholds[i].cols, thresholds[i].rows) ;
|
||||||
@@ -79,52 +70,54 @@ void OCR::performOCR(vector<Mat> thresholds, vector<Rect> charRegions)
|
|||||||
|
|
||||||
tesseract::ResultIterator* ri = tesseract->GetIterator();
|
tesseract::ResultIterator* ri = tesseract->GetIterator();
|
||||||
tesseract::PageIteratorLevel level = tesseract::RIL_SYMBOL;
|
tesseract::PageIteratorLevel level = tesseract::RIL_SYMBOL;
|
||||||
do {
|
do
|
||||||
const char* symbol = ri->GetUTF8Text(level);
|
{
|
||||||
float conf = ri->Confidence(level);
|
const char* symbol = ri->GetUTF8Text(level);
|
||||||
|
float conf = ri->Confidence(level);
|
||||||
|
|
||||||
bool dontcare;
|
bool dontcare;
|
||||||
int fontindex = 0;
|
int fontindex = 0;
|
||||||
int pointsize = 0;
|
int pointsize = 0;
|
||||||
const char* fontName = ri->WordFontAttributes(&dontcare, &dontcare, &dontcare, &dontcare, &dontcare, &dontcare, &pointsize, &fontindex);
|
const char* fontName = ri->WordFontAttributes(&dontcare, &dontcare, &dontcare, &dontcare, &dontcare, &dontcare, &pointsize, &fontindex);
|
||||||
|
|
||||||
if(symbol != 0 && pointsize >= config->ocrMinFontSize) {
|
if(symbol != 0 && pointsize >= config->ocrMinFontSize)
|
||||||
postProcessor->addLetter(*symbol, j, conf);
|
{
|
||||||
|
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 (this->config->debugOcr)
|
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;
|
bool indent = false;
|
||||||
} while((ri->Next(level)));
|
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;
|
delete ri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->debugTiming)
|
if (config->debugTiming)
|
||||||
@@ -134,7 +127,4 @@ void OCR::performOCR(vector<Mat> thresholds, vector<Rect> charRegions)
|
|||||||
cout << "OCR Time: " << diffclock(startTime, endTime) << "ms." << endl;
|
cout << "OCR Time: " << diffclock(startTime, endTime) << "ms." << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,6 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include "platecorners.h"
|
#include "platecorners.h"
|
||||||
|
|
||||||
PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, CharacterRegion* charRegion, Config* config)
|
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)
|
if (this->config->debugPlateCorners)
|
||||||
cout << "PlateCorners constructor" << endl;
|
cout << "PlateCorners constructor" << endl;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this->inputImage = inputImage;
|
this->inputImage = inputImage;
|
||||||
this->plateLines = plateLines;
|
this->plateLines = plateLines;
|
||||||
this->charRegion = charRegion;
|
this->charRegion = charRegion;
|
||||||
@@ -36,7 +33,6 @@ PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, CharacterRegi
|
|||||||
this->bestHorizontalScore = 9999999999999;
|
this->bestHorizontalScore = 9999999999999;
|
||||||
this->bestVerticalScore = 9999999999999;
|
this->bestVerticalScore = 9999999999999;
|
||||||
|
|
||||||
|
|
||||||
Point topPoint = charRegion->getTopLine().midpoint();
|
Point topPoint = charRegion->getTopLine().midpoint();
|
||||||
Point bottomPoint = charRegion->getBottomLine().closestPointOnSegmentTo(topPoint);
|
Point bottomPoint = charRegion->getBottomLine().closestPointOnSegmentTo(topPoint);
|
||||||
this->charHeight = distanceBetweenPoints(topPoint, bottomPoint);
|
this->charHeight = distanceBetweenPoints(topPoint, bottomPoint);
|
||||||
@@ -57,7 +53,7 @@ PlateCorners::~PlateCorners()
|
|||||||
vector<Point> PlateCorners::findPlateCorners()
|
vector<Point> PlateCorners::findPlateCorners()
|
||||||
{
|
{
|
||||||
if (this->config->debugPlateCorners)
|
if (this->config->debugPlateCorners)
|
||||||
cout << "PlateCorners::findPlateCorners" << endl;
|
cout << "PlateCorners::findPlateCorners" << endl;
|
||||||
|
|
||||||
timespec startTime;
|
timespec startTime;
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
@@ -65,15 +61,14 @@ vector<Point> PlateCorners::findPlateCorners()
|
|||||||
int horizontalLines = this->plateLines->horizontalLines.size();
|
int horizontalLines = this->plateLines->horizontalLines.size();
|
||||||
int verticalLines = this->plateLines->verticalLines.size();
|
int verticalLines = this->plateLines->verticalLines.size();
|
||||||
|
|
||||||
|
|
||||||
// layout horizontal lines
|
// layout horizontal lines
|
||||||
for (int h1 = NO_LINE; h1 < horizontalLines; h1++)
|
for (int h1 = NO_LINE; h1 < horizontalLines; h1++)
|
||||||
{
|
{
|
||||||
for (int h2 = NO_LINE; h2 < horizontalLines; h2++)
|
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<Point> PlateCorners::findPlateCorners()
|
|||||||
// layout vertical lines
|
// layout vertical lines
|
||||||
for (int v1 = NO_LINE; v1 < verticalLines; v1++)
|
for (int v1 = NO_LINE; v1 < verticalLines; v1++)
|
||||||
{
|
{
|
||||||
for (int v2 = NO_LINE; v2 < verticalLines; v2++)
|
for (int v2 = NO_LINE; v2 < verticalLines; v2++)
|
||||||
{
|
{
|
||||||
if (v1 == v2 && v1 != NO_LINE) continue;
|
if (v1 == v2 && v1 != NO_LINE) continue;
|
||||||
|
|
||||||
this->scoreVerticals(v1, v2);
|
this->scoreVerticals(v1, v2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (this->config->debugPlateCorners)
|
if (this->config->debugPlateCorners)
|
||||||
{
|
{
|
||||||
|
|
||||||
cout << "Drawing debug stuff..." << endl;
|
cout << "Drawing debug stuff..." << endl;
|
||||||
|
|
||||||
Mat imgCorners = Mat(inputImage.size(), inputImage.type());
|
Mat imgCorners = Mat(inputImage.size(), inputImage.type());
|
||||||
inputImage.copyTo(imgCorners);
|
inputImage.copyTo(imgCorners);
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
circle(imgCorners, charRegion->getCharArea()[i], 2, Scalar(0, 0, 0));
|
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);
|
displayImage(config, "Winning top/bottom Boundaries", imgCorners);
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,8 +117,6 @@ vector<Point> PlateCorners::findPlateCorners()
|
|||||||
corners.push_back(bestBottom.intersection(bestRight));
|
corners.push_back(bestBottom.intersection(bestRight));
|
||||||
corners.push_back(bestBottom.intersection(bestLeft));
|
corners.push_back(bestBottom.intersection(bestLeft));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (config->debugTiming)
|
if (config->debugTiming)
|
||||||
{
|
{
|
||||||
timespec endTime;
|
timespec endTime;
|
||||||
@@ -139,7 +127,6 @@ vector<Point> PlateCorners::findPlateCorners()
|
|||||||
return corners;
|
return corners;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PlateCorners::scoreVerticals(int v1, int v2)
|
void PlateCorners::scoreVerticals(int v1, int v2)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -148,8 +135,6 @@ void PlateCorners::scoreVerticals(int v1, int v2)
|
|||||||
LineSegment left;
|
LineSegment left;
|
||||||
LineSegment right;
|
LineSegment right;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
float charHeightToPlateWidthRatio = config->plateWidthMM / config->charHeightMM;
|
float charHeightToPlateWidthRatio = config->plateWidthMM / config->charHeightMM;
|
||||||
float idealPixelWidth = this->charHeight * (charHeightToPlateWidthRatio * 1.05); // Add 10% so we don't clip any characters
|
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;
|
score += SCORING_MISSING_SEGMENT_PENALTY_VERTICAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Make sure this line is to the left of our license plate letters
|
// Make sure this line is to the left of our license plate letters
|
||||||
if (left.isPointBelowLine(charRegion->getCharBoxLeft().midpoint()) == false)
|
if (left.isPointBelowLine(charRegion->getCharBoxLeft().midpoint()) == false)
|
||||||
return;
|
return;
|
||||||
@@ -193,7 +176,6 @@ void PlateCorners::scoreVerticals(int v1, int v2)
|
|||||||
if (right.isPointBelowLine(charRegion->getCharBoxRight().midpoint()))
|
if (right.isPointBelowLine(charRegion->getCharBoxRight().midpoint()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
// Score "Distance from the edge...
|
// Score "Distance from the edge...
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
@@ -204,7 +186,6 @@ void PlateCorners::scoreVerticals(int v1, int v2)
|
|||||||
float distanceFromEdge = leftDistanceFromEdge + rightDistanceFromEdge;
|
float distanceFromEdge = leftDistanceFromEdge + rightDistanceFromEdge;
|
||||||
score += distanceFromEdge * SCORING_VERTICALDISTANCE_FROMEDGE_WEIGHT;
|
score += distanceFromEdge * SCORING_VERTICALDISTANCE_FROMEDGE_WEIGHT;
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
// Score "Boxiness" of the 4 lines. How close is it to a parallelogram?
|
// 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 += (verticalAngleDiff) * SCORING_BOXINESS_WEIGHT;
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// SCORE the shape wrt character position and height relative to position
|
// SCORE the shape wrt character position and height relative to position
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
Point leftMidLinePoint = left.closestPointOnSegmentTo(charRegion->getCharBoxLeft().midpoint());
|
Point leftMidLinePoint = left.closestPointOnSegmentTo(charRegion->getCharBoxLeft().midpoint());
|
||||||
Point rightMidLinePoint = right.closestPointOnSegmentTo(charRegion->getCharBoxRight().midpoint());
|
Point rightMidLinePoint = right.closestPointOnSegmentTo(charRegion->getCharBoxRight().midpoint());
|
||||||
|
|
||||||
@@ -230,7 +209,6 @@ void PlateCorners::scoreVerticals(int v1, int v2)
|
|||||||
{
|
{
|
||||||
float scorecomponent;
|
float scorecomponent;
|
||||||
|
|
||||||
|
|
||||||
if (this->config->debugPlateCorners)
|
if (this->config->debugPlateCorners)
|
||||||
{
|
{
|
||||||
cout << "xx xx Score: charHeight " << this->charHeight << endl;
|
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 charHeightToPlateHeightRatio = config->plateHeightMM / config->charHeightMM;
|
||||||
float idealPixelHeight = this->charHeight * charHeightToPlateHeightRatio;
|
float idealPixelHeight = this->charHeight * charHeightToPlateHeightRatio;
|
||||||
|
|
||||||
|
|
||||||
if (h1 == NO_LINE && h2 == NO_LINE)
|
if (h1 == NO_LINE && h2 == NO_LINE)
|
||||||
{
|
{
|
||||||
// return;
|
// return;
|
||||||
@@ -308,39 +285,26 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
|
|||||||
score += SCORING_MISSING_SEGMENT_PENALTY_HORIZONTAL;
|
score += SCORING_MISSING_SEGMENT_PENALTY_HORIZONTAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Make sure this line is above our license plate letters
|
// Make sure this line is above our license plate letters
|
||||||
if (top.isPointBelowLine(charRegion->getCharBoxTop().midpoint()) == false)
|
if (top.isPointBelowLine(charRegion->getCharBoxTop().midpoint()) == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
// Make sure this line is below our license plate letters
|
// Make sure this line is below our license plate letters
|
||||||
if (bottom.isPointBelowLine(charRegion->getCharBoxBottom().midpoint()))
|
if (bottom.isPointBelowLine(charRegion->getCharBoxBottom().midpoint()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// We now have 4 possible lines. Let's put them to the test and score them...
|
// 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?
|
// Score "Boxiness" of the 4 lines. How close is it to a parallelogram?
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
float horizontalAngleDiff = abs(top.angle - bottom.angle);
|
float horizontalAngleDiff = abs(top.angle - bottom.angle);
|
||||||
|
|
||||||
|
|
||||||
score += (horizontalAngleDiff) * SCORING_BOXINESS_WEIGHT;
|
score += (horizontalAngleDiff) * SCORING_BOXINESS_WEIGHT;
|
||||||
// if (this->debug)
|
// if (this->debug)
|
||||||
// cout << "PlateCorners boxiness score: " << (horizontalAngleDiff + verticalAngleDiff) * SCORING_BOXINESS_WEIGHT << endl;
|
// cout << "PlateCorners boxiness score: " << (horizontalAngleDiff + verticalAngleDiff) * SCORING_BOXINESS_WEIGHT << endl;
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// SCORE the shape wrt character position and height relative to position
|
// 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
|
// Get the height difference
|
||||||
|
|
||||||
|
|
||||||
float heightRatio = charHeight / plateHeightPx;
|
float heightRatio = charHeight / plateHeightPx;
|
||||||
float idealHeightRatio = (config->charHeightMM / config->plateHeightMM);
|
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)
|
//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 idealBottomDistance = charHeight * (BOTTOM_WHITESPACE_HEIGHT_MM / CHARACTER_HEIGHT_MM);
|
||||||
// float distScore = abs(topDistance - idealTopDistance) + abs(bottomDistance - idealBottomDistance);
|
// float distScore = abs(topDistance - idealTopDistance) + abs(bottomDistance - idealBottomDistance);
|
||||||
|
|
||||||
|
|
||||||
score += heightRatioDiff * SCORING_PLATEHEIGHT_WEIGHT;
|
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);
|
float charanglediff = abs(charAngle - top.angle) + abs(charAngle - bottom.angle);
|
||||||
|
|
||||||
|
|
||||||
score += charanglediff * SCORING_ANGLE_MATCHES_LPCHARS_WEIGHT;
|
score += charanglediff * SCORING_ANGLE_MATCHES_LPCHARS_WEIGHT;
|
||||||
|
|
||||||
// if (this->debug)
|
// if (this->debug)
|
||||||
// cout << "PlateCorners boxiness score: " << charanglediff * SCORING_ANGLE_MATCHES_LPCHARS_WEIGHT << endl;
|
// cout << "PlateCorners boxiness score: " << charanglediff * SCORING_ANGLE_MATCHES_LPCHARS_WEIGHT << endl;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (score < this->bestHorizontalScore)
|
if (score < this->bestHorizontalScore)
|
||||||
{
|
{
|
||||||
float scorecomponent;
|
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);
|
bestBottom = LineSegment(bottom.p1.x, bottom.p1.y, bottom.p2.x, bottom.p2.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
#include "platelines.h"
|
#include "platelines.h"
|
||||||
|
|
||||||
|
|
||||||
PlateLines::PlateLines(Config* config)
|
PlateLines::PlateLines(Config* config)
|
||||||
{
|
{
|
||||||
this->config = config;
|
this->config = config;
|
||||||
@@ -28,7 +27,6 @@ PlateLines::PlateLines(Config* config)
|
|||||||
if (debug)
|
if (debug)
|
||||||
cout << "PlateLines constructor" << endl;
|
cout << "PlateLines constructor" << endl;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PlateLines::~PlateLines()
|
PlateLines::~PlateLines()
|
||||||
@@ -36,22 +34,18 @@ PlateLines::~PlateLines()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void PlateLines::processImage(Mat inputImage, float sensitivity)
|
void PlateLines::processImage(Mat inputImage, float sensitivity)
|
||||||
{
|
{
|
||||||
if (this->debug)
|
if (this->debug)
|
||||||
cout << "PlateLines findLines" << endl;
|
cout << "PlateLines findLines" << endl;
|
||||||
|
|
||||||
|
|
||||||
timespec startTime;
|
timespec startTime;
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
|
|
||||||
|
|
||||||
Mat smoothed(inputImage.size(), inputImage.type());
|
Mat smoothed(inputImage.size(), inputImage.type());
|
||||||
inputImage.copyTo(smoothed);
|
inputImage.copyTo(smoothed);
|
||||||
int morph_elem = 2;
|
int morph_elem = 2;
|
||||||
int morph_size = 2;
|
int morph_size = 2;
|
||||||
Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
|
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 );
|
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 ) );
|
element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
|
||||||
morphologyEx( smoothed, smoothed, MORPH_OPEN, element );
|
morphologyEx( smoothed, smoothed, MORPH_OPEN, element );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Mat edges(inputImage.size(), inputImage.type());
|
Mat edges(inputImage.size(), inputImage.type());
|
||||||
Canny(smoothed, edges, 66, 133);
|
Canny(smoothed, edges, 66, 133);
|
||||||
|
|
||||||
|
|
||||||
vector<LineSegment> hlines = this->getLines(edges, sensitivity, false);
|
vector<LineSegment> hlines = this->getLines(edges, sensitivity, false);
|
||||||
vector<LineSegment> vlines = this->getLines(edges, sensitivity, true);
|
vector<LineSegment> vlines = this->getLines(edges, sensitivity, true);
|
||||||
for (int i = 0; i < hlines.size(); i++)
|
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++)
|
for (int i = 0; i < vlines.size(); i++)
|
||||||
this->verticalLines.push_back(vlines[i]);
|
this->verticalLines.push_back(vlines[i]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// if debug is enabled, draw the image
|
// if debug is enabled, draw the image
|
||||||
if (this->debug)
|
if (this->debug)
|
||||||
{
|
{
|
||||||
@@ -109,8 +97,6 @@ void PlateLines::processImage(Mat inputImage, float sensitivity)
|
|||||||
displayImage(config, "Hough Lines", dashboard);
|
displayImage(config, "Hough Lines", dashboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (config->debugTiming)
|
if (config->debugTiming)
|
||||||
{
|
{
|
||||||
timespec endTime;
|
timespec endTime;
|
||||||
@@ -119,7 +105,6 @@ void PlateLines::processImage(Mat inputImage, float sensitivity)
|
|||||||
}
|
}
|
||||||
//smoothed.release();
|
//smoothed.release();
|
||||||
|
|
||||||
|
|
||||||
//////////////// METHOD2!!!!!!!////////////////////
|
//////////////// METHOD2!!!!!!!////////////////////
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -127,33 +112,28 @@ void PlateLines::processImage(Mat inputImage, float sensitivity)
|
|||||||
Mat imgCanny;
|
Mat imgCanny;
|
||||||
GaussianBlur(inputImage, imgBlur, Size(9, 9), 1, 1);
|
GaussianBlur(inputImage, imgBlur, Size(9, 9), 1, 1);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Canny(imgBlur, imgCanny, 10, 30, 3);
|
Canny(imgBlur, imgCanny, 10, 30, 3);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//int morph_elem = 2;
|
//int morph_elem = 2;
|
||||||
//int morph_size = 1;
|
//int morph_size = 1;
|
||||||
//Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
|
//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 );
|
morphologyEx( imgCanny, imgCanny, MORPH_CLOSE, element );
|
||||||
|
|
||||||
|
|
||||||
Mat imgShaped;
|
Mat imgShaped;
|
||||||
imgCanny.copyTo(imgShaped);
|
imgCanny.copyTo(imgShaped);
|
||||||
//Find contours of possibles characters
|
//Find contours of possibles characters
|
||||||
vector< vector< Point> > biggestShapes;
|
vector< vector< Point> > biggestShapes;
|
||||||
findContours(imgShaped,
|
findContours(imgShaped,
|
||||||
biggestShapes, // a vector of contours
|
biggestShapes, // a vector of contours
|
||||||
CV_RETR_EXTERNAL, // retrieve the external contours
|
CV_RETR_EXTERNAL, // retrieve the external contours
|
||||||
CV_CHAIN_APPROX_SIMPLE ); // all pixels of each contours
|
CV_CHAIN_APPROX_SIMPLE ); // all pixels of each contours
|
||||||
|
|
||||||
// Draw blue contours on a white image
|
// Draw blue contours on a white image
|
||||||
//cvtColor(imgShaped, imgShaped, CV_GRAY2RGB);
|
//cvtColor(imgShaped, imgShaped, CV_GRAY2RGB);
|
||||||
cv::drawContours(imgShaped,biggestShapes,
|
cv::drawContours(imgShaped,biggestShapes,
|
||||||
-1, // draw all contours
|
-1, // draw all contours
|
||||||
cv::Scalar(255,255,255), // in blue
|
cv::Scalar(255,255,255), // in blue
|
||||||
1); // with a thickness of 1
|
1); // with a thickness of 1
|
||||||
|
|
||||||
displayImage(config, "Blurred", imgCanny);
|
displayImage(config, "Blurred", imgCanny);
|
||||||
displayImage(config, "Blurred Contours", imgShaped);
|
displayImage(config, "Blurred Contours", imgShaped);
|
||||||
@@ -187,7 +167,6 @@ vector<LineSegment> PlateLines::getLines(Mat edges, bool vertical)
|
|||||||
vector<double> errors;
|
vector<double> errors;
|
||||||
lswms.run(edges, lsegs, errors);
|
lswms.run(edges, lsegs, errors);
|
||||||
|
|
||||||
|
|
||||||
for( size_t i = 0; i < lsegs.size(); i++ )
|
for( size_t i = 0; i < lsegs.size(); i++ )
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -247,7 +226,6 @@ vector<LineSegment> PlateLines::getLines(Mat edges, bool vertical)
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
vector<LineSegment> PlateLines::getLines(Mat edges, float sensitivityMultiplier, bool vertical)
|
vector<LineSegment> PlateLines::getLines(Mat edges, float sensitivityMultiplier, bool vertical)
|
||||||
{
|
{
|
||||||
if (this->debug)
|
if (this->debug)
|
||||||
@@ -267,100 +245,92 @@ vector<LineSegment> PlateLines::getLines(Mat edges, float sensitivityMultiplier,
|
|||||||
|
|
||||||
HoughLines( edges, allLines, 1, CV_PI/180, sensitivity, 0, 0 );
|
HoughLines( edges, allLines, 1, CV_PI/180, sensitivity, 0, 0 );
|
||||||
|
|
||||||
|
|
||||||
for( size_t i = 0; i < allLines.size(); i++ )
|
for( size_t i = 0; i < allLines.size(); i++ )
|
||||||
{
|
{
|
||||||
float rho = allLines[i][0], theta = allLines[i][1];
|
float rho = allLines[i][0], theta = allLines[i][1];
|
||||||
Point pt1, pt2;
|
Point pt1, pt2;
|
||||||
double a = cos(theta), b = sin(theta);
|
double a = cos(theta), b = sin(theta);
|
||||||
double x0 = a*rho, y0 = b*rho;
|
double x0 = a*rho, y0 = b*rho;
|
||||||
|
|
||||||
double angle = theta * (180 / CV_PI);
|
double angle = theta * (180 / CV_PI);
|
||||||
pt1.x = cvRound(x0 + 1000*(-b));
|
pt1.x = cvRound(x0 + 1000*(-b));
|
||||||
pt1.y = cvRound(y0 + 1000*(a));
|
pt1.y = cvRound(y0 + 1000*(a));
|
||||||
pt2.x = cvRound(x0 - 1000*(-b));
|
pt2.x = cvRound(x0 - 1000*(-b));
|
||||||
pt2.y = cvRound(y0 - 1000*(a));
|
pt2.y = cvRound(y0 - 1000*(a));
|
||||||
|
|
||||||
if (vertical)
|
if (vertical)
|
||||||
{
|
{
|
||||||
if (angle < 20 || angle > 340 || (angle > 160 && angle < 210))
|
if (angle < 20 || angle > 340 || (angle > 160 && angle < 210))
|
||||||
{
|
{
|
||||||
// good vertical
|
// good vertical
|
||||||
|
|
||||||
LineSegment line;
|
LineSegment line;
|
||||||
if (pt1.y <= pt2.y)
|
if (pt1.y <= pt2.y)
|
||||||
line = LineSegment(pt2.x, pt2.y, pt1.x, pt1.y);
|
line = LineSegment(pt2.x, pt2.y, pt1.x, pt1.y);
|
||||||
else
|
else
|
||||||
line = LineSegment(pt1.x, pt1.y, pt2.x, pt2.y);
|
line = LineSegment(pt1.x, pt1.y, pt2.x, pt2.y);
|
||||||
|
|
||||||
// Get rid of the -1000, 1000 stuff. Terminate at the edges of the image
|
// Get rid of the -1000, 1000 stuff. Terminate at the edges of the image
|
||||||
// Helps with debugging/rounding issues later
|
// Helps with debugging/rounding issues later
|
||||||
LineSegment top(0, 0, edges.cols, 0);
|
LineSegment top(0, 0, edges.cols, 0);
|
||||||
LineSegment bottom(0, edges.rows, edges.cols, edges.rows);
|
LineSegment bottom(0, edges.rows, edges.cols, edges.rows);
|
||||||
Point p1 = line.intersection(bottom);
|
Point p1 = line.intersection(bottom);
|
||||||
Point p2 = line.intersection(top);
|
Point p2 = line.intersection(top);
|
||||||
filteredLines.push_back(LineSegment(p1.x, p1.y, p2.x, p2.y));
|
filteredLines.push_back(LineSegment(p1.x, p1.y, p2.x, p2.y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
if ( (angle > 70 && angle < 110) || (angle > 250 && angle < 290))
|
if ( (angle > 70 && angle < 110) || (angle > 250 && angle < 290))
|
||||||
{
|
{
|
||||||
// good horizontal
|
// good horizontal
|
||||||
|
|
||||||
LineSegment line;
|
LineSegment line;
|
||||||
if (pt1.x <= pt2.x)
|
if (pt1.x <= pt2.x)
|
||||||
line = LineSegment(pt1.x, pt1.y, pt2.x, pt2.y);
|
line = LineSegment(pt1.x, pt1.y, pt2.x, pt2.y);
|
||||||
else
|
else
|
||||||
line =LineSegment(pt2.x, pt2.y, pt1.x, pt1.y);
|
line =LineSegment(pt2.x, pt2.y, pt1.x, pt1.y);
|
||||||
|
|
||||||
// Get rid of the -1000, 1000 stuff. Terminate at the edges of the image
|
// Get rid of the -1000, 1000 stuff. Terminate at the edges of the image
|
||||||
// Helps with debugging/ rounding issues later
|
// Helps with debugging/ rounding issues later
|
||||||
int newY1 = line.getPointAt(0);
|
int newY1 = line.getPointAt(0);
|
||||||
int newY2 = line.getPointAt(edges.cols);
|
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;
|
return filteredLines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Mat PlateLines::customGrayscaleConversion(Mat src)
|
Mat PlateLines::customGrayscaleConversion(Mat src)
|
||||||
{
|
{
|
||||||
Mat img_hsv;
|
Mat img_hsv;
|
||||||
cvtColor(src,img_hsv,CV_BGR2HSV);
|
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 );
|
for (int row = 0; row < img_hsv.rows; row++)
|
||||||
Mat hue(img_hsv.size(), CV_8U );
|
{
|
||||||
|
for (int col = 0; col < img_hsv.cols; col++)
|
||||||
for (int row = 0; row < img_hsv.rows; row++)
|
|
||||||
{
|
{
|
||||||
for (int col = 0; col < img_hsv.cols; col++)
|
int h = (int) img_hsv.at<Vec3b>(row, col)[0];
|
||||||
{
|
int s = (int) img_hsv.at<Vec3b>(row, col)[1];
|
||||||
int h = (int) img_hsv.at<Vec3b>(row, col)[0];
|
int v = (int) img_hsv.at<Vec3b>(row, col)[2];
|
||||||
int s = (int) img_hsv.at<Vec3b>(row, col)[1];
|
|
||||||
int v = (int) img_hsv.at<Vec3b>(row, col)[2];
|
|
||||||
|
|
||||||
int pixval = pow(v, 1.05);
|
int pixval = pow(v, 1.05);
|
||||||
|
|
||||||
|
if (pixval > 255)
|
||||||
|
pixval = 255;
|
||||||
|
grayscale.at<uchar>(row, col) = pixval;
|
||||||
|
|
||||||
if (pixval > 255)
|
hue.at<uchar>(row, col) = h * (255.0 / 180.0);
|
||||||
pixval = 255;
|
|
||||||
grayscale.at<uchar>(row, col) = pixval;
|
|
||||||
|
|
||||||
hue.at<uchar>(row, col) = h * (255.0 / 180.0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//displayImage(config, "Hue", hue);
|
//displayImage(config, "Hue", hue);
|
||||||
return grayscale;
|
return grayscale;
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
#include "postprocess.h"
|
#include "postprocess.h"
|
||||||
|
|
||||||
|
|
||||||
PostProcess::PostProcess(Config* config)
|
PostProcess::PostProcess(Config* config)
|
||||||
{
|
{
|
||||||
this->config = config;
|
this->config = config;
|
||||||
@@ -29,12 +28,11 @@ PostProcess::PostProcess(Config* config)
|
|||||||
|
|
||||||
std::ifstream infile(filename.str().c_str());
|
std::ifstream infile(filename.str().c_str());
|
||||||
|
|
||||||
|
|
||||||
string region, pattern;
|
string region, pattern;
|
||||||
while (infile >> region >> pattern)
|
while (infile >> region >> pattern)
|
||||||
{
|
{
|
||||||
RegexRule* rule = new RegexRule(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())
|
if (rules.find(region) == rules.end())
|
||||||
{
|
{
|
||||||
@@ -61,16 +59,16 @@ PostProcess::~PostProcess()
|
|||||||
// TODO: Delete all entries in rules vector
|
// TODO: Delete all entries in rules vector
|
||||||
map<string, vector<RegexRule*> >::iterator iter;
|
map<string, vector<RegexRule*> >::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++)
|
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)
|
void PostProcess::addLetter(char letter, int charposition, float score)
|
||||||
{
|
{
|
||||||
if (score < config->postProcessMinConfidence)
|
if (score < config->postProcessMinConfidence)
|
||||||
@@ -80,8 +78,8 @@ void PostProcess::addLetter(char letter, int charposition, float score)
|
|||||||
|
|
||||||
if (score < config->postProcessConfidenceSkipLevel)
|
if (score < config->postProcessConfidenceSkipLevel)
|
||||||
{
|
{
|
||||||
float adjustedScore = abs(config->postProcessConfidenceSkipLevel - score) + config->postProcessMinConfidence;
|
float adjustedScore = abs(config->postProcessConfidenceSkipLevel - score) + config->postProcessMinConfidence;
|
||||||
insertLetter(SKIP_CHAR, charposition, adjustedScore );
|
insertLetter(SKIP_CHAR, charposition, adjustedScore );
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (letter == '0')
|
//if (letter == '0')
|
||||||
@@ -96,25 +94,24 @@ void PostProcess::insertLetter(char letter, int charposition, float score)
|
|||||||
|
|
||||||
score = score - config->postProcessMinConfidence;
|
score = score - config->postProcessMinConfidence;
|
||||||
|
|
||||||
|
|
||||||
int existingIndex = -1;
|
int existingIndex = -1;
|
||||||
if (letters.size() < charposition + 1)
|
if (letters.size() < charposition + 1)
|
||||||
{
|
{
|
||||||
for (int i = letters.size(); i < charposition + 1; i++)
|
for (int i = letters.size(); i < charposition + 1; i++)
|
||||||
{
|
{
|
||||||
vector<Letter> tmp;
|
vector<Letter> tmp;
|
||||||
letters.push_back(tmp);
|
letters.push_back(tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < letters[charposition].size(); i++)
|
for (int i = 0; i < letters[charposition].size(); i++)
|
||||||
{
|
{
|
||||||
if (letters[charposition][i].letter == letter &&
|
if (letters[charposition][i].letter == letter &&
|
||||||
letters[charposition][i].charposition == charposition)
|
letters[charposition][i].charposition == charposition)
|
||||||
{
|
{
|
||||||
existingIndex = i;
|
existingIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingIndex == -1)
|
if (existingIndex == -1)
|
||||||
@@ -128,18 +125,17 @@ void PostProcess::insertLetter(char letter, int charposition, float score)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
letters[charposition][existingIndex].occurences = letters[charposition][existingIndex].occurences + 1;
|
letters[charposition][existingIndex].occurences = letters[charposition][existingIndex].occurences + 1;
|
||||||
letters[charposition][existingIndex].totalscore = letters[charposition][existingIndex].totalscore + score;
|
letters[charposition][existingIndex].totalscore = letters[charposition][existingIndex].totalscore + score;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PostProcess::clear()
|
void PostProcess::clear()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < letters.size(); i++)
|
for (int i = 0; i < letters.size(); i++)
|
||||||
{
|
{
|
||||||
letters[i].clear();
|
letters[i].clear();
|
||||||
}
|
}
|
||||||
letters.resize(0);
|
letters.resize(0);
|
||||||
|
|
||||||
@@ -157,22 +153,18 @@ void PostProcess::analyze(string templateregion, int topn)
|
|||||||
timespec startTime;
|
timespec startTime;
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Get a list of missing positions
|
// Get a list of missing positions
|
||||||
for (int i = letters.size() -1; i >= 0; i--)
|
for (int i = letters.size() -1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if (letters[i].size() == 0)
|
if (letters[i].size() == 0)
|
||||||
{
|
{
|
||||||
unknownCharPositions.push_back(i);
|
unknownCharPositions.push_back(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (letters.size() == 0)
|
if (letters.size() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
// Sort the letters as they are
|
// Sort the letters as they are
|
||||||
for (int i = 0; i < letters.size(); i++)
|
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);
|
sort(letters[i].begin(), letters[i].end(), letterCompare);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (this->config->debugPostProcess)
|
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 i = 0; i < letters.size(); i++)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < letters[i].size(); j++)
|
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<Letter> tmp;
|
vector<Letter> tmp;
|
||||||
findAllPermutations(tmp, 0, config->postProcessMaxSubstitutions);
|
findAllPermutations(tmp, 0, config->postProcessMaxSubstitutions);
|
||||||
|
|
||||||
|
|
||||||
timespec sortStartTime;
|
timespec sortStartTime;
|
||||||
getTime(&sortStartTime);
|
getTime(&sortStartTime);
|
||||||
|
|
||||||
@@ -228,11 +217,8 @@ void PostProcess::analyze(string templateregion, int topn)
|
|||||||
cout << " -- PostProcess Sort Time: " << diffclock(sortStartTime, sortEndTime) << "ms." << endl;
|
cout << " -- PostProcess Sort Time: " << diffclock(sortStartTime, sortEndTime) << "ms." << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
matchesTemplate = false;
|
matchesTemplate = false;
|
||||||
|
|
||||||
|
|
||||||
if (templateregion != "")
|
if (templateregion != "")
|
||||||
{
|
{
|
||||||
vector<RegexRule*> regionRules = rules[templateregion];
|
vector<RegexRule*> regionRules = rules[templateregion];
|
||||||
@@ -241,22 +227,20 @@ void PostProcess::analyze(string templateregion, int topn)
|
|||||||
{
|
{
|
||||||
for (int j = 0; j < regionRules.size(); j++)
|
for (int j = 0; j < regionRules.size(); j++)
|
||||||
{
|
{
|
||||||
allPossibilities[i].matchesTemplate = regionRules[j]->match(allPossibilities[i].letters);
|
allPossibilities[i].matchesTemplate = regionRules[j]->match(allPossibilities[i].letters);
|
||||||
if (allPossibilities[i].matchesTemplate)
|
if (allPossibilities[i].matchesTemplate)
|
||||||
{
|
{
|
||||||
allPossibilities[i].letters = regionRules[j]->filterSkips(allPossibilities[i].letters);
|
allPossibilities[i].letters = regionRules[j]->filterSkips(allPossibilities[i].letters);
|
||||||
//bestChars = regionRules[j]->filterSkips(allPossibilities[i].letters);
|
//bestChars = regionRules[j]->filterSkips(allPossibilities[i].letters);
|
||||||
matchesTemplate = true;
|
matchesTemplate = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (i >= topn - 1)
|
if (i >= topn - 1)
|
||||||
break;
|
break;
|
||||||
//if (matchesTemplate || i >= TOP_N - 1)
|
//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++)
|
for (int z = 0; z < allPossibilities.size(); z++)
|
||||||
{
|
{
|
||||||
if (allPossibilities[z].matchesTemplate)
|
if (allPossibilities[z].matchesTemplate)
|
||||||
{
|
{
|
||||||
bestChars = allPossibilities[z].letters;
|
bestChars = allPossibilities[z].letters;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -276,7 +260,7 @@ void PostProcess::analyze(string templateregion, int topn)
|
|||||||
bestChars = allPossibilities[0].letters;
|
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)
|
if (allPossibilities.size() > 0)
|
||||||
{
|
{
|
||||||
float maxPercentScore = calculateMaxConfidenceScore();
|
float maxPercentScore = calculateMaxConfidenceScore();
|
||||||
@@ -289,28 +273,23 @@ void PostProcess::analyze(string templateregion, int topn)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (this->config->debugPostProcess)
|
if (this->config->debugPostProcess)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
// Print top words
|
// Print top words
|
||||||
for (int i = 0; i < allPossibilities.size(); i++)
|
for (int i = 0; i < allPossibilities.size(); i++)
|
||||||
{
|
{
|
||||||
cout << "Top " << topn << " Possibilities: " << allPossibilities[i].letters << " :\t" << allPossibilities[i].totalscore;
|
cout << "Top " << topn << " Possibilities: " << allPossibilities[i].letters << " :\t" << allPossibilities[i].totalscore;
|
||||||
if (allPossibilities[i].letters == bestChars)
|
if (allPossibilities[i].letters == bestChars)
|
||||||
cout << " <--- ";
|
cout << " <--- ";
|
||||||
cout << endl;
|
cout << endl;
|
||||||
|
|
||||||
if (i >= topn - 1)
|
if (i >= topn - 1)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cout << allPossibilities.size() << " total permutations" << endl;
|
cout << allPossibilities.size() << " total permutations" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (config->debugTiming)
|
if (config->debugTiming)
|
||||||
{
|
{
|
||||||
timespec endTime;
|
timespec endTime;
|
||||||
@@ -333,8 +312,8 @@ float PostProcess::calculateMaxConfidenceScore()
|
|||||||
{
|
{
|
||||||
if (letters[i].size() > 0)
|
if (letters[i].size() > 0)
|
||||||
{
|
{
|
||||||
totalScore += (letters[i][0].totalscore / letters[i][0].occurences) + config->postProcessMinConfidence;
|
totalScore += (letters[i][0].totalscore / letters[i][0].occurences) + config->postProcessMinConfidence;
|
||||||
numScores++;
|
numScores++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,19 +351,18 @@ vector<int> PostProcess::getMaxDepth(int topn)
|
|||||||
nextLeastDropCharPos = getNextLeastDrop(depth);
|
nextLeastDropCharPos = getNextLeastDrop(depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return depth;
|
return depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PostProcess::getPermutationCount(vector<int> depth)
|
int PostProcess::getPermutationCount(vector<int> depth)
|
||||||
{
|
{
|
||||||
int permutationCount = 1;
|
int permutationCount = 1;
|
||||||
for (int i = 0; i < depth.size(); i++)
|
for (int i = 0; i < depth.size(); i++)
|
||||||
{
|
{
|
||||||
permutationCount *= (depth[i] + 1);
|
permutationCount *= (depth[i] + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return permutationCount;
|
return permutationCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PostProcess::getNextLeastDrop(vector<int> depth)
|
int PostProcess::getNextLeastDrop(vector<int> depth)
|
||||||
@@ -392,21 +370,21 @@ int PostProcess::getNextLeastDrop(vector<int> depth)
|
|||||||
int nextLeastDropCharPos = -1;
|
int nextLeastDropCharPos = -1;
|
||||||
float leastNextDrop = 99999999999;
|
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())
|
nextLeastDropCharPos = i;
|
||||||
continue;
|
leastNextDrop = drop;
|
||||||
|
|
||||||
float drop = letters[i][depth[i]].totalscore - letters[i][depth[i]+1].totalscore;
|
|
||||||
|
|
||||||
if (drop < leastNextDrop)
|
|
||||||
{
|
|
||||||
nextLeastDropCharPos = i;
|
|
||||||
leastNextDrop = drop;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nextLeastDropCharPos;
|
return nextLeastDropCharPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
const vector<PPResult> PostProcess::getResults()
|
const vector<PPResult> PostProcess::getResults()
|
||||||
@@ -426,23 +404,23 @@ void PostProcess::findAllPermutations(vector<Letter> prevletters, int charPos, i
|
|||||||
|
|
||||||
if (charPos == letters.size() - 1)
|
if (charPos == letters.size() - 1)
|
||||||
{
|
{
|
||||||
// Last letter, add the word
|
// Last letter, add the word
|
||||||
PPResult possibility;
|
PPResult possibility;
|
||||||
possibility.letters = "";
|
possibility.letters = "";
|
||||||
possibility.totalscore = 0;
|
possibility.totalscore = 0;
|
||||||
possibility.matchesTemplate = false;
|
possibility.matchesTemplate = false;
|
||||||
for (int z = 0; z < prevletters.size(); z++)
|
for (int z = 0; z < prevletters.size(); z++)
|
||||||
{
|
{
|
||||||
if (prevletters[z].letter != SKIP_CHAR)
|
if (prevletters[z].letter != SKIP_CHAR)
|
||||||
possibility.letters = possibility.letters + prevletters[z].letter;
|
possibility.letters = possibility.letters + prevletters[z].letter;
|
||||||
possibility.totalscore = possibility.totalscore + prevletters[z].totalscore;
|
possibility.totalscore = possibility.totalscore + prevletters[z].totalscore;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (letters[charPos][i].letter != SKIP_CHAR)
|
if (letters[charPos][i].letter != SKIP_CHAR)
|
||||||
possibility.letters = possibility.letters + letters[charPos][i].letter;
|
possibility.letters = possibility.letters + letters[charPos][i].letter;
|
||||||
possibility.totalscore = possibility.totalscore +letters[charPos][i].totalscore;
|
possibility.totalscore = possibility.totalscore +letters[charPos][i].totalscore;
|
||||||
|
|
||||||
allPossibilities.push_back(possibility);
|
allPossibilities.push_back(possibility);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -450,9 +428,9 @@ void PostProcess::findAllPermutations(vector<Letter> prevletters, int charPos, i
|
|||||||
|
|
||||||
float scorePercentDiff = abs( letters[charPos][0].totalscore - letters[charPos][i].totalscore ) / letters[charPos][0].totalscore;
|
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 )
|
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
|
else
|
||||||
findAllPermutations(prevletters, charPos + 1, substitutionsLeft);
|
findAllPermutations(prevletters, charPos + 1, substitutionsLeft);
|
||||||
|
|
||||||
prevletters.pop_back();
|
prevletters.pop_back();
|
||||||
}
|
}
|
||||||
@@ -460,19 +438,15 @@ void PostProcess::findAllPermutations(vector<Letter> prevletters, int charPos, i
|
|||||||
|
|
||||||
if (letters[charPos].size() == 0)
|
if (letters[charPos].size() == 0)
|
||||||
{
|
{
|
||||||
// No letters for this char position...
|
// No letters for this char position...
|
||||||
// Just pass it along
|
// Just pass it along
|
||||||
findAllPermutations(prevletters, charPos + 1, substitutionsLeft);
|
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)
|
if (left.totalscore < right.totalscore)
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
@@ -486,7 +460,6 @@ bool letterCompare( const Letter &left, const Letter &right )
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RegexRule::RegexRule(string region, string pattern)
|
RegexRule::RegexRule(string region, string pattern)
|
||||||
{
|
{
|
||||||
this->original = pattern;
|
this->original = pattern;
|
||||||
@@ -495,31 +468,31 @@ RegexRule::RegexRule(string region, string pattern)
|
|||||||
numchars = 0;
|
numchars = 0;
|
||||||
for (int i = 0; i < pattern.size(); i++)
|
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 + pattern.at(i);
|
}
|
||||||
i++;
|
this->regex = this->regex + ']';
|
||||||
}
|
|
||||||
this->regex = this->regex + ']';
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (pattern.at(i) == '?')
|
else if (pattern.at(i) == '?')
|
||||||
{
|
{
|
||||||
this->regex = this->regex + '.';
|
this->regex = this->regex + '.';
|
||||||
this->skipPositions.push_back(numchars);
|
this->skipPositions.push_back(numchars);
|
||||||
}
|
}
|
||||||
else if (pattern.at(i) == '@')
|
else if (pattern.at(i) == '@')
|
||||||
{
|
{
|
||||||
this->regex = this->regex + "\\a";
|
this->regex = this->regex + "\\a";
|
||||||
}
|
}
|
||||||
else if (pattern.at(i) == '#')
|
else if (pattern.at(i) == '#')
|
||||||
{
|
{
|
||||||
this->regex = this->regex + "\\d";
|
this->regex = this->regex + "\\d";
|
||||||
}
|
}
|
||||||
|
|
||||||
numchars++;
|
numchars++;
|
||||||
}
|
}
|
||||||
|
|
||||||
trexp.Compile(this->regex.c_str());
|
trexp.Compile(this->regex.c_str());
|
||||||
@@ -529,7 +502,6 @@ RegexRule::RegexRule(string region, string pattern)
|
|||||||
// cout << "AA Skip position: " << skipPositions[z] << endl;
|
// cout << "AA Skip position: " << skipPositions[z] << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool RegexRule::match(string text)
|
bool RegexRule::match(string text)
|
||||||
{
|
{
|
||||||
if (text.length() != numchars)
|
if (text.length() != numchars)
|
||||||
@@ -544,17 +516,17 @@ string RegexRule::filterSkips(string text)
|
|||||||
for (int i = 0; i < text.size(); i++)
|
for (int i = 0; i < text.size(); i++)
|
||||||
{
|
{
|
||||||
bool skip = false;
|
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)
|
if (skip == false)
|
||||||
response = response + text[i];
|
response = response + text[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
@@ -17,39 +17,33 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "regiondetector.h"
|
#include "regiondetector.h"
|
||||||
|
|
||||||
|
|
||||||
RegionDetector::RegionDetector(Config* config)
|
RegionDetector::RegionDetector(Config* config)
|
||||||
{
|
{
|
||||||
this->config = config;
|
this->config = config;
|
||||||
// Don't scale. Can change this in the future (i.e., maximum resolution preference, or some such).
|
// Don't scale. Can change this in the future (i.e., maximum resolution preference, or some such).
|
||||||
this->scale_factor = 1.0f;
|
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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;
|
delete this->plate_cascade;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool RegionDetector::isLoaded()
|
bool RegionDetector::isLoaded()
|
||||||
{
|
{
|
||||||
return this->loaded;
|
return this->loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
vector<Rect> RegionDetector::detect(Mat frame)
|
vector<Rect> RegionDetector::detect(Mat frame)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -77,7 +68,6 @@ vector<Rect> RegionDetector::detect(Mat frame)
|
|||||||
return regionsOfInterest;
|
return regionsOfInterest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** @function detectAndDisplay */
|
/** @function detectAndDisplay */
|
||||||
vector<Rect> RegionDetector::doCascade(Mat frame)
|
vector<Rect> RegionDetector::doCascade(Mat frame)
|
||||||
{
|
{
|
||||||
@@ -91,44 +81,41 @@ vector<Rect> RegionDetector::doCascade(Mat frame)
|
|||||||
resize(frame, frame, Size(w * this->scale_factor, h * this->scale_factor));
|
resize(frame, frame, Size(w * this->scale_factor, h * this->scale_factor));
|
||||||
|
|
||||||
//-- Detect plates
|
//-- Detect plates
|
||||||
timespec startTime;
|
timespec startTime;
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
|
|
||||||
Size minSize(config->minPlateSizeWidthPx * this->scale_factor, config->minPlateSizeHeightPx * 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);
|
Size maxSize(w * config->maxPlateWidthPercent * this->scale_factor, h * config->maxPlateHeightPercent * this->scale_factor);
|
||||||
|
|
||||||
if (config->opencl_enabled)
|
if (config->opencl_enabled)
|
||||||
{
|
{
|
||||||
ocl::oclMat openclFrame(frame);
|
ocl::oclMat openclFrame(frame);
|
||||||
((ocl::OclCascadeClassifier*) plate_cascade)->detectMultiScale(openclFrame, plates, 1.1, 3, 0, minSize, maxSize);
|
((ocl::OclCascadeClassifier*) plate_cascade)->detectMultiScale(openclFrame, plates, 1.1, 3, 0, minSize, maxSize);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
plate_cascade->detectMultiScale( frame, plates, 1.1, 3,
|
plate_cascade->detectMultiScale( frame, plates, 1.1, 3,
|
||||||
0,
|
0,
|
||||||
//0|CV_HAAR_SCALE_IMAGE,
|
//0|CV_HAAR_SCALE_IMAGE,
|
||||||
minSize, maxSize );
|
minSize, maxSize );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config->debugTiming)
|
||||||
|
{
|
||||||
|
timespec endTime;
|
||||||
|
getTime(&endTime);
|
||||||
|
cout << "LBP Time: " << diffclock(startTime, endTime) << "ms." << endl;
|
||||||
|
}
|
||||||
|
|
||||||
if (config->debugTiming)
|
for( int i = 0; i < plates.size(); i++ )
|
||||||
{
|
{
|
||||||
timespec endTime;
|
plates[i].x = plates[i].x / scale_factor;
|
||||||
getTime(&endTime);
|
plates[i].y = plates[i].y / scale_factor;
|
||||||
cout << "LBP Time: " << diffclock(startTime, endTime) << "ms." << endl;
|
plates[i].width = plates[i].width / scale_factor;
|
||||||
}
|
plates[i].height = plates[i].height / scale_factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return plates;
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -19,105 +19,106 @@
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
snippets(
|
snippets(
|
||||||
const char * a_pszFile,
|
const char * a_pszFile,
|
||||||
bool a_bIsUtf8,
|
bool a_bIsUtf8,
|
||||||
bool a_bUseMultiKey,
|
bool a_bUseMultiKey,
|
||||||
bool a_bUseMultiLine
|
bool a_bUseMultiLine
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// LOADING DATA
|
// LOADING DATA
|
||||||
|
|
||||||
// load from a data file
|
// load from a data file
|
||||||
CSimpleIniA ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine);
|
CSimpleIniA ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine);
|
||||||
SI_Error rc = ini.LoadFile(a_pszFile);
|
SI_Error rc = ini.LoadFile(a_pszFile);
|
||||||
if (rc < 0) return false;
|
if (rc < 0) return false;
|
||||||
|
|
||||||
// load from a string
|
// load from a string
|
||||||
std::string strData;
|
std::string strData;
|
||||||
rc = ini.LoadData(strData.c_str(), strData.size());
|
rc = ini.LoadData(strData.c_str(), strData.size());
|
||||||
if (rc < 0) return false;
|
if (rc < 0) return false;
|
||||||
|
|
||||||
// GETTING SECTIONS AND KEYS
|
// GETTING SECTIONS AND KEYS
|
||||||
|
|
||||||
// get all sections
|
// get all sections
|
||||||
CSimpleIniA::TNamesDepend sections;
|
CSimpleIniA::TNamesDepend sections;
|
||||||
ini.GetAllSections(sections);
|
ini.GetAllSections(sections);
|
||||||
|
|
||||||
// get all keys in a section
|
// get all keys in a section
|
||||||
CSimpleIniA::TNamesDepend keys;
|
CSimpleIniA::TNamesDepend keys;
|
||||||
ini.GetAllKeys("section-name", keys);
|
ini.GetAllKeys("section-name", keys);
|
||||||
|
|
||||||
// GETTING VALUES
|
// GETTING VALUES
|
||||||
|
|
||||||
// get the value of a key
|
// get the value of a key
|
||||||
const char * pszValue = ini.GetValue("section-name",
|
const char * pszValue = ini.GetValue("section-name",
|
||||||
"key-name", NULL /*default*/);
|
"key-name", NULL /*default*/);
|
||||||
|
|
||||||
// get the value of a key which may have multiple
|
// get the value of a key which may have multiple
|
||||||
// values. If bHasMultipleValues is true, then just
|
// values. If bHasMultipleValues is true, then just
|
||||||
// one value has been returned
|
// one value has been returned
|
||||||
bool bHasMultipleValues;
|
bool bHasMultipleValues;
|
||||||
pszValue = ini.GetValue("section-name", "key-name",
|
pszValue = ini.GetValue("section-name", "key-name",
|
||||||
NULL /*default*/, &bHasMultipleValues);
|
NULL /*default*/, &bHasMultipleValues);
|
||||||
|
|
||||||
// get all values of a key with multiple values
|
// get all values of a key with multiple values
|
||||||
CSimpleIniA::TNamesDepend values;
|
CSimpleIniA::TNamesDepend values;
|
||||||
ini.GetAllValues("section-name", "key-name", 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
|
#if defined(_MSC_VER) && _MSC_VER <= 1200
|
||||||
/** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */
|
/** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */
|
||||||
values.sort();
|
values.sort();
|
||||||
#else
|
#else
|
||||||
values.sort(CSimpleIniA::Entry::LoadOrder());
|
values.sort(CSimpleIniA::Entry::LoadOrder());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// output all of the items
|
// output all of the items
|
||||||
CSimpleIniA::TNamesDepend::const_iterator i;
|
CSimpleIniA::TNamesDepend::const_iterator i;
|
||||||
for (i = values.begin(); i != values.end(); ++i) {
|
for (i = values.begin(); i != values.end(); ++i)
|
||||||
printf("key-name = '%s'\n", i->pItem);
|
{
|
||||||
}
|
printf("key-name = '%s'\n", i->pItem);
|
||||||
|
}
|
||||||
|
|
||||||
// MODIFYING DATA
|
// MODIFYING DATA
|
||||||
|
|
||||||
// adding a new section
|
// adding a new section
|
||||||
rc = ini.SetValue("new-section", NULL, NULL);
|
rc = ini.SetValue("new-section", NULL, NULL);
|
||||||
if (rc < 0) return false;
|
if (rc < 0) return false;
|
||||||
printf("section: %s\n", rc == SI_INSERTED ?
|
printf("section: %s\n", rc == SI_INSERTED ?
|
||||||
"inserted" : "updated");
|
"inserted" : "updated");
|
||||||
|
|
||||||
// adding a new key ("new-section" will be added
|
// adding a new key ("new-section" will be added
|
||||||
// automatically if it doesn't already exist.
|
// automatically if it doesn't already exist.
|
||||||
rc = ini.SetValue("new-section", "new-key", "value");
|
rc = ini.SetValue("new-section", "new-key", "value");
|
||||||
if (rc < 0) return false;
|
if (rc < 0) return false;
|
||||||
printf("key: %s\n", rc == SI_INSERTED ?
|
printf("key: %s\n", rc == SI_INSERTED ?
|
||||||
"inserted" : "updated");
|
"inserted" : "updated");
|
||||||
|
|
||||||
// changing the value of a key
|
// changing the value of a key
|
||||||
rc = ini.SetValue("section", "key", "updated-value");
|
rc = ini.SetValue("section", "key", "updated-value");
|
||||||
if (rc < 0) return false;
|
if (rc < 0) return false;
|
||||||
printf("key: %s\n", rc == SI_INSERTED ?
|
printf("key: %s\n", rc == SI_INSERTED ?
|
||||||
"inserted" : "updated");
|
"inserted" : "updated");
|
||||||
|
|
||||||
// DELETING DATA
|
// DELETING DATA
|
||||||
|
|
||||||
// deleting a key from a section. Optionally the entire
|
// deleting a key from a section. Optionally the entire
|
||||||
// section may be deleted if it is now empty.
|
// section may be deleted if it is now empty.
|
||||||
ini.Delete("section-name", "key-name",
|
ini.Delete("section-name", "key-name",
|
||||||
true /*delete the section if empty*/);
|
true /*delete the section if empty*/);
|
||||||
|
|
||||||
// deleting an entire section and all keys in it
|
// deleting an entire section and all keys in it
|
||||||
ini.Delete("section-name", NULL);
|
ini.Delete("section-name", NULL);
|
||||||
|
|
||||||
// SAVING DATA
|
// SAVING DATA
|
||||||
|
|
||||||
// save the data to a string
|
// save the data to a string
|
||||||
rc = ini.Save(strData);
|
rc = ini.Save(strData);
|
||||||
if (rc < 0) return false;
|
if (rc < 0) return false;
|
||||||
|
|
||||||
// save the data back to the file
|
// save the data back to the file
|
||||||
rc = ini.SaveFile(a_pszFile);
|
rc = ini.SaveFile(a_pszFile);
|
||||||
if (rc < 0) return false;
|
if (rc < 0) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -19,20 +19,19 @@
|
|||||||
|
|
||||||
#include "stateidentifier.h"
|
#include "stateidentifier.h"
|
||||||
|
|
||||||
|
|
||||||
StateIdentifier::StateIdentifier(Config* config)
|
StateIdentifier::StateIdentifier(Config* config)
|
||||||
{
|
{
|
||||||
this->config = config;
|
this->config = config;
|
||||||
|
|
||||||
featureMatcher = new FeatureMatcher(config);
|
featureMatcher = new FeatureMatcher(config);
|
||||||
|
|
||||||
if (featureMatcher->isLoaded() == false)
|
if (featureMatcher->isLoaded() == false)
|
||||||
{
|
{
|
||||||
cout << "Can not create detector or descriptor extractor or descriptor matcher of given types" << endl;
|
cout << "Can not create detector or descriptor extractor or descriptor matcher of given types" << endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
featureMatcher->loadRecognitionSet(config->country);
|
featureMatcher->loadRecognitionSet(config->country);
|
||||||
}
|
}
|
||||||
|
|
||||||
StateIdentifier::~StateIdentifier()
|
StateIdentifier::~StateIdentifier()
|
||||||
@@ -66,19 +65,16 @@ int StateIdentifier::recognize(Mat img, char* stateCode)
|
|||||||
plateImg.copyTo(debugImg);
|
plateImg.copyTo(debugImg);
|
||||||
vector<int> matchesArray(featureMatcher->numTrainingElements());
|
vector<int> matchesArray(featureMatcher->numTrainingElements());
|
||||||
|
|
||||||
|
|
||||||
RecognitionResult result = featureMatcher->recognize(plateImg, true, &debugImg, true, matchesArray );
|
RecognitionResult result = featureMatcher->recognize(plateImg, true, &debugImg, true, matchesArray );
|
||||||
|
|
||||||
if (this->config->debugStateId)
|
if (this->config->debugStateId)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
displayImage(config, "State Identifier1", plateImg);
|
displayImage(config, "State Identifier1", plateImg);
|
||||||
displayImage(config, "State Identifier", debugImg);
|
displayImage(config, "State Identifier", debugImg);
|
||||||
cout << result.haswinner << " : " << result.confidence << " : " << result.winner << endl;
|
cout << result.haswinner << " : " << result.confidence << " : " << result.winner << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (config->debugTiming)
|
if (config->debugTiming)
|
||||||
{
|
{
|
||||||
timespec endTime;
|
timespec endTime;
|
||||||
@@ -86,12 +82,10 @@ int StateIdentifier::recognize(Mat img, char* stateCode)
|
|||||||
cout << "State Identification Time: " << diffclock(startTime, endTime) << "ms." << endl;
|
cout << "State Identification Time: " << diffclock(startTime, endTime) << "ms." << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (result.haswinner == false)
|
if (result.haswinner == false)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
strcpy(stateCode, result.winner.c_str());
|
strcpy(stateCode, result.winner.c_str());
|
||||||
|
|
||||||
|
|
||||||
return result.confidence;
|
return result.confidence;
|
||||||
}
|
}
|
||||||
|
@@ -1,43 +1,44 @@
|
|||||||
#include "filesystem.h"
|
#include "filesystem.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool hasEnding (std::string const &fullString, std::string const &ending)
|
bool hasEnding (std::string const &fullString, std::string const &ending)
|
||||||
{
|
{
|
||||||
if (fullString.length() >= ending.length()) {
|
if (fullString.length() >= ending.length())
|
||||||
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
|
{
|
||||||
} else {
|
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
|
||||||
return false;
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DirectoryExists( const char* pzPath )
|
bool DirectoryExists( const char* pzPath )
|
||||||
{
|
{
|
||||||
if ( pzPath == NULL) return false;
|
if ( pzPath == NULL) return false;
|
||||||
|
|
||||||
DIR *pDir;
|
DIR *pDir;
|
||||||
bool bExists = false;
|
bool bExists = false;
|
||||||
|
|
||||||
pDir = opendir (pzPath);
|
pDir = opendir (pzPath);
|
||||||
|
|
||||||
if (pDir != NULL)
|
if (pDir != NULL)
|
||||||
{
|
{
|
||||||
bExists = true;
|
bExists = true;
|
||||||
(void) closedir (pDir);
|
(void) closedir (pDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
return bExists;
|
return bExists;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fileExists( const char* pzPath )
|
bool fileExists( const char* pzPath )
|
||||||
{
|
{
|
||||||
if (pzPath == NULL) return false;
|
if (pzPath == NULL) return false;
|
||||||
|
|
||||||
bool fExists = false;
|
bool fExists = false;
|
||||||
std::ifstream f(pzPath);
|
std::ifstream f(pzPath);
|
||||||
fExists = f.is_open();
|
fExists = f.is_open();
|
||||||
f.close();
|
f.close();
|
||||||
return fExists;
|
return fExists;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> getFilesInDir(const char* dirPath)
|
std::vector<std::string> getFilesInDir(const char* dirPath)
|
||||||
@@ -47,14 +48,18 @@ std::vector<std::string> getFilesInDir(const char* dirPath)
|
|||||||
std::vector<std::string> files;
|
std::vector<std::string> files;
|
||||||
|
|
||||||
struct dirent *ent;
|
struct dirent *ent;
|
||||||
if ((dir = opendir (dirPath)) != NULL) {
|
if ((dir = opendir (dirPath)) != NULL)
|
||||||
|
{
|
||||||
/* print all the files and directories within directory */
|
/* 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)
|
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);
|
closedir (dir);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
/* could not open directory */
|
/* could not open directory */
|
||||||
perror ("");
|
perror ("");
|
||||||
return files;
|
return files;
|
||||||
@@ -63,14 +68,14 @@ std::vector<std::string> getFilesInDir(const char* dirPath)
|
|||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool stringCompare( const std::string &left, const std::string &right )
|
||||||
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 )
|
for( std::string::const_iterator lit = left.begin(), rit = right.begin(); lit != left.end() && rit != right.end(); ++lit, ++rit )
|
||||||
if( tolower( *lit ) < tolower( *rit ) )
|
if( tolower( *lit ) < tolower( *rit ) )
|
||||||
return true;
|
|
||||||
else if( tolower( *lit ) > tolower( *rit ) )
|
|
||||||
return false;
|
|
||||||
if( left.size() < right.size() )
|
|
||||||
return true;
|
return true;
|
||||||
return false;
|
else if( tolower( *lit ) > tolower( *rit ) )
|
||||||
|
return false;
|
||||||
|
if( left.size() < right.size() )
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -1,143 +1,144 @@
|
|||||||
#include "timing.h"
|
#include "timing.h"
|
||||||
|
|
||||||
|
|
||||||
timespec diff(timespec start, timespec end);
|
timespec diff(timespec start, timespec end);
|
||||||
|
|
||||||
|
|
||||||
#ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
|
|
||||||
// Windows timing code
|
// Windows timing code
|
||||||
LARGE_INTEGER getFILETIMEoffset()
|
LARGE_INTEGER getFILETIMEoffset()
|
||||||
{
|
{
|
||||||
SYSTEMTIME s;
|
SYSTEMTIME s;
|
||||||
FILETIME f;
|
FILETIME f;
|
||||||
LARGE_INTEGER t;
|
LARGE_INTEGER t;
|
||||||
|
|
||||||
s.wYear = 1970;
|
s.wYear = 1970;
|
||||||
s.wMonth = 1;
|
s.wMonth = 1;
|
||||||
s.wDay = 1;
|
s.wDay = 1;
|
||||||
s.wHour = 0;
|
s.wHour = 0;
|
||||||
s.wMinute = 0;
|
s.wMinute = 0;
|
||||||
s.wSecond = 0;
|
s.wSecond = 0;
|
||||||
s.wMilliseconds = 0;
|
s.wMilliseconds = 0;
|
||||||
SystemTimeToFileTime(&s, &f);
|
SystemTimeToFileTime(&s, &f);
|
||||||
t.QuadPart = f.dwHighDateTime;
|
t.QuadPart = f.dwHighDateTime;
|
||||||
t.QuadPart <<= 32;
|
t.QuadPart <<= 32;
|
||||||
t.QuadPart |= f.dwLowDateTime;
|
t.QuadPart |= f.dwLowDateTime;
|
||||||
return (t);
|
return (t);
|
||||||
}
|
}
|
||||||
|
|
||||||
int clock_gettime(int X, timespec *tv)
|
int clock_gettime(int X, timespec *tv)
|
||||||
{
|
{
|
||||||
LARGE_INTEGER t;
|
LARGE_INTEGER t;
|
||||||
FILETIME f;
|
FILETIME f;
|
||||||
double microseconds;
|
double microseconds;
|
||||||
static LARGE_INTEGER offset;
|
static LARGE_INTEGER offset;
|
||||||
static double frequencyToMicroseconds;
|
static double frequencyToMicroseconds;
|
||||||
static int initialized = 0;
|
static int initialized = 0;
|
||||||
static BOOL usePerformanceCounter = 0;
|
static BOOL usePerformanceCounter = 0;
|
||||||
|
|
||||||
if (!initialized) {
|
if (!initialized)
|
||||||
LARGE_INTEGER performanceFrequency;
|
{
|
||||||
initialized = 1;
|
LARGE_INTEGER performanceFrequency;
|
||||||
usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency);
|
initialized = 1;
|
||||||
if (usePerformanceCounter) {
|
usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency);
|
||||||
QueryPerformanceCounter(&offset);
|
if (usePerformanceCounter)
|
||||||
frequencyToMicroseconds = (double)performanceFrequency.QuadPart / 1000000.;
|
{
|
||||||
} else {
|
QueryPerformanceCounter(&offset);
|
||||||
offset = getFILETIMEoffset();
|
frequencyToMicroseconds = (double)performanceFrequency.QuadPart / 1000000.;
|
||||||
frequencyToMicroseconds = 10.;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (usePerformanceCounter) QueryPerformanceCounter(&t);
|
else
|
||||||
else {
|
{
|
||||||
GetSystemTimeAsFileTime(&f);
|
offset = getFILETIMEoffset();
|
||||||
t.QuadPart = f.dwHighDateTime;
|
frequencyToMicroseconds = 10.;
|
||||||
t.QuadPart <<= 32;
|
|
||||||
t.QuadPart |= f.dwLowDateTime;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (usePerformanceCounter) QueryPerformanceCounter(&t);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GetSystemTimeAsFileTime(&f);
|
||||||
|
t.QuadPart = f.dwHighDateTime;
|
||||||
|
t.QuadPart <<= 32;
|
||||||
|
t.QuadPart |= f.dwLowDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
t.QuadPart -= offset.QuadPart;
|
t.QuadPart -= offset.QuadPart;
|
||||||
microseconds = (double)t.QuadPart / frequencyToMicroseconds;
|
microseconds = (double)t.QuadPart / frequencyToMicroseconds;
|
||||||
t.QuadPart = microseconds;
|
t.QuadPart = microseconds;
|
||||||
tv->tv_sec = t.QuadPart / 1000000;
|
tv->tv_sec = t.QuadPart / 1000000;
|
||||||
tv->tv_usec = t.QuadPart % 1000000;
|
tv->tv_usec = t.QuadPart % 1000000;
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void getTime(timespec* time)
|
void getTime(timespec* time)
|
||||||
{
|
{
|
||||||
clock_gettime(0, time);
|
clock_gettime(0, time);
|
||||||
|
|
||||||
}
|
}
|
||||||
double diffclock(timespec time1,timespec time2)
|
double diffclock(timespec time1,timespec time2)
|
||||||
{
|
{
|
||||||
timespec delta = diff(time1,time2);
|
timespec delta = diff(time1,time2);
|
||||||
double milliseconds = (delta.tv_sec * 1000) + (((double) delta.tv_usec) / 1000.0);
|
double milliseconds = (delta.tv_sec * 1000) + (((double) delta.tv_usec) / 1000.0);
|
||||||
|
|
||||||
|
return milliseconds;
|
||||||
return milliseconds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
timespec diff(timespec start, timespec end)
|
timespec diff(timespec start, timespec end)
|
||||||
{
|
{
|
||||||
timespec temp;
|
timespec temp;
|
||||||
if ((end.tv_usec-start.tv_usec)<0) {
|
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;
|
temp.tv_sec = end.tv_sec-start.tv_sec-1;
|
||||||
} else {
|
temp.tv_usec = 1000000+end.tv_usec-start.tv_usec;
|
||||||
temp.tv_sec = end.tv_sec-start.tv_sec;
|
}
|
||||||
temp.tv_usec = end.tv_usec-start.tv_usec;
|
else
|
||||||
}
|
{
|
||||||
return temp;
|
temp.tv_sec = end.tv_sec-start.tv_sec;
|
||||||
|
temp.tv_usec = end.tv_usec-start.tv_usec;
|
||||||
|
}
|
||||||
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void getTime(timespec* time)
|
void getTime(timespec* time)
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time
|
#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time
|
||||||
clock_serv_t cclock;
|
clock_serv_t cclock;
|
||||||
mach_timespec_t mts;
|
mach_timespec_t mts;
|
||||||
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
|
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
|
||||||
clock_get_time(cclock, &mts);
|
clock_get_time(cclock, &mts);
|
||||||
mach_port_deallocate(mach_task_self(), cclock);
|
mach_port_deallocate(mach_task_self(), cclock);
|
||||||
time->tv_sec = mts.tv_sec;
|
time->tv_sec = mts.tv_sec;
|
||||||
time->tv_nsec = mts.tv_nsec;
|
time->tv_nsec = mts.tv_nsec;
|
||||||
#else
|
#else
|
||||||
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, time);
|
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, time);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
double diffclock(timespec time1,timespec time2)
|
double diffclock(timespec time1,timespec time2)
|
||||||
{
|
{
|
||||||
|
|
||||||
timespec delta = diff(time1,time2);
|
timespec delta = diff(time1,time2);
|
||||||
double milliseconds = (delta.tv_sec * 1000) + (((double) delta.tv_nsec) / 1000000.0);
|
double milliseconds = (delta.tv_sec * 1000) + (((double) delta.tv_nsec) / 1000000.0);
|
||||||
|
|
||||||
|
|
||||||
return milliseconds;
|
|
||||||
|
|
||||||
|
return milliseconds;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
timespec diff(timespec start, timespec end)
|
timespec diff(timespec start, timespec end)
|
||||||
{
|
{
|
||||||
timespec temp;
|
timespec temp;
|
||||||
if ((end.tv_nsec-start.tv_nsec)<0) {
|
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;
|
temp.tv_sec = end.tv_sec-start.tv_sec-1;
|
||||||
} else {
|
temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
|
||||||
temp.tv_sec = end.tv_sec-start.tv_sec;
|
}
|
||||||
temp.tv_nsec = end.tv_nsec-start.tv_nsec;
|
else
|
||||||
}
|
{
|
||||||
return temp;
|
temp.tv_sec = end.tv_sec-start.tv_sec;
|
||||||
|
temp.tv_nsec = end.tv_nsec-start.tv_nsec;
|
||||||
|
}
|
||||||
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -17,12 +17,8 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
|
|
||||||
|
|
||||||
Rect expandRect(Rect original, int expandXPixels, int expandYPixels, int maxX, int maxY)
|
Rect expandRect(Rect original, int expandXPixels, int expandYPixels, int maxX, int maxY)
|
||||||
{
|
{
|
||||||
Rect expandedRegion = Rect(original);
|
Rect expandedRegion = Rect(original);
|
||||||
@@ -48,22 +44,22 @@ Rect expandRect(Rect original, int expandXPixels, int expandYPixels, int maxX, i
|
|||||||
|
|
||||||
Mat drawImageDashboard(vector<Mat> images, int imageType, int numColumns)
|
Mat drawImageDashboard(vector<Mat> 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++)
|
for (int i = 0; i < numColumns * numRows; i++)
|
||||||
{
|
{
|
||||||
if (i < images.size())
|
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)));
|
images[i].copyTo(dashboard(Rect((i%numColumns) * images[i].cols, floor((float) i/numColumns) * images[i].rows, images[i].cols, images[i].rows)));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Mat black = Mat::zeros(images[0].size(), imageType);
|
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)));
|
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)
|
Mat addLabel(Mat input, string label)
|
||||||
@@ -90,15 +86,13 @@ Mat addLabel(Mat input, string label)
|
|||||||
return newImage;
|
return newImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void drawAndWait(cv::Mat* frame)
|
void drawAndWait(cv::Mat* frame)
|
||||||
{
|
{
|
||||||
cv::imshow("Temp Window", *frame);
|
cv::imshow("Temp Window", *frame);
|
||||||
|
|
||||||
while (cv::waitKey(50) == -1)
|
while (cv::waitKey(50) == -1)
|
||||||
{
|
{
|
||||||
// loop
|
// loop
|
||||||
}
|
}
|
||||||
|
|
||||||
cv::destroyWindow("Temp Window");
|
cv::destroyWindow("Temp Window");
|
||||||
@@ -137,7 +131,8 @@ vector<Mat> produceThresholds(const Mat img_gray, Config* config)
|
|||||||
NiblackSauvolaWolfJolion (img_gray, thresholds[i++], WOLFJOLION, win, win, 0.05 + (k * 0.35));
|
NiblackSauvolaWolfJolion (img_gray, thresholds[i++], WOLFJOLION, win, win, 0.05 + (k * 0.35));
|
||||||
bitwise_not(thresholds[i-1], thresholds[i-1]);
|
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));
|
NiblackSauvolaWolfJolion (img_gray, thresholds[i++], WOLFJOLION, win, win, 0.05 + (k * 0.35));
|
||||||
bitwise_not(thresholds[i-1], thresholds[i-1]);
|
bitwise_not(thresholds[i-1], thresholds[i-1]);
|
||||||
//NiblackSauvolaWolfJolion (img_gray, thresholds[i++], WOLFJOLION, win, win, 0.05 + (k * 0.35));
|
//NiblackSauvolaWolfJolion (img_gray, thresholds[i++], WOLFJOLION, win, win, 0.05 + (k * 0.35));
|
||||||
@@ -151,11 +146,6 @@ vector<Mat> produceThresholds(const Mat img_gray, Config* config)
|
|||||||
NiblackSauvolaWolfJolion (img_gray, thresholds[i++], SAUVOLA, 12, 12, 0.18 * k);
|
NiblackSauvolaWolfJolion (img_gray, thresholds[i++], SAUVOLA, 12, 12, 0.18 * k);
|
||||||
bitwise_not(thresholds[i-1], thresholds[i-1]);
|
bitwise_not(thresholds[i-1], thresholds[i-1]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (config->debugTiming)
|
if (config->debugTiming)
|
||||||
{
|
{
|
||||||
timespec endTime;
|
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;
|
return arraySize % 2 ? array[arraySize / 2] : (array[arraySize / 2 - 1] + array[arraySize / 2]) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Mat equalizeBrightness(Mat img)
|
Mat equalizeBrightness(Mat img)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -194,7 +183,6 @@ Mat equalizeBrightness(Mat img)
|
|||||||
normalize(img, img, 0, 255, NORM_MINMAX);
|
normalize(img, img, 0, 255, NORM_MINMAX);
|
||||||
img.convertTo(img, CV_8U); // convert back to unsigned int
|
img.convertTo(img, CV_8U); // convert back to unsigned int
|
||||||
|
|
||||||
|
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,18 +206,17 @@ void fillMask(Mat img, const Mat mask, Scalar color)
|
|||||||
|
|
||||||
if (m)
|
if (m)
|
||||||
{
|
{
|
||||||
for (int z = 0; z < 3; z++)
|
for (int z = 0; z < 3; z++)
|
||||||
{
|
{
|
||||||
int prevVal = img.at<Vec3b>(row, col)[z];
|
int prevVal = img.at<Vec3b>(row, col)[z];
|
||||||
img.at<Vec3b>(row, col)[z] = ((int) color[z]) | prevVal;
|
img.at<Vec3b>(row, col)[z] = ((int) color[z]) | prevVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void drawX(Mat img, Rect rect, Scalar color, int thickness)
|
void drawX(Mat img, Rect rect, Scalar color, int thickness)
|
||||||
{
|
{
|
||||||
Point tl(rect.x, rect.y);
|
Point tl(rect.x, rect.y);
|
||||||
@@ -251,28 +238,26 @@ double distanceBetweenPoints(Point p1, Point p2)
|
|||||||
|
|
||||||
float angleBetweenPoints(Point p1, Point p2)
|
float angleBetweenPoints(Point p1, Point p2)
|
||||||
{
|
{
|
||||||
int deltaY = p2.y - p1.y;
|
int deltaY = p2.y - p1.y;
|
||||||
int deltaX = p2.x - p1.x;
|
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)
|
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)
|
if (maxWidth / aspect > maxHeight)
|
||||||
{
|
{
|
||||||
return Size(maxHeight * aspect, maxHeight);
|
return Size(maxHeight * aspect, maxHeight);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return Size(maxWidth, maxWidth / aspect);
|
return Size(maxWidth, maxWidth / aspect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LineSegment::LineSegment()
|
LineSegment::LineSegment()
|
||||||
{
|
{
|
||||||
init(0, 0, 0, 0);
|
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->p1 = Point(x1, y1);
|
||||||
this->p2 = Point(x2, y2);
|
this->p2 = Point(x2, y2);
|
||||||
|
|
||||||
if (p2.x - p1.x == 0)
|
if (p2.x - p1.x == 0)
|
||||||
this->slope = 0.00000000001;
|
this->slope = 0.00000000001;
|
||||||
else
|
else
|
||||||
this->slope = (float) (p2.y - p1.y) / (float) (p2.x - p1.x);
|
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 ){
|
bool LineSegment::isPointBelowLine( Point tp )
|
||||||
return ((p2.x - p1.x)*(tp.y - p1.y) - (p2.y - p1.y)*(tp.x - p1.x)) > 0;
|
{
|
||||||
|
return ((p2.x - p1.x)*(tp.y - p1.y) - (p2.y - p1.y)*(tp.x - p1.x)) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
float LineSegment::getPointAt(float x)
|
float LineSegment::getPointAt(float x)
|
||||||
@@ -328,41 +314,38 @@ Point LineSegment::closestPointOnSegmentTo(Point p)
|
|||||||
|
|
||||||
Point LineSegment::intersection(LineSegment line)
|
Point LineSegment::intersection(LineSegment line)
|
||||||
{
|
{
|
||||||
float c1, c2;
|
float c1, c2;
|
||||||
float intersection_X = -1, intersection_Y= -1;
|
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()
|
Point LineSegment::midpoint()
|
||||||
{
|
{
|
||||||
// Handle the case where the line is vertical
|
// Handle the case where the line is vertical
|
||||||
@@ -381,17 +364,17 @@ Point LineSegment::midpoint()
|
|||||||
|
|
||||||
LineSegment LineSegment::getParallelLine(float distance)
|
LineSegment LineSegment::getParallelLine(float distance)
|
||||||
{
|
{
|
||||||
float diff_x = p2.x - p1.x;
|
float diff_x = p2.x - p1.x;
|
||||||
float diff_y = p2.y - p1.y;
|
float diff_y = p2.y - p1.y;
|
||||||
float angle = atan2( diff_x, diff_y);
|
float angle = atan2( diff_x, diff_y);
|
||||||
float dist_x = distance * cos(angle);
|
float dist_x = distance * cos(angle);
|
||||||
float dist_y = -distance * sin(angle);
|
float dist_y = -distance * sin(angle);
|
||||||
|
|
||||||
int offsetX = (int)round(dist_x);
|
int offsetX = (int)round(dist_x);
|
||||||
int offsetY = (int)round(dist_y);
|
int offsetY = (int)round(dist_y);
|
||||||
|
|
||||||
LineSegment result(p1.x + offsetX, p1.y + offsetY,
|
LineSegment result(p1.x + offsetX, p1.y + offsetY,
|
||||||
p2.x + offsetX, p2.y + offsetY);
|
p2.x + offsetX, p2.y + offsetY);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@@ -23,8 +23,6 @@ VerticalHistogram::VerticalHistogram(Mat inputImage, Mat mask)
|
|||||||
{
|
{
|
||||||
analyzeImage(inputImage, mask);
|
analyzeImage(inputImage, mask);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VerticalHistogram::~VerticalHistogram()
|
VerticalHistogram::~VerticalHistogram()
|
||||||
@@ -33,42 +31,38 @@ VerticalHistogram::~VerticalHistogram()
|
|||||||
colHeights.clear();
|
colHeights.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void VerticalHistogram::analyzeImage(Mat inputImage, Mat mask)
|
void VerticalHistogram::analyzeImage(Mat inputImage, Mat mask)
|
||||||
{
|
{
|
||||||
highestPeak = 0;
|
highestPeak = 0;
|
||||||
lowestValley = inputImage.rows;
|
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<uchar>(row, col) > 0 && mask.at<uchar>(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<uchar>(inputImage.rows - columnCount, col) = 255;
|
|
||||||
|
|
||||||
|
if (inputImage.at<uchar>(row, col) > 0 && mask.at<uchar>(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<uchar>(inputImage.rows - columnCount, col) = 255;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int VerticalHistogram::getLocalMinimum(int leftX, int rightX)
|
int VerticalHistogram::getLocalMinimum(int leftX, int rightX)
|
||||||
@@ -107,7 +101,7 @@ int VerticalHistogram::getLocalMaximum(int leftX, int rightX)
|
|||||||
|
|
||||||
int VerticalHistogram::getHeightAt(int x)
|
int VerticalHistogram::getHeightAt(int x)
|
||||||
{
|
{
|
||||||
return colHeights[x];
|
return colHeights[x];
|
||||||
}
|
}
|
||||||
|
|
||||||
void VerticalHistogram::findValleys()
|
void VerticalHistogram::findValleys()
|
||||||
@@ -130,8 +124,7 @@ void VerticalHistogram::findValleys()
|
|||||||
if (aboveMidpoint)
|
if (aboveMidpoint)
|
||||||
{
|
{
|
||||||
if (colHeights[i] > relativePeakHeight)
|
if (colHeights[i] > relativePeakHeight)
|
||||||
relativePeakHeight = colHeights[i];
|
relativePeakHeight = colHeights[i];
|
||||||
|
|
||||||
|
|
||||||
prevDirection = FLAT;
|
prevDirection = FLAT;
|
||||||
|
|
||||||
@@ -171,7 +164,7 @@ HistogramDirection VerticalHistogram::getHistogramDirection(int index)
|
|||||||
|
|
||||||
for (int i = index; i >= trailStartIndex; i--)
|
for (int i = index; i >= trailStartIndex; i--)
|
||||||
{
|
{
|
||||||
trailingAverage += colHeights[i];
|
trailingAverage += colHeights[i];
|
||||||
}
|
}
|
||||||
trailingAverage = trailingAverage / ((float) (1 + index - trailStartIndex));
|
trailingAverage = trailingAverage / ((float) (1 + index - trailStartIndex));
|
||||||
|
|
||||||
@@ -181,7 +174,6 @@ HistogramDirection VerticalHistogram::getHistogramDirection(int index)
|
|||||||
}
|
}
|
||||||
forwardAverage = forwardAverage / ((float) (1 + forwardEndIndex - index));
|
forwardAverage = forwardAverage / ((float) (1 + forwardEndIndex - index));
|
||||||
|
|
||||||
|
|
||||||
float diff = forwardAverage - trailingAverage;
|
float diff = forwardAverage - trailingAverage;
|
||||||
float minDiff = ((float) (highestPeak - lowestValley)) * 0.10;
|
float minDiff = ((float) (highestPeak - lowestValley)) * 0.10;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user