Wrapped OpenALPR library in "alpr" namespace. Resolves issue #60.

This commit is contained in:
Matt Hill
2014-10-27 20:12:57 -04:00
parent 83ed86c6b4
commit 85f52a6b8c
79 changed files with 7234 additions and 6968 deletions

View File

@@ -21,6 +21,8 @@
#include <log4cplus/consoleappender.h> #include <log4cplus/consoleappender.h>
#include <log4cplus/fileappender.h> #include <log4cplus/fileappender.h>
using namespace alpr;
// prototypes // prototypes
void streamRecognitionThread(void* arg); void streamRecognitionThread(void* arg);
bool writeToQueue(std::string jsonResult); bool writeToQueue(std::string jsonResult);

View File

@@ -33,6 +33,8 @@
#include "video/videobuffer.h" #include "video/videobuffer.h"
#include "alpr.h" #include "alpr.h"
using namespace alpr;
const std::string MAIN_WINDOW_NAME = "ALPR main window"; const std::string MAIN_WINDOW_NAME = "ALPR main window";
const bool SAVE_LAST_VIDEO_STILL = false; const bool SAVE_LAST_VIDEO_STILL = false;

View File

@@ -35,6 +35,7 @@
using namespace std; using namespace std;
using namespace cv; using namespace cv;
using namespace alpr;
// 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

View File

@@ -3,6 +3,7 @@
#include "benchmark_utils.h" #include "benchmark_utils.h"
using namespace std; using namespace std;
using namespace alpr;
vector<string> filterByExtension(vector<string> fileList, string extension) vector<string> filterByExtension(vector<string> fileList, string extension)
{ {

View File

@@ -2,6 +2,7 @@
using namespace std; using namespace std;
using namespace cv; using namespace cv;
using namespace alpr;
EndToEndTest::EndToEndTest(string inputDir, string outputDir) EndToEndTest::EndToEndTest(string inputDir, string outputDir)

View File

@@ -15,8 +15,8 @@ class EndToEndTest
private: private:
bool rectMatches(cv::Rect actualPlate, PlateRegion candidate); bool rectMatches(cv::Rect actualPlate, alpr::PlateRegion candidate);
int totalRectCount(PlateRegion rootCandidate); int totalRectCount(alpr::PlateRegion rootCandidate);
std::string inputDir; std::string inputDir;
std::string outputDir; std::string outputDir;

View File

@@ -32,6 +32,7 @@
using namespace std; using namespace std;
using namespace cv; using namespace cv;
using namespace alpr;
// 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

View File

@@ -28,6 +28,7 @@
using namespace std; using namespace std;
using namespace cv; using namespace cv;
using namespace alpr;
// 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

View File

@@ -31,6 +31,7 @@
using namespace std; using namespace std;
using namespace cv; using namespace cv;
using namespace alpr;
// 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.

View File

@@ -58,6 +58,7 @@ const int UP_ARROW_KEY= 82;
using namespace std; using namespace std;
using namespace cv; using namespace cv;
using namespace alpr;
static bool ldragging = false; static bool ldragging = false;
static int xPos1 = 0; static int xPos1 = 0;

View File

@@ -20,20 +20,23 @@
#include "alpr.h" #include "alpr.h"
#include "alpr_impl.h" #include "alpr_impl.h"
// ALPR code namespace alpr
Alpr::Alpr(const std::string country, const std::string configFile, const std::string runtimeDir)
{ {
// ALPR code
Alpr::Alpr(const std::string country, const std::string configFile, const std::string runtimeDir)
{
impl = new AlprImpl(country, configFile, runtimeDir); impl = new AlprImpl(country, configFile, runtimeDir);
} }
Alpr::~Alpr() Alpr::~Alpr()
{ {
delete impl; delete impl;
} }
AlprResults Alpr::recognize(std::string filepath) AlprResults Alpr::recognize(std::string filepath)
{ {
std::ifstream ifs(filepath.c_str(), std::ios::binary|std::ios::ate); std::ifstream ifs(filepath.c_str(), std::ios::binary|std::ios::ate);
std::ifstream::pos_type pos = ifs.tellg(); std::ifstream::pos_type pos = ifs.tellg();
@@ -44,52 +47,53 @@ AlprResults Alpr::recognize(std::string filepath)
ifs.read(&buffer[0], pos); ifs.read(&buffer[0], pos);
return this->recognize( buffer ); return this->recognize( buffer );
} }
AlprResults Alpr::recognize(std::vector<char> imageBytes) AlprResults Alpr::recognize(std::vector<char> imageBytes)
{ {
std::vector<AlprRegionOfInterest> regionsOfInterest; std::vector<AlprRegionOfInterest> regionsOfInterest;
return impl->recognize(imageBytes, regionsOfInterest); return impl->recognize(imageBytes, regionsOfInterest);
} }
AlprResults Alpr::recognize(unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight, std::vector<AlprRegionOfInterest> regionsOfInterest) AlprResults Alpr::recognize(unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight, std::vector<AlprRegionOfInterest> regionsOfInterest)
{ {
return impl->recognize(pixelData, bytesPerPixel, imgWidth, imgHeight, regionsOfInterest); return impl->recognize(pixelData, bytesPerPixel, imgWidth, imgHeight, regionsOfInterest);
} }
std::string Alpr::toJson( AlprResults results ) std::string Alpr::toJson( AlprResults results )
{ {
return AlprImpl::toJson(results); return AlprImpl::toJson(results);
} }
AlprResults Alpr::fromJson(std::string json) { AlprResults Alpr::fromJson(std::string json) {
return AlprImpl::fromJson(json); return AlprImpl::fromJson(json);
} }
void Alpr::setDetectRegion(bool detectRegion) void Alpr::setDetectRegion(bool detectRegion)
{ {
impl->setDetectRegion(detectRegion); impl->setDetectRegion(detectRegion);
} }
void Alpr::setTopN(int topN) void Alpr::setTopN(int topN)
{ {
impl->setTopN(topN); impl->setTopN(topN);
} }
void Alpr::setDefaultRegion(std::string region) void Alpr::setDefaultRegion(std::string region)
{ {
impl->setDefaultRegion(region); impl->setDefaultRegion(region);
} }
bool Alpr::isLoaded() bool Alpr::isLoaded()
{ {
return impl->isLoaded(); return impl->isLoaded();
} }
std::string Alpr::getVersion() std::string Alpr::getVersion()
{ {
return AlprImpl::getVersion(); return AlprImpl::getVersion();
}
} }

View File

@@ -24,23 +24,26 @@
#include <vector> #include <vector>
#include <fstream> #include <fstream>
struct AlprPlate namespace alpr
{ {
struct AlprPlate
{
std::string characters; std::string characters;
float overall_confidence; float overall_confidence;
bool matches_template; bool matches_template;
}; };
struct AlprCoordinate struct AlprCoordinate
{ {
int x; int x;
int y; int y;
}; };
class AlprRegionOfInterest class AlprRegionOfInterest
{ {
public: public:
AlprRegionOfInterest(); AlprRegionOfInterest();
AlprRegionOfInterest(int x, int y, int width, int height) AlprRegionOfInterest(int x, int y, int width, int height)
{ {
@@ -54,10 +57,10 @@ public:
int y; int y;
int width; int width;
int height; int height;
}; };
class AlprPlateResult class AlprPlateResult
{ {
public: public:
AlprPlateResult() {}; AlprPlateResult() {};
virtual ~AlprPlateResult() {}; virtual ~AlprPlateResult() {};
@@ -72,10 +75,10 @@ class AlprPlateResult
int regionConfidence; int regionConfidence;
std::string region; std::string region;
}; };
class AlprResults class AlprResults
{ {
public: public:
AlprResults() {}; AlprResults() {};
virtual ~AlprResults() {}; virtual ~AlprResults() {};
@@ -89,12 +92,12 @@ class AlprResults
std::vector<AlprRegionOfInterest> regionsOfInterest; std::vector<AlprRegionOfInterest> regionsOfInterest;
}; };
class AlprImpl; class AlprImpl;
class Alpr class Alpr
{ {
public: public:
Alpr(const std::string country, const std::string configFile = "", const std::string runtimeDir = ""); Alpr(const std::string country, const std::string configFile = "", const std::string runtimeDir = "");
@@ -123,6 +126,7 @@ class Alpr
private: private:
AlprImpl* impl; AlprImpl* impl;
}; };
}
#endif // OPENALPR_APLR_H #endif // OPENALPR_APLR_H

View File

@@ -24,8 +24,10 @@ void plateAnalysisThread(void* arg);
using namespace std; using namespace std;
using namespace cv; using namespace cv;
AlprImpl::AlprImpl(const std::string country, const std::string configFile, const std::string runtimeDir) namespace alpr
{ {
AlprImpl::AlprImpl(const std::string country, const std::string configFile, const std::string runtimeDir)
{
config = new Config(country, configFile, runtimeDir); config = new Config(country, configFile, runtimeDir);
// Config file or runtime dir not found. Don't process any further. // Config file or runtime dir not found. Don't process any further.
@@ -46,9 +48,9 @@ AlprImpl::AlprImpl(const std::string country, const std::string configFile, cons
this->topN = DEFAULT_TOPN; this->topN = DEFAULT_TOPN;
this->defaultRegion = ""; this->defaultRegion = "";
} }
AlprImpl::~AlprImpl() AlprImpl::~AlprImpl()
{ {
delete config; delete config;
if (plateDetector != ALPR_NULL_PTR) if (plateDetector != ALPR_NULL_PTR)
@@ -59,23 +61,23 @@ AlprImpl::~AlprImpl()
if (ocr != ALPR_NULL_PTR) if (ocr != ALPR_NULL_PTR)
delete ocr; delete ocr;
} }
bool AlprImpl::isLoaded() bool AlprImpl::isLoaded()
{ {
return config->loaded; return config->loaded;
} }
AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img) AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img)
{ {
std::vector<cv::Rect> regionsOfInterest; std::vector<cv::Rect> regionsOfInterest;
regionsOfInterest.push_back(cv::Rect(0, 0, img.cols, img.rows)); regionsOfInterest.push_back(cv::Rect(0, 0, img.cols, img.rows));
return this->recognizeFullDetails(img, regionsOfInterest); return this->recognizeFullDetails(img, regionsOfInterest);
} }
AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img, std::vector<cv::Rect> regionsOfInterest) AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img, std::vector<cv::Rect> regionsOfInterest)
{ {
timespec startTime; timespec startTime;
getTime(&startTime); getTime(&startTime);
@@ -249,19 +251,19 @@ AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img, std::vector<cv::Rect
} }
return response; return response;
} }
AlprResults AlprImpl::recognize( std::vector<char> imageBytes, std::vector<AlprRegionOfInterest> regionsOfInterest ) AlprResults AlprImpl::recognize( std::vector<char> imageBytes, std::vector<AlprRegionOfInterest> regionsOfInterest )
{ {
cv::Mat img = cv::imdecode(cv::Mat(imageBytes), 1); cv::Mat img = cv::imdecode(cv::Mat(imageBytes), 1);
return this->recognize(img, this->convertRects(regionsOfInterest)); return this->recognize(img, this->convertRects(regionsOfInterest));
} }
AlprResults AlprImpl::recognize( unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight, std::vector<AlprRegionOfInterest> regionsOfInterest) AlprResults AlprImpl::recognize( unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight, std::vector<AlprRegionOfInterest> regionsOfInterest)
{ {
int arraySize = imgWidth * imgHeight * bytesPerPixel; int arraySize = imgWidth * imgHeight * bytesPerPixel;
cv::Mat imgData = cv::Mat(arraySize, 1, CV_8U, pixelData); cv::Mat imgData = cv::Mat(arraySize, 1, CV_8U, pixelData);
@@ -275,14 +277,14 @@ AlprResults AlprImpl::recognize( unsigned char* pixelData, int bytesPerPixel, in
} }
return this->recognize(img, this->convertRects(regionsOfInterest)); return this->recognize(img, this->convertRects(regionsOfInterest));
} }
AlprResults AlprImpl::recognize(cv::Mat img, std::vector<cv::Rect> regionsOfInterest) AlprResults AlprImpl::recognize(cv::Mat img, std::vector<cv::Rect> regionsOfInterest)
{ {
AlprFullDetails fullDetails = recognizeFullDetails(img, regionsOfInterest); AlprFullDetails fullDetails = recognizeFullDetails(img, regionsOfInterest);
return fullDetails.results; return fullDetails.results;
} }
std::vector<cv::Rect> AlprImpl::convertRects(std::vector<AlprRegionOfInterest> regionsOfInterest) std::vector<cv::Rect> AlprImpl::convertRects(std::vector<AlprRegionOfInterest> regionsOfInterest)
@@ -296,8 +298,8 @@ AlprResults AlprImpl::recognize(cv::Mat img, std::vector<cv::Rect> regionsOfInte
return rectRegions; return rectRegions;
} }
string AlprImpl::toJson( const AlprResults results ) string AlprImpl::toJson( const AlprResults results )
{ {
cJSON *root, *jsonResults; cJSON *root, *jsonResults;
root = cJSON_CreateObject(); root = cJSON_CreateObject();
@@ -343,12 +345,12 @@ string AlprImpl::toJson( const AlprResults results )
free(out); free(out);
return response; return response;
} }
cJSON* AlprImpl::createJsonObj(const AlprPlateResult* result) cJSON* AlprImpl::createJsonObj(const AlprPlateResult* result)
{ {
cJSON *root, *coords, *candidates; cJSON *root, *coords, *candidates;
root=cJSON_CreateObject(); root=cJSON_CreateObject();
@@ -388,9 +390,9 @@ cJSON* AlprImpl::createJsonObj(const AlprPlateResult* result)
} }
return root; return root;
} }
AlprResults AlprImpl::fromJson(std::string json) { AlprResults AlprImpl::fromJson(std::string json) {
AlprResults allResults; AlprResults allResults;
cJSON* root = cJSON_Parse(json.c_str()); cJSON* root = cJSON_Parse(json.c_str());
@@ -468,27 +470,28 @@ AlprResults AlprImpl::fromJson(std::string json) {
return allResults; return allResults;
} }
void AlprImpl::setDetectRegion(bool detectRegion) void AlprImpl::setDetectRegion(bool detectRegion)
{ {
this->detectRegion = detectRegion; this->detectRegion = detectRegion;
} }
void AlprImpl::setTopN(int topn) void AlprImpl::setTopN(int topn)
{ {
this->topN = topn; this->topN = topn;
} }
void AlprImpl::setDefaultRegion(string region) void AlprImpl::setDefaultRegion(string region)
{ {
this->defaultRegion = region; this->defaultRegion = region;
} }
std::string AlprImpl::getVersion() std::string AlprImpl::getVersion()
{ {
std::stringstream ss; std::stringstream ss;
ss << OPENALPR_MAJOR_VERSION << "." << OPENALPR_MINOR_VERSION << "." << OPENALPR_PATCH_VERSION; ss << OPENALPR_MAJOR_VERSION << "." << OPENALPR_MINOR_VERSION << "." << OPENALPR_PATCH_VERSION;
return ss.str(); return ss.str();
} }
}

View File

@@ -52,15 +52,17 @@
#define ALPR_NULL_PTR 0 #define ALPR_NULL_PTR 0
namespace alpr
struct AlprFullDetails
{ {
struct AlprFullDetails
{
std::vector<PlateRegion> plateRegions; std::vector<PlateRegion> plateRegions;
AlprResults results; AlprResults results;
}; };
class AlprImpl class AlprImpl
{ {
public: public:
AlprImpl(const std::string country, const std::string configFile = "", const std::string runtimeDir = ""); AlprImpl(const std::string country, const std::string configFile = "", const std::string runtimeDir = "");
@@ -100,8 +102,8 @@ class AlprImpl
std::vector<cv::Rect> convertRects(std::vector<AlprRegionOfInterest> regionsOfInterest); std::vector<cv::Rect> convertRects(std::vector<AlprRegionOfInterest> regionsOfInterest);
static cJSON* createJsonObj(const AlprPlateResult* result); static cJSON* createJsonObj(const AlprPlateResult* result);
}; };
}
#endif // OPENALPR_ALPRIMPL_H #endif // OPENALPR_ALPRIMPL_H

View File

@@ -36,13 +36,15 @@
using namespace std; using namespace std;
using namespace cv; using namespace cv;
// ************************************************************* namespace alpr
// glide a window across the image and
// create two maps: mean and standard deviation.
// *************************************************************
float calcLocalStats (Mat &im, Mat &map_m, Mat &map_s, int winx, int winy)
{ {
// *************************************************************
// glide a window across the image and
// create two maps: mean and standard deviation.
// *************************************************************
float calcLocalStats (Mat &im, Mat &map_m, Mat &map_s, int winx, int winy)
{
float m,s,max_s; float m,s,max_s;
long sum, sum_sq; long sum, sum_sq;
uchar foo; uchar foo;
@@ -102,15 +104,15 @@ float calcLocalStats (Mat &im, Mat &map_m, Mat &map_s, int winx, int winy)
} }
return max_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;
@@ -242,4 +244,6 @@ void NiblackSauvolaWolfJolion (Mat im, Mat output, NiblackVersion version,
outputdatarow[x]=0; outputdatarow[x]=0;
} }
} }
}
} }

View File

@@ -26,23 +26,27 @@
#include <iostream> #include <iostream>
#include "opencv2/opencv.hpp" #include "opencv2/opencv.hpp"
namespace alpr
enum NiblackVersion
{ {
enum NiblackVersion
{
NIBLACK=0, NIBLACK=0,
SAUVOLA, SAUVOLA,
WOLFJOLION, WOLFJOLION,
}; };
#define BINARIZEWOLF_VERSION "2.3 (February 26th, 2013)" #define BINARIZEWOLF_VERSION "2.3 (February 26th, 2013)"
#define BINARIZEWOLF_DEFAULTDR 128 #define BINARIZEWOLF_DEFAULTDR 128
#define uget(x,y) at<unsigned char>(y,x) #define uget(x,y) at<unsigned char>(y,x)
#define uset(x,y,v) at<unsigned char>(y,x)=v; #define uset(x,y,v) at<unsigned char>(y,x)=v;
#define fget(x,y) at<float>(y,x) #define fget(x,y) at<float>(y,x)
#define fset(x,y,v) at<float>(y,x)=v; #define fset(x,y,v) at<float>(y,x)=v;
void NiblackSauvolaWolfJolion (cv::Mat im, cv::Mat output, NiblackVersion version, void NiblackSauvolaWolfJolion (cv::Mat im, cv::Mat output, NiblackVersion version,
int winx, int winy, float k); int winx, int winy, float k);
}
#endif // OPENALPR_BINARIZEWOLF_H #endif // OPENALPR_BINARIZEWOLF_H

View File

@@ -22,8 +22,12 @@
using namespace cv; using namespace cv;
using namespace std; using namespace std;
ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config) namespace alpr
{ {
ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config)
{
timespec startTime; timespec startTime;
getTime(&startTime); getTime(&startTime);
@@ -52,14 +56,14 @@ ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config)
getTime(&endTime); getTime(&endTime);
cout << " -- ColorFilter Time: " << diffclock(startTime, endTime) << "ms." << endl; cout << " -- ColorFilter Time: " << diffclock(startTime, endTime) << "ms." << endl;
} }
} }
ColorFilter::~ColorFilter() ColorFilter::~ColorFilter()
{ {
} }
bool ColorFilter::imageIsGrayscale(Mat image) bool ColorFilter::imageIsGrayscale(Mat image)
{ {
// Check whether the original image is grayscale. If it is, we shouldn't attempt any color filter // Check whether the original image is grayscale. If it is, we shouldn't attempt any color filter
for (int row = 0; row < image.rows; row++) for (int row = 0; row < image.rows; row++)
{ {
@@ -82,21 +86,21 @@ bool ColorFilter::imageIsGrayscale(Mat image)
} }
return true; return true;
} }
void ColorFilter::preprocessImage() void ColorFilter::preprocessImage()
{ {
// Equalize the brightness on the HSV channel "V" // Equalize the brightness on the HSV channel "V"
vector<Mat> channels; vector<Mat> channels;
split(this->hsv,channels); split(this->hsv,channels);
Mat img_equalized = equalizeBrightness(channels[2]); Mat img_equalized = equalizeBrightness(channels[2]);
merge(channels,this->hsv); merge(channels,this->hsv);
} }
// Gets the hue/sat/val for areas that we believe are license plate characters // Gets the hue/sat/val for areas that we believe are license plate characters
// Then uses that to filter the whole image and provide a mask. // Then uses that to filter the whole image and provide a mask.
void ColorFilter::findCharColors() void ColorFilter::findCharColors()
{ {
int MINIMUM_SATURATION = 45; int MINIMUM_SATURATION = 45;
if (this->debug) if (this->debug)
@@ -362,12 +366,12 @@ void ColorFilter::findCharColors()
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)
{ {
float bestPercentAgreement = 0; float bestPercentAgreement = 0;
float lowestOverallDiff = 1000000000; float lowestOverallDiff = 1000000000;
int bestPercentAgreementIndex = -1; int bestPercentAgreementIndex = -1;
@@ -395,4 +399,6 @@ int ColorFilter::getMajorityOpinion(vector<float> values, float minPercentAgreem
} }
return bestPercentAgreementIndex; return bestPercentAgreementIndex;
}
} }

View File

@@ -27,10 +27,12 @@
#include "utility.h" #include "utility.h"
#include "config.h" #include "config.h"
namespace alpr
class ColorFilter
{ {
class ColorFilter
{
public: public:
ColorFilter(cv::Mat image, cv::Mat characterMask, Config* config); ColorFilter(cv::Mat image, cv::Mat characterMask, Config* config);
virtual ~ColorFilter(); virtual ~ColorFilter();
@@ -52,6 +54,7 @@ class ColorFilter
bool imageIsGrayscale(cv::Mat image); bool imageIsGrayscale(cv::Mat image);
int getMajorityOpinion(std::vector<float> values, float minPercentAgreement, float maxValDifference); int getMajorityOpinion(std::vector<float> values, float minPercentAgreement, float maxValDifference);
}; };
}
#endif // OPENALPR_COLORFILTER_H #endif // OPENALPR_COLORFILTER_H

View File

@@ -21,9 +21,12 @@
using namespace std; using namespace std;
Config::Config(const std::string country, const std::string config_file, const std::string runtime_dir) namespace alpr
{ {
Config::Config(const std::string country, const std::string config_file, const std::string runtime_dir)
{
string debug_message = ""; string debug_message = "";
this->loaded = false; this->loaded = false;
@@ -116,14 +119,14 @@ Config::Config(const std::string country, const std::string config_file, const s
} }
this->loaded = true; this->loaded = true;
} }
Config::~Config() Config::~Config()
{ {
delete ini; delete ini;
} }
void Config::loadValues(string country) void Config::loadValues(string country)
{ {
runtimeBaseDir = getString("common", "runtime_dir", "/usr/share/openalpr/runtime_data"); runtimeBaseDir = getString("common", "runtime_dir", "/usr/share/openalpr/runtime_data");
@@ -196,10 +199,10 @@ void Config::loadValues(string country)
debugShowImages = getBoolean("debug", "show_images", false); debugShowImages = getBoolean("debug", "show_images", false);
debugPauseOnFrame = getBoolean("debug", "pause_on_frame", false); debugPauseOnFrame = getBoolean("debug", "pause_on_frame", false);
} }
void Config::debugOff() void Config::debugOff()
{ {
debugGeneral = false; debugGeneral = false;
debugTiming = false; debugTiming = false;
debugStateId = false; debugStateId = false;
@@ -211,31 +214,31 @@ void Config::debugOff()
debugOcr = false; debugOcr = false;
debugPostProcess = false; debugPostProcess = false;
debugPauseOnFrame = false; debugPauseOnFrame = false;
} }
string Config::getCascadeRuntimeDir() string Config::getCascadeRuntimeDir()
{ {
return this->runtimeBaseDir + CASCADE_DIR; return this->runtimeBaseDir + CASCADE_DIR;
} }
string Config::getKeypointsRuntimeDir() string Config::getKeypointsRuntimeDir()
{ {
return this->runtimeBaseDir + KEYPOINTS_DIR; return this->runtimeBaseDir + KEYPOINTS_DIR;
} }
string Config::getPostProcessRuntimeDir() string Config::getPostProcessRuntimeDir()
{ {
return this->runtimeBaseDir + POSTPROCESS_DIR; return this->runtimeBaseDir + POSTPROCESS_DIR;
} }
string Config::getTessdataPrefix() string Config::getTessdataPrefix()
{ {
return this->runtimeBaseDir + "/ocr/"; return 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*/);
if (pszValue == NULL) if (pszValue == NULL)
{ {
@@ -245,9 +248,9 @@ float Config::getFloat(string section, string key, float defaultValue)
float val = atof(pszValue); float val = atof(pszValue);
return val; return val;
} }
int Config::getInt(string section, string key, int defaultValue) int Config::getInt(string section, string key, int 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*/);
if (pszValue == NULL) if (pszValue == NULL)
{ {
@@ -257,9 +260,9 @@ int Config::getInt(string section, string key, int defaultValue)
int val = atoi(pszValue); int val = atoi(pszValue);
return val; return val;
} }
bool Config::getBoolean(string section, string key, bool defaultValue) bool Config::getBoolean(string section, string key, bool 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*/);
if (pszValue == NULL) if (pszValue == NULL)
{ {
@@ -269,9 +272,9 @@ bool Config::getBoolean(string section, string key, bool defaultValue)
int val = atoi(pszValue); int val = atoi(pszValue);
return val != 0; return val != 0;
} }
string Config::getString(string section, string key, string defaultValue) string Config::getString(string section, string key, string 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*/);
if (pszValue == NULL) if (pszValue == NULL)
{ {
@@ -281,4 +284,5 @@ string Config::getString(string section, string key, string defaultValue)
string val = string(pszValue); string val = string(pszValue);
return val; return val;
}
} }

View File

@@ -33,10 +33,12 @@
#include <stdlib.h> /* getenv */ #include <stdlib.h> /* getenv */
#include <math.h> #include <math.h>
namespace alpr
class Config
{ {
class Config
{
public: public:
Config(const std::string country, const std::string config_file = "", const std::string runtime_dir = ""); Config(const std::string country, const std::string config_file = "", const std::string runtime_dir = "");
virtual ~Config(); virtual ~Config();
@@ -118,7 +120,7 @@ class Config
std::string getPostProcessRuntimeDir(); std::string getPostProcessRuntimeDir();
std::string getTessdataPrefix(); std::string getTessdataPrefix();
private: private:
CSimpleIniA* ini; CSimpleIniA* ini;
std::string runtimeBaseDir; std::string runtimeBaseDir;
@@ -129,7 +131,7 @@ private:
float getFloat(std::string section, std::string key, float defaultValue); float getFloat(std::string section, std::string key, float defaultValue);
std::string getString(std::string section, std::string key, std::string defaultValue); std::string getString(std::string section, std::string key, std::string defaultValue);
bool getBoolean(std::string section, std::string key, bool defaultValue); bool getBoolean(std::string section, std::string key, bool defaultValue);
}; };
}
#endif // OPENALPR_CONFIG_H #endif // OPENALPR_CONFIG_H

View File

@@ -22,42 +22,45 @@
using namespace cv; using namespace cv;
using namespace std; using namespace std;
Detector::Detector(Config* config) namespace alpr
{ {
Detector::Detector(Config* config)
{
this->config = config; this->config = config;
this->scale_factor = 1.0f; this->scale_factor = 1.0f;
} }
Detector::~Detector() Detector::~Detector()
{ {
} }
bool Detector::isLoaded() bool Detector::isLoaded()
{ {
return this->loaded; return this->loaded;
} }
vector<PlateRegion> Detector::detect(cv::Mat frame) vector<PlateRegion> Detector::detect(cv::Mat frame)
{ {
std::vector<cv::Rect> regionsOfInterest; std::vector<cv::Rect> regionsOfInterest;
regionsOfInterest.push_back(Rect(0, 0, frame.cols, frame.rows)); regionsOfInterest.push_back(Rect(0, 0, frame.cols, frame.rows));
return this->detect(frame, regionsOfInterest); return this->detect(frame, regionsOfInterest);
} }
vector<PlateRegion> Detector::detect(Mat frame, std::vector<cv::Rect> regionsOfInterest) vector<PlateRegion> Detector::detect(Mat frame, std::vector<cv::Rect> regionsOfInterest)
{ {
// Must be implemented by subclass // Must be implemented by subclass
std::vector<PlateRegion> rois; std::vector<PlateRegion> rois;
return rois; return rois;
} }
bool rectHasLargerArea(cv::Rect a, cv::Rect b) { return a.area() < b.area(); }; bool rectHasLargerArea(cv::Rect a, cv::Rect b) { return a.area() < b.area(); };
vector<PlateRegion> Detector::aggregateRegions(vector<Rect> regions) vector<PlateRegion> Detector::aggregateRegions(vector<Rect> regions)
{ {
// Combines overlapping regions into a parent->child order. // Combines overlapping regions into a parent->child order.
// The largest regions will be parents, and they will have children if they are within them. // The largest regions will be parents, and they will have children if they are within them.
// This way, when processing regions later, we can process the parents first, and only delve into the children // This way, when processing regions later, we can process the parents first, and only delve into the children
@@ -106,4 +109,6 @@ vector<PlateRegion> Detector::aggregateRegions(vector<Rect> regions)
return topLevelRegions; return topLevelRegions;
}
} }

View File

@@ -28,15 +28,18 @@
#include "support/timing.h" #include "support/timing.h"
#include "constants.h" #include "constants.h"
struct PlateRegion namespace alpr
{ {
struct PlateRegion
{
cv::Rect rect; cv::Rect rect;
std::vector<PlateRegion> children; std::vector<PlateRegion> children;
}; };
class Detector class Detector
{ {
public: public:
Detector(Config* config); Detector(Config* config);
@@ -56,6 +59,8 @@ class Detector
}; };
}
#endif // OPENALPR_REGIONDETECTOR_H #endif // OPENALPR_REGIONDETECTOR_H

View File

@@ -22,7 +22,10 @@
using namespace cv; using namespace cv;
using namespace std; using namespace std;
DetectorCPU::DetectorCPU(Config* config) : Detector(config) { namespace alpr
{
DetectorCPU::DetectorCPU(Config* config) : Detector(config) {
@@ -35,14 +38,14 @@ DetectorCPU::DetectorCPU(Config* config) : Detector(config) {
this->loaded = false; this->loaded = false;
printf("--(!)Error loading classifier\n"); printf("--(!)Error loading classifier\n");
} }
} }
DetectorCPU::~DetectorCPU() { DetectorCPU::~DetectorCPU() {
} }
vector<PlateRegion> DetectorCPU::detect(Mat frame, std::vector<cv::Rect> regionsOfInterest) vector<PlateRegion> DetectorCPU::detect(Mat frame, std::vector<cv::Rect> regionsOfInterest)
{ {
Mat frame_gray; Mat frame_gray;
cvtColor( frame, frame_gray, CV_BGR2GRAY ); cvtColor( frame, frame_gray, CV_BGR2GRAY );
@@ -50,10 +53,10 @@ vector<PlateRegion> DetectorCPU::detect(Mat frame, std::vector<cv::Rect> regions
vector<PlateRegion> detectedRegions = doCascade(frame_gray, regionsOfInterest); vector<PlateRegion> detectedRegions = doCascade(frame_gray, regionsOfInterest);
return detectedRegions; return detectedRegions;
} }
vector<PlateRegion> DetectorCPU::doCascade(Mat frame, std::vector<cv::Rect> regionsOfInterest) vector<PlateRegion> DetectorCPU::doCascade(Mat frame, std::vector<cv::Rect> regionsOfInterest)
{ {
if (frame.cols > config->maxDetectionInputWidth) if (frame.cols > config->maxDetectionInputWidth)
@@ -115,4 +118,6 @@ vector<PlateRegion> DetectorCPU::doCascade(Mat frame, std::vector<cv::Rect> regi
return orderedRegions; return orderedRegions;
}
} }

View File

@@ -31,19 +31,24 @@
#include "detector.h" #include "detector.h"
class DetectorCPU : public Detector { namespace alpr
public: {
class DetectorCPU : public Detector {
public:
DetectorCPU(Config* config); DetectorCPU(Config* config);
virtual ~DetectorCPU(); virtual ~DetectorCPU();
std::vector<PlateRegion> detect(cv::Mat frame, std::vector<cv::Rect> regionsOfInterest); std::vector<PlateRegion> detect(cv::Mat frame, std::vector<cv::Rect> regionsOfInterest);
private: private:
cv::CascadeClassifier plate_cascade; cv::CascadeClassifier plate_cascade;
std::vector<PlateRegion> doCascade(cv::Mat frame, std::vector<cv::Rect> regionsOfInterest); std::vector<PlateRegion> doCascade(cv::Mat frame, std::vector<cv::Rect> regionsOfInterest);
}; };
}
#endif /* OPENALPR_DETECTORCPU_H */ #endif /* OPENALPR_DETECTORCPU_H */

View File

@@ -1,7 +1,11 @@
#include "detectorfactory.h" #include "detectorfactory.h"
Detector* createDetector(Config* config) namespace alpr
{ {
return new DetectorCPU(config);
}
Detector* createDetector(Config* config)
{
return new DetectorCPU(config);
}
}

View File

@@ -23,7 +23,11 @@
#include "detectorcpu.h" #include "detectorcpu.h"
#include "config.h" #include "config.h"
Detector* createDetector(Config* config); namespace alpr
{
Detector* createDetector(Config* config);
}
#endif /* OPENALPR_DETECTORFACTORY_H */ #endif /* OPENALPR_DETECTORFACTORY_H */

View File

@@ -23,20 +23,23 @@
using namespace std; using namespace std;
using namespace cv; using namespace cv;
EdgeFinder::EdgeFinder(PipelineData* pipeline_data) { namespace alpr
{
EdgeFinder::EdgeFinder(PipelineData* pipeline_data) {
this->pipeline_data = pipeline_data; this->pipeline_data = pipeline_data;
// First re-crop the area from the original picture knowing the text position // First re-crop the area from the original picture knowing the text position
this->confidence = 0; this->confidence = 0;
} }
EdgeFinder::~EdgeFinder() { EdgeFinder::~EdgeFinder() {
} }
std::vector<cv::Point2f> EdgeFinder::findEdgeCorners() { std::vector<cv::Point2f> EdgeFinder::findEdgeCorners() {
TextLineCollection tlc(pipeline_data->textLines); TextLineCollection tlc(pipeline_data->textLines);
@@ -85,10 +88,10 @@ std::vector<cv::Point2f> EdgeFinder::findEdgeCorners() {
corners.push_back(Point(expandX + w, expandY + h)); corners.push_back(Point(expandX + w, expandY + h));
corners.push_back(Point(-1 * expandX, expandY + h)); corners.push_back(Point(-1 * expandX, expandY + h));
// for (int i = 0; i < 4; i++) // for (int i = 0; i < 4; i++)
// { // {
// std::cout << "CORNER: " << corners[i].x << " - " << corners[i].y << std::endl; // std::cout << "CORNER: " << corners[i].x << " - " << corners[i].y << std::endl;
// } // }
} }
// Re-crop an image (from the original image) using the new coordinates // Re-crop an image (from the original image) using the new coordinates
@@ -139,5 +142,6 @@ std::vector<cv::Point2f> EdgeFinder::findEdgeCorners() {
return cornersInOriginalImg; return cornersInOriginalImg;
} }
}

View File

@@ -26,8 +26,11 @@
#include "platelines.h" #include "platelines.h"
#include "platecorners.h" #include "platecorners.h"
class EdgeFinder { namespace alpr
public: {
class EdgeFinder {
public:
EdgeFinder(PipelineData* pipeline_data); EdgeFinder(PipelineData* pipeline_data);
virtual ~EdgeFinder(); virtual ~EdgeFinder();
@@ -35,10 +38,11 @@ public:
float confidence; float confidence;
private: private:
PipelineData* pipeline_data; PipelineData* pipeline_data;
}; };
}
#endif /* OPENALPR_EDGEFINDER_H */ #endif /* OPENALPR_EDGEFINDER_H */

View File

@@ -22,9 +22,12 @@
using namespace cv; using namespace cv;
using namespace std; using namespace std;
PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, PipelineData* pipelineData, vector<TextLine> textLines) : namespace alpr
tlc(textLines)
{ {
PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, PipelineData* pipelineData, vector<TextLine> textLines) :
tlc(textLines)
{
this->pipelineData = pipelineData; this->pipelineData = pipelineData;
if (pipelineData->config->debugPlateCorners) if (pipelineData->config->debugPlateCorners)
@@ -38,14 +41,14 @@ PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, PipelineData*
this->bestVerticalScore = 9999999999999; this->bestVerticalScore = 9999999999999;
} }
PlateCorners::~PlateCorners() PlateCorners::~PlateCorners()
{ {
} }
vector<Point> PlateCorners::findPlateCorners() vector<Point> PlateCorners::findPlateCorners()
{ {
if (pipelineData->config->debugPlateCorners) if (pipelineData->config->debugPlateCorners)
cout << "PlateCorners::findPlateCorners" << endl; cout << "PlateCorners::findPlateCorners" << endl;
@@ -120,10 +123,10 @@ vector<Point> PlateCorners::findPlateCorners()
} }
return corners; return corners;
} }
void PlateCorners::scoreVerticals(int v1, int v2) void PlateCorners::scoreVerticals(int v1, int v2)
{ {
ScoreKeeper scoreKeeper; ScoreKeeper scoreKeeper;
LineSegment left; LineSegment left;
@@ -216,11 +219,11 @@ void PlateCorners::scoreVerticals(int v1, int v2)
bestLeft = LineSegment(left.p1.x, left.p1.y, left.p2.x, left.p2.y); bestLeft = LineSegment(left.p1.x, left.p1.y, left.p2.x, left.p2.y);
bestRight = LineSegment(right.p1.x, right.p1.y, right.p2.x, right.p2.y); bestRight = LineSegment(right.p1.x, right.p1.y, right.p2.x, right.p2.y);
} }
} }
// Score a collection of lines as a possible license plate region. // Score a collection of lines as a possible license plate region.
// If any segments are missing, extrapolate the missing pieces // If any segments are missing, extrapolate the missing pieces
void PlateCorners::scoreHorizontals(int h1, int h2) void PlateCorners::scoreHorizontals(int h1, int h2)
{ {
ScoreKeeper scoreKeeper; ScoreKeeper scoreKeeper;
@@ -235,7 +238,7 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
if (h1 == NO_LINE && h2 == NO_LINE) if (h1 == NO_LINE && h2 == NO_LINE)
{ {
// return; // return;
top = tlc.centerHorizontalLine.getParallelLine(idealPixelHeight / 2); top = tlc.centerHorizontalLine.getParallelLine(idealPixelHeight / 2);
@@ -347,8 +350,8 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
bestTop = LineSegment(top.p1.x, top.p1.y, top.p2.x, top.p2.y); bestTop = LineSegment(top.p1.x, top.p1.y, top.p2.x, top.p2.y);
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);
} }
}
} }

View File

@@ -40,11 +40,12 @@
#define SCORING_LINE_CONFIDENCE_WEIGHT 18.0 #define SCORING_LINE_CONFIDENCE_WEIGHT 18.0
namespace alpr
class PlateCorners
{ {
class PlateCorners
{
public: public:
PlateCorners(cv::Mat inputImage, PlateLines* plateLines, PipelineData* pipelineData, std::vector<TextLine> textLines) ; PlateCorners(cv::Mat inputImage, PlateLines* plateLines, PipelineData* pipelineData, std::vector<TextLine> textLines) ;
@@ -74,6 +75,7 @@ class PlateCorners
void scoreHorizontals( int h1, int h2 ); void scoreHorizontals( int h1, int h2 );
void scoreVerticals( int v1, int v2 ); void scoreVerticals( int v1, int v2 );
}; };
}
#endif // OPENALPR_PLATELINES_H #endif // OPENALPR_PLATELINES_H

View File

@@ -24,23 +24,25 @@ using namespace std;
const float MIN_CONFIDENCE = 0.3; const float MIN_CONFIDENCE = 0.3;
namespace alpr
PlateLines::PlateLines(PipelineData* pipelineData)
{ {
PlateLines::PlateLines(PipelineData* pipelineData)
{
this->pipelineData = pipelineData; this->pipelineData = pipelineData;
this->debug = pipelineData->config->debugPlateLines; this->debug = pipelineData->config->debugPlateLines;
if (debug) if (debug)
cout << "PlateLines constructor" << endl; cout << "PlateLines constructor" << endl;
} }
PlateLines::~PlateLines() PlateLines::~PlateLines()
{ {
} }
void PlateLines::processImage(Mat inputImage, vector<TextLine> textLines, float sensitivity) void PlateLines::processImage(Mat inputImage, vector<TextLine> textLines, float sensitivity)
{ {
if (this->debug) if (this->debug)
cout << "PlateLines findLines" << endl; cout << "PlateLines findLines" << endl;
@@ -131,13 +133,13 @@ void PlateLines::processImage(Mat inputImage, vector<TextLine> textLines, float
cout << "Plate Lines Time: " << diffclock(startTime, endTime) << "ms." << endl; cout << "Plate Lines Time: " << diffclock(startTime, endTime) << "ms." << endl;
} }
} }
vector<PlateLine> PlateLines::getLines(Mat edges, float sensitivityMultiplier, bool vertical) vector<PlateLine> PlateLines::getLines(Mat edges, float sensitivityMultiplier, bool vertical)
{ {
if (this->debug) if (this->debug)
cout << "PlateLines::getLines" << endl; cout << "PlateLines::getLines" << endl;
@@ -219,10 +221,10 @@ vector<PlateLine> PlateLines::getLines(Mat edges, float sensitivityMultiplier, b
} }
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);
@@ -249,4 +251,6 @@ Mat PlateLines::customGrayscaleConversion(Mat src)
//displayImage(config, "Hue", hue); //displayImage(config, "Hue", hue);
return grayscale; return grayscale;
}
} }

View File

@@ -27,14 +27,17 @@
#include "config.h" #include "config.h"
#include "pipeline_data.h" #include "pipeline_data.h"
struct PlateLine namespace alpr
{ {
struct PlateLine
{
LineSegment line; LineSegment line;
float confidence; float confidence;
}; };
class PlateLines class PlateLines
{ {
public: public:
PlateLines(PipelineData* pipelineData); PlateLines(PipelineData* pipelineData);
@@ -55,6 +58,8 @@ class PlateLines
cv::Mat customGrayscaleConversion(cv::Mat src); cv::Mat customGrayscaleConversion(cv::Mat src);
void findLines(cv::Mat inputImage); void findLines(cv::Mat inputImage);
std::vector<PlateLine> getLines(cv::Mat edges, float sensitivityMultiplier, bool vertical); std::vector<PlateLine> getLines(cv::Mat edges, float sensitivityMultiplier, bool vertical);
}; };
}
#endif // OPENALPR_PLATELINES_H #endif // OPENALPR_PLATELINES_H

View File

@@ -21,23 +21,26 @@
#include "scorekeeper.h" #include "scorekeeper.h"
ScoreKeeper::ScoreKeeper() { namespace alpr
} {
ScoreKeeper::ScoreKeeper() {
}
ScoreKeeper::~ScoreKeeper() { ScoreKeeper::~ScoreKeeper() {
} }
void ScoreKeeper::setScore(std::string weight_id, float score, float weight) { void ScoreKeeper::setScore(std::string weight_id, float score, float weight) {
// Assume that we never set this value twice // Assume that we never set this value twice
weight_ids.push_back(weight_id); weight_ids.push_back(weight_id);
scores.push_back(score); scores.push_back(score);
weights.push_back(weight); weights.push_back(weight);
} }
float ScoreKeeper::getTotal() { float ScoreKeeper::getTotal() {
float score = 0; float score = 0;
@@ -47,9 +50,9 @@ float ScoreKeeper::getTotal() {
} }
return score; return score;
} }
void ScoreKeeper::printDebugScores() { void ScoreKeeper::printDebugScores() {
int longest_weight_id = 0; int longest_weight_id = 0;
for (unsigned int i = 0; i < weight_ids.size(); i++) for (unsigned int i = 0; i < weight_ids.size(); i++)
@@ -72,4 +75,6 @@ void ScoreKeeper::printDebugScores() {
std::cout << "Total: " << total << std::endl; std::cout << "Total: " << total << std::endl;
}
} }

View File

@@ -24,8 +24,11 @@
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
class ScoreKeeper { namespace alpr
public: {
class ScoreKeeper {
public:
ScoreKeeper(); ScoreKeeper();
virtual ~ScoreKeeper(); virtual ~ScoreKeeper();
@@ -35,14 +38,16 @@ public:
void printDebugScores(); void printDebugScores();
private: private:
std::vector<std::string> weight_ids; std::vector<std::string> weight_ids;
std::vector<float> weights; std::vector<float> weights;
std::vector<float> scores; std::vector<float> scores;
}; };
}
#endif /* OPENALPR_SCOREKEEPER_H */ #endif /* OPENALPR_SCOREKEEPER_H */

View File

@@ -10,7 +10,10 @@
using namespace cv; using namespace cv;
using namespace std; using namespace std;
TextLineCollection::TextLineCollection(std::vector<TextLine> textLines) { namespace alpr
{
TextLineCollection::TextLineCollection(std::vector<TextLine> textLines) {
charHeight = 0; charHeight = 0;
@@ -50,9 +53,9 @@ TextLineCollection::TextLineCollection(std::vector<TextLine> textLines) {
// Center Vertical Line // Center Vertical Line
} }
cv::Mat TextLineCollection::getDebugImage(cv::Size imageSize) { cv::Mat TextLineCollection::getDebugImage(cv::Size imageSize) {
Mat debugImage = Mat::zeros(imageSize, CV_8U); Mat debugImage = Mat::zeros(imageSize, CV_8U);
line(debugImage, this->centerHorizontalLine.p1, this->centerHorizontalLine.p2, Scalar(255,255,255), 2); line(debugImage, this->centerHorizontalLine.p1, this->centerHorizontalLine.p2, Scalar(255,255,255), 2);
@@ -60,11 +63,11 @@ cv::Mat TextLineCollection::getDebugImage(cv::Size imageSize) {
return debugImage; return debugImage;
} }
// Returns 1 for above, 0 for within, and -1 for below // Returns 1 for above, 0 for within, and -1 for below
int TextLineCollection::isAboveText(LineSegment line) { int TextLineCollection::isAboveText(LineSegment line) {
// Test four points (left and right corner of top and bottom line) // Test four points (left and right corner of top and bottom line)
Point topLeft = line.closestPointOnSegmentTo(topCharArea.p1); Point topLeft = line.closestPointOnSegmentTo(topCharArea.p1);
@@ -86,10 +89,10 @@ int TextLineCollection::isAboveText(LineSegment line) {
return 0; return 0;
} }
// Returns 1 for left, 0 for within, and -1 for to the right // Returns 1 for left, 0 for within, and -1 for to the right
int TextLineCollection::isLeftOfText(LineSegment line) { int TextLineCollection::isLeftOfText(LineSegment line) {
LineSegment leftSide = LineSegment(bottomCharArea.p1, topCharArea.p1); LineSegment leftSide = LineSegment(bottomCharArea.p1, topCharArea.p1);
@@ -113,9 +116,9 @@ int TextLineCollection::isLeftOfText(LineSegment line) {
return -1; return -1;
return 0; return 0;
} }
void TextLineCollection::findCenterHorizontal() { void TextLineCollection::findCenterHorizontal() {
// To find the center horizontal line: // To find the center horizontal line:
// Find the longer of the lines (if multiline) // Find the longer of the lines (if multiline)
// Get the nearest point on the bottom-most line for the // Get the nearest point on the bottom-most line for the
@@ -139,9 +142,9 @@ void TextLineCollection::findCenterHorizontal() {
this->centerHorizontalLine = LineSegment(leftMidpoint, rightMidpoint); this->centerHorizontalLine = LineSegment(leftMidpoint, rightMidpoint);
} }
void TextLineCollection::findCenterVertical() { void TextLineCollection::findCenterVertical() {
// To find the center vertical line: // To find the center vertical line:
// Choose the longest line (if multiline) // Choose the longest line (if multiline)
// Get the midpoint // Get the midpoint
@@ -157,4 +160,6 @@ void TextLineCollection::findCenterVertical() {
this->centerVerticalLine = LineSegment(p1, p2); this->centerVerticalLine = LineSegment(p1, p2);
else else
this->centerVerticalLine = LineSegment(p2, p1); this->centerVerticalLine = LineSegment(p2, p1);
}
} }

View File

@@ -13,10 +13,12 @@
#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/imgproc/imgproc.hpp"
#include "textdetection/textline.h" #include "textdetection/textline.h"
namespace alpr
class TextLineCollection
{ {
public:
class TextLineCollection
{
public:
TextLineCollection(std::vector<TextLine> textLines); TextLineCollection(std::vector<TextLine> textLines);
int isLeftOfText(LineSegment line); int isLeftOfText(LineSegment line);
@@ -33,7 +35,7 @@ public:
cv::Mat getDebugImage(cv::Size imageSize); cv::Mat getDebugImage(cv::Size imageSize);
private: private:
LineSegment topCharArea; LineSegment topCharArea;
LineSegment bottomCharArea; LineSegment bottomCharArea;
@@ -43,7 +45,9 @@ private:
void findCenterHorizontal(); void findCenterHorizontal();
void findCenterVertical(); void findCenterVertical();
}; };
}
#endif /* OPENALPR_TEXTLINECOLLECTION_H */ #endif /* OPENALPR_TEXTLINECOLLECTION_H */

View File

@@ -22,12 +22,15 @@
using namespace cv; using namespace cv;
using namespace std; using namespace std;
//const int DEFAULT_QUERY_FEATURES = 305; namespace alpr
//const int DEFAULT_TRAINING_FEATURES = 305;
const float MAX_DISTANCE_TO_MATCH = 100.0f;
FeatureMatcher::FeatureMatcher(Config* config)
{ {
//const int DEFAULT_QUERY_FEATURES = 305;
//const int DEFAULT_TRAINING_FEATURES = 305;
const float MAX_DISTANCE_TO_MATCH = 100.0f;
FeatureMatcher::FeatureMatcher(Config* config)
{
this->config = config; this->config = config;
//this->descriptorMatcher = DescriptorMatcher::create( "BruteForce-HammingLUT" ); //this->descriptorMatcher = DescriptorMatcher::create( "BruteForce-HammingLUT" );
@@ -37,10 +40,10 @@ FeatureMatcher::FeatureMatcher(Config* config)
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);
} }
FeatureMatcher::~FeatureMatcher() FeatureMatcher::~FeatureMatcher()
{ {
for (uint i = 0; i < trainingImgKeypoints.size(); i++) for (uint i = 0; i < trainingImgKeypoints.size(); i++)
trainingImgKeypoints[i].clear(); trainingImgKeypoints[i].clear();
trainingImgKeypoints.clear(); trainingImgKeypoints.clear();
@@ -48,26 +51,26 @@ FeatureMatcher::~FeatureMatcher()
descriptorMatcher.release(); descriptorMatcher.release();
detector.release(); detector.release();
extractor.release(); extractor.release();
} }
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);
@@ -76,10 +79,10 @@ void FeatureMatcher::surfStyleMatching( const Mat& queryDescriptors, vector<KeyP
_surfStyleMatching(queryDescriptors, matchesKnn, tempMatches); _surfStyleMatching(queryDescriptors, matchesKnn, tempMatches);
crisscrossFiltering(queryKeypoints, tempMatches, matches12); 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;
@@ -143,12 +146,12 @@ void FeatureMatcher::_surfStyleMatching(const Mat& queryDescriptors, vector<vect
//matches12.push_back(match); //matches12.push_back(match);
//} //}
} }
} }
// Compares the matches keypoints for parallel lines. Removes matches that are criss-crossing too much // Compares the matches keypoints for parallel lines. Removes matches that are criss-crossing too much
// We assume that license plates won't be upside-down or backwards. So expect lines to be closely parallel // We assume that license plates won't be upside-down or backwards. So expect lines to be closely parallel
void FeatureMatcher::crisscrossFiltering(const vector<KeyPoint> queryKeypoints, const vector<DMatch> inputMatches, vector<DMatch> &outputMatches) void FeatureMatcher::crisscrossFiltering(const vector<KeyPoint> queryKeypoints, const vector<DMatch> inputMatches, vector<DMatch> &outputMatches)
{ {
Rect crissCrossAreaVertical(0, 0, config->stateIdImageWidthPx, config->stateIdimageHeightPx * 2); Rect crissCrossAreaVertical(0, 0, config->stateIdImageWidthPx, config->stateIdimageHeightPx * 2);
Rect crissCrossAreaHorizontal(0, 0, config->stateIdImageWidthPx * 2, config->stateIdimageHeightPx); Rect crissCrossAreaHorizontal(0, 0, config->stateIdImageWidthPx * 2, config->stateIdimageHeightPx);
@@ -226,11 +229,11 @@ void FeatureMatcher::crisscrossFiltering(const vector<KeyPoint> queryKeypoints,
outputMatches.push_back(matchesForOnePlate[matchIdx[j]]); outputMatches.push_back(matchesForOnePlate[matchIdx[j]]);
} }
} }
} }
// 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();
@@ -279,12 +282,12 @@ bool FeatureMatcher::loadRecognitionSet(string country)
} }
return false; 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;
@@ -387,4 +390,6 @@ RecognitionResult FeatureMatcher::recognize( const Mat& queryImg, bool drawOnIma
} }
return result; return result;
}
} }

View File

@@ -30,16 +30,18 @@
#include "utility.h" #include "utility.h"
#include "config.h" #include "config.h"
namespace alpr
struct RecognitionResult
{ {
struct RecognitionResult
{
bool haswinner; bool haswinner;
std::string winner; std::string winner;
int confidence; int confidence;
} ; } ;
class FeatureMatcher class FeatureMatcher
{ {
public: public:
FeatureMatcher(Config* config); FeatureMatcher(Config* config);
@@ -72,6 +74,7 @@ class FeatureMatcher
void surfStyleMatching( const cv::Mat& queryDescriptors, std::vector<cv::KeyPoint> queryKeypoints, void surfStyleMatching( const cv::Mat& queryDescriptors, std::vector<cv::KeyPoint> queryKeypoints,
std::vector<cv::DMatch>& matches12 ); std::vector<cv::DMatch>& matches12 );
}; };
}
#endif // OPENALPR_FEATUREMATCHER_H #endif // OPENALPR_FEATUREMATCHER_H

View File

@@ -26,21 +26,24 @@
using namespace std; using namespace std;
using namespace cv; using namespace cv;
LicensePlateCandidate::LicensePlateCandidate(PipelineData* pipeline_data) namespace alpr
{ {
LicensePlateCandidate::LicensePlateCandidate(PipelineData* pipeline_data)
{
this->pipeline_data = pipeline_data; this->pipeline_data = pipeline_data;
this->config = pipeline_data->config; this->config = pipeline_data->config;
} }
LicensePlateCandidate::~LicensePlateCandidate() LicensePlateCandidate::~LicensePlateCandidate()
{ {
delete charSegmenter; delete charSegmenter;
} }
// Must delete this pointer in parent class // Must delete this pointer in parent class
void LicensePlateCandidate::recognize() void LicensePlateCandidate::recognize()
{ {
charSegmenter = NULL; charSegmenter = NULL;
pipeline_data->plate_area_confidence = 0; pipeline_data->plate_area_confidence = 0;
@@ -121,6 +124,6 @@ void LicensePlateCandidate::recognize()
} }
} }
}
} }

View File

@@ -37,12 +37,12 @@
#include "config.h" #include "config.h"
#include "pipeline_data.h" #include "pipeline_data.h"
//vector<Rect> getCharacterRegions(Mat frame, vector<Rect> regionsOfInterest); namespace alpr
//vector<RotatedRect> getCharSegmentsBetweenLines(Mat img, vector<vector<Point> > contours, LineSegment top, LineSegment bottom);
class LicensePlateCandidate
{ {
class LicensePlateCandidate
{
public: public:
LicensePlateCandidate(PipelineData* pipeline_data); LicensePlateCandidate(PipelineData* pipeline_data);
virtual ~LicensePlateCandidate(); virtual ~LicensePlateCandidate();
@@ -62,6 +62,7 @@ class LicensePlateCandidate
cv::Size getCropSize(std::vector<cv::Point2f> areaCorners); cv::Size getCropSize(std::vector<cv::Point2f> areaCorners);
}; };
}
#endif // OPENALPR_LICENSEPLATECANDIDATE_H #endif // OPENALPR_LICENSEPLATECANDIDATE_H

View File

@@ -23,9 +23,12 @@ using namespace std;
using namespace cv; using namespace cv;
using namespace tesseract; using namespace tesseract;
OCR::OCR(Config* config) namespace alpr
: postProcessor(config)
{ {
OCR::OCR(Config* config)
: postProcessor(config)
{
const string EXPECTED_TESSERACT_VERSION = "3.03"; const string EXPECTED_TESSERACT_VERSION = "3.03";
this->config = config; this->config = config;
@@ -41,15 +44,15 @@ OCR::OCR(Config* config)
tesseract.Init(config->getTessdataPrefix().c_str(), config->ocrLanguage.c_str() ); tesseract.Init(config->getTessdataPrefix().c_str(), config->ocrLanguage.c_str() );
tesseract.SetVariable("save_blob_choices", "T"); tesseract.SetVariable("save_blob_choices", "T");
tesseract.SetPageSegMode(PSM_SINGLE_CHAR); tesseract.SetPageSegMode(PSM_SINGLE_CHAR);
} }
OCR::~OCR() OCR::~OCR()
{ {
tesseract.Clear(); tesseract.Clear();
} }
void OCR::performOCR(PipelineData* pipeline_data) void OCR::performOCR(PipelineData* pipeline_data)
{ {
timespec startTime; timespec startTime;
getTime(&startTime); getTime(&startTime);
@@ -131,4 +134,6 @@ void OCR::performOCR(PipelineData* pipeline_data)
getTime(&endTime); getTime(&endTime);
cout << "OCR Time: " << diffclock(startTime, endTime) << "ms." << endl; cout << "OCR Time: " << diffclock(startTime, endTime) << "ms." << endl;
} }
}
} }

View File

@@ -34,9 +34,12 @@
#include "tesseract/baseapi.h" #include "tesseract/baseapi.h"
class OCR namespace alpr
{ {
class OCR
{
public: public:
OCR(Config* config); OCR(Config* config);
virtual ~OCR(); virtual ~OCR();
@@ -50,6 +53,8 @@ class OCR
tesseract::TessBaseAPI tesseract; tesseract::TessBaseAPI tesseract;
}; };
}
#endif // OPENALPR_OCR_H #endif // OPENALPR_OCR_H

View File

@@ -3,8 +3,11 @@
using namespace cv; using namespace cv;
using namespace std; using namespace std;
PipelineData::PipelineData(Mat colorImage, Rect regionOfInterest, Config* config) namespace alpr
{ {
PipelineData::PipelineData(Mat colorImage, Rect regionOfInterest, Config* config)
{
this->colorImg = colorImage; this->colorImg = colorImage;
cvtColor(this->colorImg, this->grayImg, CV_BGR2GRAY); cvtColor(this->colorImg, this->grayImg, CV_BGR2GRAY);
@@ -14,18 +17,20 @@ PipelineData::PipelineData(Mat colorImage, Rect regionOfInterest, Config* config
this->region_confidence = 0; this->region_confidence = 0;
plate_inverted = false; plate_inverted = false;
} }
PipelineData::~PipelineData() PipelineData::~PipelineData()
{ {
clearThresholds(); clearThresholds();
} }
void PipelineData::clearThresholds() void PipelineData::clearThresholds()
{ {
for (uint i = 0; i < thresholds.size(); i++) for (uint i = 0; i < thresholds.size(); i++)
{ {
thresholds[i].release(); thresholds[i].release();
} }
thresholds.clear(); thresholds.clear();
}
} }

View File

@@ -7,9 +7,12 @@
#include "config.h" #include "config.h"
#include "textdetection/textline.h" #include "textdetection/textline.h"
class PipelineData namespace alpr
{ {
class PipelineData
{
public: public:
PipelineData(cv::Mat colorImage, cv::Rect regionOfInterest, Config* config); PipelineData(cv::Mat colorImage, cv::Rect regionOfInterest, Config* config);
virtual ~PipelineData(); virtual ~PipelineData();
@@ -52,7 +55,8 @@ class PipelineData
// OCR // OCR
}; };
}
#endif // OPENALPR_PIPELINEDATA_H #endif // OPENALPR_PIPELINEDATA_H

View File

@@ -21,8 +21,12 @@
using namespace std; using namespace std;
PostProcess::PostProcess(Config* config) namespace alpr
{ {
PostProcess::PostProcess(Config* config)
{
this->config = config; this->config = config;
stringstream filename; stringstream filename;
@@ -53,10 +57,10 @@ PostProcess::PostProcess(Config* config)
//vector<RegexRule> test = rules["base"]; //vector<RegexRule> test = rules["base"];
//for (int i = 0; i < test.size(); i++) //for (int i = 0; i < test.size(); i++)
// cout << "Rule: " << test[i].regex << endl; // cout << "Rule: " << test[i].regex << endl;
} }
PostProcess::~PostProcess() 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;
@@ -67,10 +71,10 @@ PostProcess::~PostProcess()
delete iter->second[i]; delete iter->second[i];
} }
} }
} }
void PostProcess::addLetter(string letter, int charposition, float score) void PostProcess::addLetter(string letter, int charposition, float score)
{ {
if (score < config->postProcessMinConfidence) if (score < config->postProcessMinConfidence)
return; return;
@@ -86,10 +90,10 @@ void PostProcess::addLetter(string letter, int charposition, float score)
//{ //{
// insertLetter('O', charposition, score - 0.5); // insertLetter('O', charposition, score - 0.5);
//} //}
} }
void PostProcess::insertLetter(string letter, int charposition, float score) void PostProcess::insertLetter(string letter, int charposition, float score)
{ {
score = score - config->postProcessMinConfidence; score = score - config->postProcessMinConfidence;
int existingIndex = -1; int existingIndex = -1;
@@ -126,10 +130,10 @@ void PostProcess::insertLetter(string letter, int charposition, float score)
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();
@@ -143,10 +147,10 @@ void PostProcess::clear()
bestChars = ""; bestChars = "";
matchesTemplate = false; matchesTemplate = false;
} }
void PostProcess::analyze(string templateregion, int topn) void PostProcess::analyze(string templateregion, int topn)
{ {
timespec startTime; timespec startTime;
getTime(&startTime); getTime(&startTime);
@@ -292,10 +296,10 @@ void PostProcess::analyze(string templateregion, int topn)
if (this->config->debugPostProcess) if (this->config->debugPostProcess)
cout << "PostProcess Analysis Complete: " << bestChars << " -- MATCH: " << matchesTemplate << endl; cout << "PostProcess Analysis Complete: " << bestChars << " -- MATCH: " << matchesTemplate << endl;
} }
float PostProcess::calculateMaxConfidenceScore() float PostProcess::calculateMaxConfidenceScore()
{ {
// Take the best score for each char position and average it. // Take the best score for each char position and average it.
float totalScore = 0; float totalScore = 0;
@@ -314,20 +318,20 @@ float PostProcess::calculateMaxConfidenceScore()
return 0; return 0;
return totalScore / ((float) numScores); return totalScore / ((float) numScores);
} }
// Finds the minimum number of letters to include in the recursive sorting algorithm. // Finds the minimum number of letters to include in the recursive sorting algorithm.
// For example, if I have letters // For example, if I have letters
// A-200 B-100 C-100 // A-200 B-100 C-100
// X-99 Y-95 Z-90 // X-99 Y-95 Z-90
// Q-55 R-80 // Q-55 R-80
// And my topN value was 3, this would return: // And my topN value was 3, this would return:
// 0, 1, 1 // 0, 1, 1
// Which represents: // Which represents:
// A-200 B-100 C-100 // A-200 B-100 C-100
// Y-95 Z-90 // Y-95 Z-90
vector<int> PostProcess::getMaxDepth(int topn) vector<int> PostProcess::getMaxDepth(int topn)
{ {
vector<int> depth; vector<int> depth;
for (int i = 0; i < letters.size(); i++) for (int i = 0; i < letters.size(); i++)
depth.push_back(0); depth.push_back(0);
@@ -344,10 +348,10 @@ vector<int> PostProcess::getMaxDepth(int topn)
} }
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++)
{ {
@@ -355,10 +359,10 @@ int PostProcess::getPermutationCount(vector<int> depth)
} }
return permutationCount; return permutationCount;
} }
int PostProcess::getNextLeastDrop(vector<int> depth) int PostProcess::getNextLeastDrop(vector<int> depth)
{ {
int nextLeastDropCharPos = -1; int nextLeastDropCharPos = -1;
float leastNextDrop = 99999999999; float leastNextDrop = 99999999999;
@@ -377,15 +381,15 @@ int PostProcess::getNextLeastDrop(vector<int> depth)
} }
return nextLeastDropCharPos; return nextLeastDropCharPos;
} }
const vector<PPResult> PostProcess::getResults() const vector<PPResult> PostProcess::getResults()
{ {
return this->allPossibilities; return this->allPossibilities;
} }
void PostProcess::findAllPermutations(vector<Letter> prevletters, int charPos, int substitutionsLeft) void PostProcess::findAllPermutations(vector<Letter> prevletters, int charPos, int substitutionsLeft)
{ {
if (substitutionsLeft < 0) if (substitutionsLeft < 0)
return; return;
@@ -432,24 +436,24 @@ void PostProcess::findAllPermutations(vector<Letter> prevletters, int charPos, i
// 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;
} }
bool letterCompare( const Letter &left, const Letter &right ) bool letterCompare( const Letter &left, const Letter &right )
{ {
if (left.totalscore < right.totalscore) if (left.totalscore < right.totalscore)
return false; return false;
return true; return true;
} }
RegexRule::RegexRule(string region, string pattern) RegexRule::RegexRule(string region, string pattern)
{ {
this->original = pattern; this->original = pattern;
this->region = region; this->region = region;
@@ -487,18 +491,18 @@ RegexRule::RegexRule(string region, string pattern)
//cout << "AA " << this->region << ": " << original << " regex: " << regex << endl; //cout << "AA " << this->region << ": " << original << " regex: " << regex << endl;
//for (int z = 0; z < this->skipPositions.size(); z++) //for (int z = 0; z < this->skipPositions.size(); z++)
// 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)
return false; return false;
return trexp.Match(text.c_str()); return trexp.Match(text.c_str());
} }
string RegexRule::filterSkips(string text) string RegexRule::filterSkips(string text)
{ {
string response = ""; string response = "";
for (int i = 0; i < text.size(); i++) for (int i = 0; i < text.size(); i++)
{ {
@@ -517,4 +521,6 @@ string RegexRule::filterSkips(string text)
} }
return response; return response;
}
} }

View File

@@ -32,26 +32,29 @@
#define SKIP_CHAR "~" #define SKIP_CHAR "~"
struct Letter namespace alpr
{ {
struct Letter
{
std::string letter; std::string letter;
int charposition; int charposition;
float totalscore; float totalscore;
int occurences; int occurences;
}; };
struct PPResult struct PPResult
{ {
std::string letters; std::string letters;
float totalscore; float totalscore;
bool matchesTemplate; bool matchesTemplate;
}; };
bool wordCompare( const PPResult &left, const PPResult &right ); bool wordCompare( const PPResult &left, const PPResult &right );
bool letterCompare( const Letter &left, const Letter &right ); bool letterCompare( const Letter &left, const Letter &right );
class RegexRule class RegexRule
{ {
public: public:
RegexRule(std::string region, std::string pattern); RegexRule(std::string region, std::string pattern);
@@ -65,10 +68,10 @@ class RegexRule
std::string regex; std::string regex;
std::string region; std::string region;
std::vector<int> skipPositions; std::vector<int> skipPositions;
}; };
class PostProcess class PostProcess
{ {
public: public:
PostProcess(Config* config); PostProcess(Config* config);
~PostProcess(); ~PostProcess();
@@ -103,25 +106,7 @@ class PostProcess
std::vector<int> getMaxDepth(int topn); std::vector<int> getMaxDepth(int topn);
int getPermutationCount(std::vector<int> depth); int getPermutationCount(std::vector<int> depth);
int getNextLeastDrop(std::vector<int> depth); int getNextLeastDrop(std::vector<int> depth);
}; };
/* }
class LetterScores
{
public:
LetterScores(int numCharPositions);
void addScore(char letter, int charposition, float score);
vector<char> getBestScore();
float getConfidence();
private:
int numCharPositions;
vector<char> letters;
vector<int> charpositions;
vector<float> scores;
};
*/
#endif // OPENALPR_POSTPROCESS_H #endif // OPENALPR_POSTPROCESS_H

View File

@@ -24,8 +24,11 @@
using namespace cv; using namespace cv;
using namespace std; using namespace std;
CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data) namespace alpr
{ {
CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data)
{
this->pipeline_data = pipeline_data; this->pipeline_data = pipeline_data;
this->config = pipeline_data->config; this->config = pipeline_data->config;
@@ -59,37 +62,37 @@ CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data)
displayImage(config, "CharacterSegmenter Thresholds", drawImageDashboard(pipeline_data->thresholds, CV_8U, 3)); displayImage(config, "CharacterSegmenter Thresholds", drawImageDashboard(pipeline_data->thresholds, CV_8U, 3));
} }
// if (this->config->debugCharSegmenter && pipeline_data->textLines.size() > 0) // if (this->config->debugCharSegmenter && pipeline_data->textLines.size() > 0)
// { // {
// Mat img_contours(charAnalysis->bestThreshold.size(), CV_8U); // Mat img_contours(charAnalysis->bestThreshold.size(), CV_8U);
// charAnalysis->bestThreshold.copyTo(img_contours); // charAnalysis->bestThreshold.copyTo(img_contours);
// cvtColor(img_contours, img_contours, CV_GRAY2RGB); // cvtColor(img_contours, img_contours, CV_GRAY2RGB);
// //
// vector<vector<Point> > allowedContours; // vector<vector<Point> > allowedContours;
// for (uint i = 0; i < charAnalysis->bestContours.size(); i++) // for (uint i = 0; i < charAnalysis->bestContours.size(); i++)
// { // {
// if (charAnalysis->bestContours.goodIndices[i]) // if (charAnalysis->bestContours.goodIndices[i])
// allowedContours.push_back(charAnalysis->bestContours.contours[i]); // allowedContours.push_back(charAnalysis->bestContours.contours[i]);
// } // }
// //
// drawContours(img_contours, charAnalysis->bestContours.contours, // drawContours(img_contours, charAnalysis->bestContours.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
// //
// //
// line(img_contours, pipeline_data->textLines[0].linePolygon[0], pipeline_data->textLines[0].linePolygon[1], Scalar(255, 0, 255), 1); // line(img_contours, pipeline_data->textLines[0].linePolygon[0], pipeline_data->textLines[0].linePolygon[1], Scalar(255, 0, 255), 1);
// line(img_contours, pipeline_data->textLines[0].linePolygon[3], pipeline_data->textLines[0].linePolygon[2], Scalar(255, 0, 255), 1); // line(img_contours, pipeline_data->textLines[0].linePolygon[3], pipeline_data->textLines[0].linePolygon[2], Scalar(255, 0, 255), 1);
// //
// //
// Mat bordered = addLabel(img_contours, "Best Contours"); // Mat bordered = addLabel(img_contours, "Best Contours");
// imgDbgGeneral.push_back(bordered); // imgDbgGeneral.push_back(bordered);
// } // }
@@ -130,7 +133,7 @@ CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data)
allHistograms.push_back(addLabel(histoCopy, label)); allHistograms.push_back(addLabel(histoCopy, label));
} }
// //
float score = 0; float score = 0;
vector<Rect> charBoxes = getHistogramBoxes(vertHistogram, avgCharWidth, avgCharHeight, &score); vector<Rect> charBoxes = getHistogramBoxes(vertHistogram, avgCharWidth, avgCharHeight, &score);
@@ -226,17 +229,17 @@ CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data)
getTime(&endTime); getTime(&endTime);
cout << "Character Segmenter Time: " << diffclock(startTime, endTime) << "ms." << endl; cout << "Character Segmenter Time: " << diffclock(startTime, endTime) << "ms." << endl;
} }
} }
CharacterSegmenter::~CharacterSegmenter() CharacterSegmenter::~CharacterSegmenter()
{ {
} }
// Given a histogram and the horizontal line boundaries, respond with an array of boxes where the characters are // Given a histogram and the horizontal line boundaries, respond with an array of boxes where the characters are
// Scores the histogram quality as well based on num chars, char volume, and even separation // Scores the histogram quality as well based on num chars, char volume, and even separation
vector<Rect> CharacterSegmenter::getHistogramBoxes(VerticalHistogram histogram, float avgCharWidth, float avgCharHeight, float* score) vector<Rect> CharacterSegmenter::getHistogramBoxes(VerticalHistogram histogram, float avgCharWidth, float avgCharHeight, float* score)
{ {
float MIN_HISTOGRAM_HEIGHT = avgCharHeight * config->segmentationMinCharHeightPercent; float MIN_HISTOGRAM_HEIGHT = avgCharHeight * config->segmentationMinCharHeightPercent;
float MAX_SEGMENT_WIDTH = avgCharWidth * config->segmentationMaxCharWidthvsAverage; float MAX_SEGMENT_WIDTH = avgCharWidth * config->segmentationMaxCharWidthvsAverage;
@@ -287,10 +290,10 @@ vector<Rect> CharacterSegmenter::getHistogramBoxes(VerticalHistogram histogram,
} }
return charBoxes; return charBoxes;
} }
vector<Rect> CharacterSegmenter::getBestCharBoxes(Mat img, vector<Rect> charBoxes, float avgCharWidth) vector<Rect> CharacterSegmenter::getBestCharBoxes(Mat img, vector<Rect> charBoxes, float avgCharWidth)
{ {
float MAX_SEGMENT_WIDTH = avgCharWidth * 1.65; float MAX_SEGMENT_WIDTH = avgCharWidth * 1.65;
// This histogram is based on how many char boxes (from ALL of the many thresholded images) are covering each column // This histogram is based on how many char boxes (from ALL of the many thresholded images) are covering each column
@@ -403,10 +406,10 @@ vector<Rect> CharacterSegmenter::getBestCharBoxes(Mat img, vector<Rect> charBoxe
} }
return bestBoxes; return bestBoxes;
} }
vector<Rect> CharacterSegmenter::get1DHits(Mat img, int yOffset) vector<Rect> CharacterSegmenter::get1DHits(Mat img, int yOffset)
{ {
vector<Rect> hits; vector<Rect> hits;
bool onSegment = false; bool onSegment = false;
@@ -434,10 +437,10 @@ vector<Rect> CharacterSegmenter::get1DHits(Mat img, int yOffset)
} }
return hits; return hits;
} }
void CharacterSegmenter::removeSmallContours(vector<Mat> thresholds, float avgCharHeight, TextLine textLine) void CharacterSegmenter::removeSmallContours(vector<Mat> thresholds, float avgCharHeight, TextLine textLine)
{ {
//const float MIN_CHAR_AREA = 0.02 * avgCharWidth * avgCharHeight; // To clear out the tiny specks //const float MIN_CHAR_AREA = 0.02 * avgCharWidth * avgCharHeight; // To clear out the tiny specks
const float MIN_CONTOUR_HEIGHT = 0.3 * avgCharHeight; const float MIN_CONTOUR_HEIGHT = 0.3 * avgCharHeight;
@@ -467,10 +470,10 @@ void CharacterSegmenter::removeSmallContours(vector<Mat> thresholds, float avgCh
} }
} }
} }
} }
vector<Rect> CharacterSegmenter::combineCloseBoxes( vector<Rect> charBoxes, float biggestCharWidth) vector<Rect> CharacterSegmenter::combineCloseBoxes( vector<Rect> charBoxes, float biggestCharWidth)
{ {
vector<Rect> newCharBoxes; vector<Rect> newCharBoxes;
for (uint i = 0; i < charBoxes.size(); i++) for (uint i = 0; i < charBoxes.size(); i++)
@@ -511,10 +514,10 @@ vector<Rect> CharacterSegmenter::combineCloseBoxes( vector<Rect> charBoxes, floa
} }
return newCharBoxes; return newCharBoxes;
} }
void CharacterSegmenter::cleanCharRegions(vector<Mat> thresholds, vector<Rect> charRegions) void CharacterSegmenter::cleanCharRegions(vector<Mat> thresholds, vector<Rect> charRegions)
{ {
const float MIN_SPECKLE_HEIGHT_PERCENT = 0.13; const float MIN_SPECKLE_HEIGHT_PERCENT = 0.13;
const float MIN_SPECKLE_WIDTH_PX = 3; const float MIN_SPECKLE_WIDTH_PX = 3;
const float MIN_CONTOUR_AREA_PERCENT = 0.1; const float MIN_CONTOUR_AREA_PERCENT = 0.1;
@@ -531,8 +534,8 @@ void CharacterSegmenter::cleanCharRegions(vector<Mat> thresholds, vector<Rect> c
thresholds[i].copyTo(tempImg); thresholds[i].copyTo(tempImg);
//Mat element = getStructuringElement( 1, //Mat element = getStructuringElement( 1,
// Size( 2 + 1, 2+1 ), // Size( 2 + 1, 2+1 ),
// Point( 1, 1 ) ); // Point( 1, 1 ) );
//dilate(thresholds[i], tempImg, element); //dilate(thresholds[i], tempImg, element);
//morphologyEx(thresholds[i], tempImg, MORPH_CLOSE, element); //morphologyEx(thresholds[i], tempImg, MORPH_CLOSE, element);
//drawAndWait(&tempImg); //drawAndWait(&tempImg);
@@ -624,10 +627,10 @@ void CharacterSegmenter::cleanCharRegions(vector<Mat> thresholds, vector<Rect> c
line(thresholds[i], Point(charRegions[j].x + charRegions[j].width + 1, charRegions[j].y), Point(charRegions[j].x + charRegions[j].width + 1, charRegions[j].y + charRegions[j].height), Scalar(0, 0, 0)); line(thresholds[i], Point(charRegions[j].x + charRegions[j].width + 1, charRegions[j].y), Point(charRegions[j].x + charRegions[j].width + 1, charRegions[j].y + charRegions[j].height), Scalar(0, 0, 0));
} }
} }
} }
void CharacterSegmenter::cleanBasedOnColor(vector<Mat> thresholds, Mat colorMask, vector<Rect> charRegions) void CharacterSegmenter::cleanBasedOnColor(vector<Mat> thresholds, Mat colorMask, vector<Rect> charRegions)
{ {
// If I knock out x% of the contour area from this thing (after applying the color filter) // If I knock out x% of the contour area from this thing (after applying the color filter)
// Consider it a bad news bear. REmove the whole area. // Consider it a bad news bear. REmove the whole area.
const float MIN_PERCENT_CHUNK_REMOVED = 0.6; const float MIN_PERCENT_CHUNK_REMOVED = 0.6;
@@ -662,9 +665,9 @@ void CharacterSegmenter::cleanBasedOnColor(vector<Mat> thresholds, Mat colorMask
//tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask + Thresh")); //tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask + Thresh"));
//bitwise_and(colorMask, thresholdCopy2, thresholdCopy2); //bitwise_and(colorMask, thresholdCopy2, thresholdCopy2);
//tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask + Thresh + Color")); //tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask + Thresh + Color"));
// //
// Mat tmpytmp = addLabel(thresholdCopy2, "box Mask + Thresh + Color"); // Mat tmpytmp = addLabel(thresholdCopy2, "box Mask + Thresh + Color");
// Mat tmpx = drawImageDashboard(tmpDebug, tmpytmp.type(), 1); // Mat tmpx = drawImageDashboard(tmpDebug, tmpytmp.type(), 1);
//drawAndWait( &tmpx ); //drawAndWait( &tmpx );
cout << "Segmentation Filter Clean by Color: Removed Threshold " << i << " charregion " << j << endl; cout << "Segmentation Filter Clean by Color: Removed Threshold " << i << " charregion " << j << endl;
@@ -676,10 +679,10 @@ void CharacterSegmenter::cleanBasedOnColor(vector<Mat> thresholds, Mat colorMask
} }
} }
} }
} }
void CharacterSegmenter::cleanMostlyFullBoxes(vector<Mat> thresholds, const vector<Rect> charRegions) void CharacterSegmenter::cleanMostlyFullBoxes(vector<Mat> thresholds, const vector<Rect> charRegions)
{ {
float MAX_FILLED = 0.95 * 255; float MAX_FILLED = 0.95 * 255;
for (uint i = 0; i < charRegions.size(); i++) for (uint i = 0; i < charRegions.size(); i++)
@@ -702,10 +705,10 @@ void CharacterSegmenter::cleanMostlyFullBoxes(vector<Mat> thresholds, const vect
} }
} }
} }
} }
vector<Rect> CharacterSegmenter::filterMostlyEmptyBoxes(vector<Mat> thresholds, const vector<Rect> charRegions) vector<Rect> CharacterSegmenter::filterMostlyEmptyBoxes(vector<Mat> thresholds, const vector<Rect> charRegions)
{ {
// Of the n thresholded images, if box 3 (for example) is empty in half (for example) of the thresholded images, // Of the n thresholded images, if box 3 (for example) is empty in half (for example) of the thresholded images,
// clear all data for every box #3. // clear all data for every box #3.
@@ -799,10 +802,10 @@ vector<Rect> CharacterSegmenter::filterMostlyEmptyBoxes(vector<Mat> thresholds,
} }
return newCharRegions; return newCharRegions;
} }
void CharacterSegmenter::filterEdgeBoxes(vector<Mat> thresholds, const vector<Rect> charRegions, float avgCharWidth, float avgCharHeight) void CharacterSegmenter::filterEdgeBoxes(vector<Mat> thresholds, const vector<Rect> charRegions, float avgCharWidth, float avgCharHeight)
{ {
const float MIN_ANGLE_FOR_ROTATION = 0.4; const float MIN_ANGLE_FOR_ROTATION = 0.4;
int MIN_CONNECTED_EDGE_PIXELS = (avgCharHeight * 1.5); int MIN_CONNECTED_EDGE_PIXELS = (avgCharHeight * 1.5);
@@ -959,10 +962,10 @@ void CharacterSegmenter::filterEdgeBoxes(vector<Mat> thresholds, const vector<Re
} }
} }
} }
int CharacterSegmenter::getLongestBlobLengthBetweenLines(Mat img, int col) int CharacterSegmenter::getLongestBlobLengthBetweenLines(Mat img, int col)
{ {
int longestBlobLength = 0; int longestBlobLength = 0;
bool onSegment = false; bool onSegment = false;
@@ -1007,12 +1010,12 @@ int CharacterSegmenter::getLongestBlobLengthBetweenLines(Mat img, int col)
} }
return longestBlobLength; return longestBlobLength;
} }
// Checks to see if a skinny, tall line (extending above or below the char Height) is inside the given box. // Checks to see if a skinny, tall line (extending above or below the char Height) is inside the given box.
// Returns the contour index if true. -1 otherwise // Returns the contour index if true. -1 otherwise
int CharacterSegmenter::isSkinnyLineInsideBox(Mat threshold, Rect box, vector<vector<Point> > contours, vector<Vec4i> hierarchy, float avgCharWidth, float avgCharHeight) int CharacterSegmenter::isSkinnyLineInsideBox(Mat threshold, Rect box, vector<vector<Point> > contours, vector<Vec4i> hierarchy, float avgCharWidth, float avgCharHeight)
{ {
float MIN_EDGE_CONTOUR_HEIGHT = avgCharHeight * 1.25; float MIN_EDGE_CONTOUR_HEIGHT = avgCharHeight * 1.25;
// Sometimes the threshold is smaller than the MIN_EDGE_CONTOUR_HEIGHT. // Sometimes the threshold is smaller than the MIN_EDGE_CONTOUR_HEIGHT.
@@ -1067,15 +1070,15 @@ int CharacterSegmenter::isSkinnyLineInsideBox(Mat threshold, Rect box, vector<ve
} }
return -1; return -1;
} }
Mat CharacterSegmenter::getCharBoxMask(Mat img_threshold, vector<Rect> charBoxes) Mat CharacterSegmenter::getCharBoxMask(Mat img_threshold, vector<Rect> charBoxes)
{ {
Mat mask = Mat::zeros(img_threshold.size(), CV_8U); Mat mask = Mat::zeros(img_threshold.size(), CV_8U);
for (uint i = 0; i < charBoxes.size(); i++) for (uint i = 0; i < charBoxes.size(); i++)
rectangle(mask, charBoxes[i], Scalar(255, 255, 255), -1); rectangle(mask, charBoxes[i], Scalar(255, 255, 255), -1);
return mask; return mask;
}
} }

View File

@@ -29,20 +29,21 @@
#include "textdetection/textcontours.h" #include "textdetection/textcontours.h"
#include "pipeline_data.h" #include "pipeline_data.h"
namespace alpr
//const float MIN_BOX_WIDTH_PX = 4; // 4 pixels
const cv::Scalar COLOR_DEBUG_EDGE(0,0,255); // Red
const cv::Scalar COLOR_DEBUG_SPECKLES(0,0,255); // Red
const cv::Scalar COLOR_DEBUG_MIN_HEIGHT(255,0,0); // Blue
const cv::Scalar COLOR_DEBUG_MIN_AREA(255,0,0); // Blue
const cv::Scalar COLOR_DEBUG_FULLBOX(255,255,0); // Blue-green
const cv::Scalar COLOR_DEBUG_COLORFILTER(255,0,255); // Magenta
const cv::Scalar COLOR_DEBUG_EMPTYFILTER(0,255,255); // Yellow
class CharacterSegmenter
{ {
const cv::Scalar COLOR_DEBUG_EDGE(0,0,255); // Red
const cv::Scalar COLOR_DEBUG_SPECKLES(0,0,255); // Red
const cv::Scalar COLOR_DEBUG_MIN_HEIGHT(255,0,0); // Blue
const cv::Scalar COLOR_DEBUG_MIN_AREA(255,0,0); // Blue
const cv::Scalar COLOR_DEBUG_FULLBOX(255,255,0); // Blue-green
const cv::Scalar COLOR_DEBUG_COLORFILTER(255,0,255); // Magenta
const cv::Scalar COLOR_DEBUG_EMPTYFILTER(0,255,255); // Yellow
class CharacterSegmenter
{
public: public:
CharacterSegmenter(PipelineData* pipeline_data); CharacterSegmenter(PipelineData* pipeline_data);
virtual ~CharacterSegmenter(); virtual ~CharacterSegmenter();
@@ -81,6 +82,8 @@ class CharacterSegmenter
int isSkinnyLineInsideBox(cv::Mat threshold, cv::Rect box, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, float avgCharWidth, float avgCharHeight); int isSkinnyLineInsideBox(cv::Mat threshold, cv::Rect box, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, float avgCharWidth, float avgCharHeight);
}; };
}
#endif // OPENALPR_CHARACTERSEGMENTER_H #endif // OPENALPR_CHARACTERSEGMENTER_H

View File

@@ -19,18 +19,21 @@
#include "segment.h" #include "segment.h"
Segment::Segment(cv::Rect newSegment) namespace alpr
{ {
Segment::Segment(cv::Rect newSegment)
{
this->segment = newSegment; this->segment = newSegment;
} }
Segment::~Segment() Segment::~Segment()
{ {
} }
bool Segment::matches(cv::Rect newSegment) bool Segment::matches(cv::Rect newSegment)
{ {
// Compare the two segments with a given leniency // Compare the two segments with a given leniency
const float WIDTH_LENIENCY_MIN = 0.25; const float WIDTH_LENIENCY_MIN = 0.25;
const float WIDTH_LENIENCY_MAX = 0.20; const float WIDTH_LENIENCY_MAX = 0.20;
@@ -46,5 +49,6 @@ bool Segment::matches(cv::Rect newSegment)
return true; return true;
return false; return false;
} }
}

View File

@@ -25,8 +25,11 @@
#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/imgproc/imgproc.hpp"
class Segment namespace alpr
{ {
class Segment
{
public: public:
Segment(cv::Rect newSegment); Segment(cv::Rect newSegment);
@@ -36,6 +39,8 @@ class Segment
bool matches(cv::Rect newSegment); bool matches(cv::Rect newSegment);
}; };
}
#endif // OPENALPR_SEGMENTATIONGROUP_H #endif // OPENALPR_SEGMENTATIONGROUP_H

View File

@@ -19,23 +19,26 @@
#include "segmentationgroup.h" #include "segmentationgroup.h"
SegmentationGroup::SegmentationGroup() namespace alpr
{ {
} SegmentationGroup::SegmentationGroup()
{
SegmentationGroup::~SegmentationGroup() }
{
} SegmentationGroup::~SegmentationGroup()
{
void SegmentationGroup::add(int segmentID) }
{
void SegmentationGroup::add(int segmentID)
{
this->segmentIDs.push_back(segmentID); this->segmentIDs.push_back(segmentID);
} }
bool SegmentationGroup::equals(SegmentationGroup otherGroup) bool SegmentationGroup::equals(SegmentationGroup otherGroup)
{ {
if (segmentIDs.size() != otherGroup.segmentIDs.size()) if (segmentIDs.size() != otherGroup.segmentIDs.size())
return false; return false;
@@ -46,4 +49,6 @@ bool SegmentationGroup::equals(SegmentationGroup otherGroup)
} }
return true; return true;
}
} }

View File

@@ -27,10 +27,12 @@
#include "segment.h" #include "segment.h"
namespace alpr
class SegmentationGroup
{ {
class SegmentationGroup
{
public: public:
SegmentationGroup(); SegmentationGroup();
virtual ~SegmentationGroup(); virtual ~SegmentationGroup();
@@ -45,6 +47,8 @@ class SegmentationGroup
private: private:
float strength; // Debuggin purposes -- how many threshold segmentations match this one perfectly float strength; // Debuggin purposes -- how many threshold segmentations match this one perfectly
}; };
}
#endif // OPENALPR_SEGMENTATIONGROUP_H #endif // OPENALPR_SEGMENTATIONGROUP_H

View File

@@ -22,19 +22,22 @@
using namespace cv; using namespace cv;
using namespace std; using namespace std;
VerticalHistogram::VerticalHistogram(Mat inputImage, Mat mask) namespace alpr
{ {
analyzeImage(inputImage, mask);
}
VerticalHistogram::~VerticalHistogram() VerticalHistogram::VerticalHistogram(Mat inputImage, Mat mask)
{ {
analyzeImage(inputImage, mask);
}
VerticalHistogram::~VerticalHistogram()
{
histoImg.release(); histoImg.release();
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;
@@ -62,10 +65,10 @@ void VerticalHistogram::analyzeImage(Mat inputImage, Mat mask)
for (; columnCount > 0; columnCount--) for (; columnCount > 0; columnCount--)
histoImg.at<uchar>(inputImage.rows - columnCount, col) = 255; histoImg.at<uchar>(inputImage.rows - columnCount, col) = 255;
} }
} }
int VerticalHistogram::getLocalMinimum(int leftX, int rightX) int VerticalHistogram::getLocalMinimum(int leftX, int rightX)
{ {
int minimum = histoImg.rows + 1; int minimum = histoImg.rows + 1;
int lowestX = leftX; int lowestX = leftX;
@@ -79,10 +82,10 @@ int VerticalHistogram::getLocalMinimum(int leftX, int rightX)
} }
return lowestX; return lowestX;
} }
int VerticalHistogram::getLocalMaximum(int leftX, int rightX) int VerticalHistogram::getLocalMaximum(int leftX, int rightX)
{ {
int maximum = -1; int maximum = -1;
int highestX = leftX; int highestX = leftX;
@@ -96,15 +99,15 @@ int VerticalHistogram::getLocalMaximum(int leftX, int rightX)
} }
return highestX; return highestX;
} }
int VerticalHistogram::getHeightAt(int x) int VerticalHistogram::getHeightAt(int x)
{ {
return colHeights[x]; return colHeights[x];
} }
void VerticalHistogram::findValleys() void VerticalHistogram::findValleys()
{ {
//int MINIMUM_PEAK_HEIGHT = (int) (((float) highestPeak) * 0.75); //int MINIMUM_PEAK_HEIGHT = (int) (((float) highestPeak) * 0.75);
int totalWidth = colHeights.size(); int totalWidth = colHeights.size();
@@ -141,10 +144,10 @@ void VerticalHistogram::findValleys()
} }
} }
} }
} }
HistogramDirection VerticalHistogram::getHistogramDirection(uint index) HistogramDirection VerticalHistogram::getHistogramDirection(uint index)
{ {
int EXTRA_WIDTH_TO_AVERAGE = 2; int EXTRA_WIDTH_TO_AVERAGE = 2;
float trailingAverage = 0; float trailingAverage = 0;
@@ -178,4 +181,6 @@ HistogramDirection VerticalHistogram::getHistogramDirection(uint index)
return FALLING; return FALLING;
else else
return FLAT; return FLAT;
}
} }

View File

@@ -22,19 +22,21 @@
#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/imgproc/imgproc.hpp"
namespace alpr
struct Valley
{ {
struct Valley
{
int startIndex; int startIndex;
int endIndex; int endIndex;
int width; int width;
int pixelsWithin; int pixelsWithin;
}; };
enum HistogramDirection { RISING, FALLING, FLAT }; enum HistogramDirection { RISING, FALLING, FLAT };
class VerticalHistogram class VerticalHistogram
{ {
public: public:
VerticalHistogram(cv::Mat inputImage, cv::Mat mask); VerticalHistogram(cv::Mat inputImage, cv::Mat mask);
@@ -59,6 +61,8 @@ class VerticalHistogram
void findValleys(); void findValleys();
HistogramDirection getHistogramDirection(uint index); HistogramDirection getHistogramDirection(uint index);
}; };
}
#endif // OPENALPR_VERTICALHISTOGRAM_H #endif // OPENALPR_VERTICALHISTOGRAM_H

View File

@@ -23,8 +23,11 @@
using namespace cv; using namespace cv;
using namespace std; using namespace std;
StateIdentifier::StateIdentifier(Config* config) namespace alpr
{ {
StateIdentifier::StateIdentifier(Config* config)
{
this->config = config; this->config = config;
featureMatcher = new FeatureMatcher(config); featureMatcher = new FeatureMatcher(config);
@@ -36,18 +39,18 @@ StateIdentifier::StateIdentifier(Config* config)
} }
featureMatcher->loadRecognitionSet(config->country); featureMatcher->loadRecognitionSet(config->country);
} }
StateIdentifier::~StateIdentifier() StateIdentifier::~StateIdentifier()
{ {
delete featureMatcher; delete featureMatcher;
} }
// Attempts to recognize the plate. Returns a confidence level. Updates the region code and confidence // Attempts to recognize the plate. Returns a confidence level. Updates the region code and confidence
// If region is found, returns true. // If region is found, returns true.
bool StateIdentifier::recognize(PipelineData* pipeline_data) bool StateIdentifier::recognize(PipelineData* pipeline_data)
{ {
timespec startTime; timespec startTime;
getTime(&startTime); getTime(&startTime);
@@ -86,4 +89,6 @@ bool StateIdentifier::recognize(PipelineData* pipeline_data)
return true; return true;
return false; return false;
}
} }

View File

@@ -27,9 +27,12 @@
#include "config.h" #include "config.h"
#include "pipeline_data.h" #include "pipeline_data.h"
class StateIdentifier namespace alpr
{ {
class StateIdentifier
{
public: public:
StateIdentifier(Config* config); StateIdentifier(Config* config);
virtual ~StateIdentifier(); virtual ~StateIdentifier();
@@ -45,6 +48,7 @@ class StateIdentifier
FeatureMatcher* featureMatcher; FeatureMatcher* featureMatcher;
}; };
}
#endif // OPENALPR_STATEIDENTIFIER_H #endif // OPENALPR_STATEIDENTIFIER_H

View File

@@ -1,16 +1,19 @@
#include "filesystem.h" #include "filesystem.h"
bool startsWith(std::string const &fullString, std::string const &prefix) namespace alpr
{ {
bool startsWith(std::string const &fullString, std::string const &prefix)
{
if(fullString.substr(0, prefix.size()).compare(prefix) == 0) { if(fullString.substr(0, prefix.size()).compare(prefix) == 0) {
return true; return true;
} }
return false; return false;
} }
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)); return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
@@ -19,10 +22,10 @@ bool hasEnding (std::string const &fullString, std::string const &ending)
{ {
return false; return false;
} }
} }
bool hasEndingInsensitive(const std::string& fullString, const std::string& ending) bool hasEndingInsensitive(const std::string& fullString, const std::string& ending)
{ {
if (fullString.length() < ending.length()) if (fullString.length() < ending.length())
return false; return false;
@@ -32,10 +35,10 @@ bool hasEndingInsensitive(const std::string& fullString, const std::string& endi
if (tolower(fullString[i]) != tolower(ending[i - startidx])) if (tolower(fullString[i]) != tolower(ending[i - startidx]))
return false; return false;
return true; return true;
} }
bool DirectoryExists( const char* pzPath ) bool DirectoryExists( const char* pzPath )
{ {
if ( pzPath == NULL) return false; if ( pzPath == NULL) return false;
DIR *pDir; DIR *pDir;
@@ -50,10 +53,10 @@ bool DirectoryExists( const char* pzPath )
} }
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;
@@ -61,10 +64,10 @@ bool fileExists( const char* 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)
{ {
DIR *dir; DIR *dir;
std::vector<std::string> files; std::vector<std::string> files;
@@ -88,10 +91,10 @@ 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; return true;
@@ -100,10 +103,12 @@ bool stringCompare( const std::string &left, const std::string &right )
if( left.size() < right.size() ) if( left.size() < right.size() )
return true; return true;
return false; return false;
} }
std::string filenameWithoutExtension(std::string filename) std::string filenameWithoutExtension(std::string filename)
{ {
int lastindex = filename.find_last_of("."); int lastindex = filename.find_last_of(".");
return filename.substr(0, lastindex); return filename.substr(0, lastindex);
}
} }

View File

@@ -17,16 +17,21 @@
#include <string.h> #include <string.h>
#include <vector> #include <vector>
bool startsWith(std::string const &fullString, std::string const &prefix); namespace alpr
bool hasEnding (std::string const &fullString, std::string const &ending); {
bool hasEndingInsensitive(const std::string& fullString, const std::string& ending);
std::string filenameWithoutExtension(std::string filename); bool startsWith(std::string const &fullString, std::string const &prefix);
bool hasEnding (std::string const &fullString, std::string const &ending);
bool hasEndingInsensitive(const std::string& fullString, const std::string& ending);
bool DirectoryExists( const char* pzPath ); std::string filenameWithoutExtension(std::string filename);
bool fileExists( const char* pzPath );
std::vector<std::string> getFilesInDir(const char* dirPath);
bool stringCompare( const std::string &left, const std::string &right ); bool DirectoryExists( const char* pzPath );
bool fileExists( const char* pzPath );
std::vector<std::string> getFilesInDir(const char* dirPath);
bool stringCompare( const std::string &left, const std::string &right );
}
#endif // FILESYSTEM_H #endif // FILESYSTEM_H

View File

@@ -1,16 +1,19 @@
#include "platform.h" #include "platform.h"
void sleep_ms(int sleepMs) namespace alpr
{ {
void sleep_ms(int sleepMs)
{
#ifdef WINDOWS #ifdef WINDOWS
Sleep(sleepMs); Sleep(sleepMs);
#else #else
usleep(sleepMs * 1000); // usleep takes sleep time in us (1 millionth of a second) usleep(sleepMs * 1000); // usleep takes sleep time in us (1 millionth of a second)
#endif #endif
} }
std::string getExeDir() std::string getExeDir()
{ {
#ifdef WINDOWS #ifdef WINDOWS
TCHAR szEXEPath[2048]; TCHAR szEXEPath[2048];
std::stringstream ss; std::stringstream ss;
@@ -42,4 +45,6 @@ std::string getExeDir()
} }
return directory; return directory;
#endif #endif
}
} }

View File

@@ -10,11 +10,13 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
namespace alpr
{
void sleep_ms(int sleepMs);
std::string getExeDir();
void sleep_ms(int sleepMs); }
std::string getExeDir();
#endif //OPENALPR_PLATFORM_H #endif //OPENALPR_PLATFORM_H

View File

@@ -1,12 +1,15 @@
#include "timing.h" #include "timing.h"
timespec diff(timespec start, timespec end); namespace alpr
#ifdef WINDOWS
// Windows timing code
LARGE_INTEGER getFILETIMEoffset()
{ {
timespec diff(timespec start, timespec end);
#ifdef WINDOWS
// Windows timing code
LARGE_INTEGER getFILETIMEoffset()
{
SYSTEMTIME s; SYSTEMTIME s;
FILETIME f; FILETIME f;
LARGE_INTEGER t; LARGE_INTEGER t;
@@ -23,10 +26,10 @@ LARGE_INTEGER getFILETIMEoffset()
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;
@@ -66,23 +69,23 @@ int clock_gettime(int X, timespec *tv)
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)
{ {
@@ -95,19 +98,19 @@ timespec diff(timespec start, timespec end)
temp.tv_usec = end.tv_usec-start.tv_usec; temp.tv_usec = end.tv_usec-start.tv_usec;
} }
return temp; return temp;
} }
long getEpochTime() long getEpochTime()
{ {
return std::time(0) * 1000; return std::time(0) * 1000;
} }
#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);
@@ -115,21 +118,21 @@ void getTime(timespec* time)
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_MONOTONIC, time); clock_gettime(CLOCK_MONOTONIC, 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)
{ {
@@ -142,18 +145,20 @@ timespec diff(timespec start, timespec end)
temp.tv_nsec = end.tv_nsec-start.tv_nsec; temp.tv_nsec = end.tv_nsec-start.tv_nsec;
} }
return temp; return temp;
} }
long getEpochTime() long getEpochTime()
{ {
struct timeval tp; struct timeval tp;
gettimeofday(&tp, NULL); gettimeofday(&tp, NULL);
long ms = tp.tv_sec * 1000 + tp.tv_usec / 1000; long ms = tp.tv_sec * 1000 + tp.tv_usec / 1000;
return ms; return ms;
}
#endif
} }
#endif

View File

@@ -23,9 +23,14 @@
#define timespec timeval #define timespec timeval
#endif #endif
void getTime(timespec* time); namespace alpr
double diffclock(timespec time1,timespec time2); {
long getEpochTime(); void getTime(timespec* time);
double diffclock(timespec time1,timespec time2);
long getEpochTime();
}
#endif #endif

View File

@@ -25,10 +25,13 @@
using namespace cv; using namespace cv;
using namespace std; using namespace std;
bool sort_text_line(TextLine i, TextLine j) { return (i.topLine.p1.y < j.topLine.p1.y); } namespace alpr
CharacterAnalysis::CharacterAnalysis(PipelineData* pipeline_data)
{ {
bool sort_text_line(TextLine i, TextLine j) { return (i.topLine.p1.y < j.topLine.p1.y); }
CharacterAnalysis::CharacterAnalysis(PipelineData* pipeline_data)
{
this->pipeline_data = pipeline_data; this->pipeline_data = pipeline_data;
this->config = pipeline_data->config; this->config = pipeline_data->config;
@@ -38,15 +41,15 @@ CharacterAnalysis::CharacterAnalysis(PipelineData* pipeline_data)
cout << "Starting CharacterAnalysis identification" << endl; cout << "Starting CharacterAnalysis identification" << endl;
this->analyze(); this->analyze();
} }
CharacterAnalysis::~CharacterAnalysis() CharacterAnalysis::~CharacterAnalysis()
{ {
} }
void CharacterAnalysis::analyze() void CharacterAnalysis::analyze()
{ {
pipeline_data->clearThresholds(); pipeline_data->clearThresholds();
pipeline_data->thresholds = produceThresholds(pipeline_data->crop_gray, config); pipeline_data->thresholds = produceThresholds(pipeline_data->crop_gray, config);
@@ -234,13 +237,13 @@ void CharacterAnalysis::analyze()
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));
} }
} }
Mat CharacterAnalysis::getCharacterMask() Mat CharacterAnalysis::getCharacterMask()
{ {
Mat charMask = Mat::zeros(bestThreshold.size(), CV_8U); Mat charMask = Mat::zeros(bestThreshold.size(), CV_8U);
for (uint i = 0; i < bestContours.size(); i++) for (uint i = 0; i < bestContours.size(); i++)
@@ -260,11 +263,11 @@ Mat CharacterAnalysis::getCharacterMask()
} }
return charMask; return charMask;
} }
void CharacterAnalysis::filter(Mat img, TextContours& textContours) void CharacterAnalysis::filter(Mat img, TextContours& textContours)
{ {
static int STARTING_MIN_HEIGHT = round (((float) img.rows) * config->charAnalysisMinPercent); static int STARTING_MIN_HEIGHT = round (((float) img.rows) * config->charAnalysisMinPercent);
static int STARTING_MAX_HEIGHT = round (((float) img.rows) * (config->charAnalysisMinPercent + config->charAnalysisHeightRange)); static int STARTING_MAX_HEIGHT = round (((float) img.rows) * (config->charAnalysisMinPercent + config->charAnalysisHeightRange));
static int HEIGHT_STEP = round (((float) img.rows) * config->charAnalysisHeightStepSize); static int HEIGHT_STEP = round (((float) img.rows) * config->charAnalysisHeightStepSize);
@@ -303,11 +306,11 @@ void CharacterAnalysis::filter(Mat img, TextContours& textContours)
} }
textContours.setIndices(bestIndices); textContours.setIndices(bestIndices);
} }
// Goes through the contours for the plate and picks out possible char segments based on min/max height // Goes through the contours for the plate and picks out possible char segments based on min/max height
void CharacterAnalysis::filterByBoxSize(TextContours& textContours, int minHeightPx, int maxHeightPx) void CharacterAnalysis::filterByBoxSize(TextContours& textContours, int minHeightPx, int maxHeightPx)
{ {
float idealAspect=config->charWidthMM / config->charHeightMM; float idealAspect=config->charWidthMM / config->charHeightMM;
float aspecttolerance=0.25; float aspecttolerance=0.25;
@@ -336,10 +339,10 @@ void CharacterAnalysis::filterByBoxSize(TextContours& textContours, int minHeigh
} }
} }
} }
void CharacterAnalysis::filterContourHoles(TextContours& textContours) void CharacterAnalysis::filterContourHoles(TextContours& textContours)
{ {
for (uint i = 0; i < textContours.size(); i++) for (uint i = 0; i < textContours.size(); i++)
{ {
@@ -364,12 +367,12 @@ void CharacterAnalysis::filterContourHoles(TextContours& textContours)
} }
} }
} }
// Goes through the contours for the plate and picks out possible char segments based on min/max height // Goes through the contours for the plate and picks out possible char segments based on min/max height
// returns a vector of indices corresponding to valid contours // returns a vector of indices corresponding to valid contours
void CharacterAnalysis::filterByParentContour( TextContours& textContours) void CharacterAnalysis::filterByParentContour( TextContours& textContours)
{ {
vector<int> parentIDs; vector<int> parentIDs;
vector<int> votes; vector<int> votes;
@@ -433,10 +436,10 @@ void CharacterAnalysis::filterByParentContour( TextContours& textContours)
} }
} }
} }
void CharacterAnalysis::filterBetweenLines(Mat img, TextContours& textContours, vector<TextLine> textLines ) void CharacterAnalysis::filterBetweenLines(Mat img, TextContours& textContours, vector<TextLine> textLines )
{ {
static float MIN_AREA_PERCENT_WITHIN_LINES = 0.88; static float MIN_AREA_PERCENT_WITHIN_LINES = 0.88;
static float MAX_DISTANCE_PERCENT_FROM_LINES = 0.15; static float MAX_DISTANCE_PERCENT_FROM_LINES = 0.15;
@@ -512,10 +515,10 @@ void CharacterAnalysis::filterBetweenLines(Mat img, TextContours& textContours,
} }
} }
void CharacterAnalysis::filterByOuterMask(TextContours& textContours) void CharacterAnalysis::filterByOuterMask(TextContours& textContours)
{ {
float MINIMUM_PERCENT_LEFT_AFTER_MASK = 0.1; float MINIMUM_PERCENT_LEFT_AFTER_MASK = 0.1;
float MINIMUM_PERCENT_OF_CHARS_INSIDE_PLATE_MASK = 0.6; float MINIMUM_PERCENT_OF_CHARS_INSIDE_PLATE_MASK = 0.6;
@@ -570,10 +573,10 @@ void CharacterAnalysis::filterByOuterMask(TextContours& textContours)
return; return;
} }
} }
bool CharacterAnalysis::isPlateInverted() bool CharacterAnalysis::isPlateInverted()
{ {
Mat charMask = getCharacterMask(); Mat charMask = getCharacterMask();
@@ -586,10 +589,10 @@ bool CharacterAnalysis::isPlateInverted()
return true; return true;
return false; return false;
} }
bool CharacterAnalysis::verifySize(Mat r, float minHeightPx, float maxHeightPx) bool CharacterAnalysis::verifySize(Mat r, float minHeightPx, float maxHeightPx)
{ {
//Char sizes 45x90 //Char sizes 45x90
float aspect=config->charWidthMM / config->charHeightMM; float aspect=config->charWidthMM / config->charHeightMM;
float charAspect= (float)r.cols/(float)r.rows; float charAspect= (float)r.cols/(float)r.rows;
@@ -612,10 +615,10 @@ bool CharacterAnalysis::verifySize(Mat r, float minHeightPx, float maxHeightPx)
return true; return true;
else else
return false; return false;
} }
vector<Point> CharacterAnalysis::getCharArea(LineSegment topLine, LineSegment bottomLine) vector<Point> CharacterAnalysis::getCharArea(LineSegment topLine, LineSegment bottomLine)
{ {
const int MAX = 100000; const int MAX = 100000;
const int MIN= -1; const int MIN= -1;
@@ -650,4 +653,6 @@ vector<Point> CharacterAnalysis::getCharArea(LineSegment topLine, LineSegment bo
} }
return charArea; return charArea;
}
} }

View File

@@ -29,9 +29,12 @@
#include "platemask.h" #include "platemask.h"
#include "linefinder.h" #include "linefinder.h"
class CharacterAnalysis namespace alpr
{ {
class CharacterAnalysis
{
public: public:
CharacterAnalysis(PipelineData* pipeline_data); CharacterAnalysis(PipelineData* pipeline_data);
virtual ~CharacterAnalysis(); virtual ~CharacterAnalysis();
@@ -69,6 +72,8 @@ class CharacterAnalysis
bool verifySize(cv::Mat r, float minHeightPx, float maxHeightPx); bool verifySize(cv::Mat r, float minHeightPx, float maxHeightPx);
}; };
}
#endif // OPENALPR_CHARACTERANALYSIS_H #endif // OPENALPR_CHARACTERANALYSIS_H

View File

@@ -26,15 +26,18 @@
using namespace std; using namespace std;
using namespace cv; using namespace cv;
LineFinder::LineFinder(PipelineData* pipeline_data) { namespace alpr
this->pipeline_data = pipeline_data;
}
LineFinder::~LineFinder() {
}
vector<vector<Point> > LineFinder::findLines(Mat image, const TextContours contours)
{ {
LineFinder::LineFinder(PipelineData* pipeline_data) {
this->pipeline_data = pipeline_data;
}
LineFinder::~LineFinder() {
}
vector<vector<Point> > LineFinder::findLines(Mat image, const TextContours contours)
{
const float MIN_AREA_TO_IGNORE = 0.65; const float MIN_AREA_TO_IGNORE = 0.65;
vector<vector<Point> > linesFound; vector<vector<Point> > linesFound;
@@ -83,12 +86,12 @@ vector<vector<Point> > LineFinder::findLines(Mat image, const TextContours conto
return linesFound; return linesFound;
} }
// Returns a polygon "stripe" across the width of the character region. The lines are voted and the polygon starts at 0 and extends to image width // Returns a polygon "stripe" across the width of the character region. The lines are voted and the polygon starts at 0 and extends to image width
vector<Point> LineFinder::getBestLine(const TextContours contours, vector<CharPointInfo> charPoints) vector<Point> LineFinder::getBestLine(const TextContours contours, vector<CharPointInfo> charPoints)
{ {
vector<Point> bestStripe; vector<Point> bestStripe;
// Find the best fit line segment that is parallel with the most char segments // Find the best fit line segment that is parallel with the most char segments
@@ -132,12 +135,12 @@ vector<Point> LineFinder::getBestLine(const TextContours contours, vector<CharPo
// Only allow lines that have a sane angle // Only allow lines that have a sane angle
// if (abs(top.angle) <= pipeline_data->config->maxPlateAngleDegrees && // if (abs(top.angle) <= pipeline_data->config->maxPlateAngleDegrees &&
// abs(bottom.angle) <= pipeline_data->config->maxPlateAngleDegrees) // abs(bottom.angle) <= pipeline_data->config->maxPlateAngleDegrees)
// { // {
// topLines.push_back(top); // topLines.push_back(top);
// bottomLines.push_back(bottom); // bottomLines.push_back(bottom);
// } // }
LineSegment parallelBot = top.getParallelLine(medianCharHeight * -1); LineSegment parallelBot = top.getParallelLine(medianCharHeight * -1);
LineSegment parallelTop = bottom.getParallelLine(medianCharHeight); LineSegment parallelTop = bottom.getParallelLine(medianCharHeight);
@@ -230,9 +233,9 @@ vector<Point> LineFinder::getBestLine(const TextContours contours, vector<CharPo
return bestStripe; return bestStripe;
} }
CharPointInfo::CharPointInfo(vector<Point> contour, int index) { CharPointInfo::CharPointInfo(vector<Point> contour, int index) {
this->contourIndex = index; this->contourIndex = index;
@@ -250,4 +253,6 @@ CharPointInfo::CharPointInfo(vector<Point> contour, int index) {
this->bottom = Point(x,y); this->bottom = Point(x,y);
}
} }

View File

@@ -27,9 +27,12 @@
#include "textline.h" #include "textline.h"
#include "pipeline_data.h" #include "pipeline_data.h"
class CharPointInfo namespace alpr
{ {
public:
class CharPointInfo
{
public:
CharPointInfo(std::vector<cv::Point> contour, int index); CharPointInfo(std::vector<cv::Point> contour, int index);
cv::Rect boundingBox; cv::Rect boundingBox;
@@ -37,19 +40,21 @@ public:
cv::Point bottom; cv::Point bottom;
int contourIndex; int contourIndex;
}; };
class LineFinder { class LineFinder {
public: public:
LineFinder(PipelineData* pipeline_data); LineFinder(PipelineData* pipeline_data);
virtual ~LineFinder(); virtual ~LineFinder();
std::vector<std::vector<cv::Point> > findLines(cv::Mat image, const TextContours contours); std::vector<std::vector<cv::Point> > findLines(cv::Mat image, const TextContours contours);
private: private:
PipelineData* pipeline_data; PipelineData* pipeline_data;
std::vector<cv::Point> getBestLine(const TextContours contours, std::vector<CharPointInfo> charPoints); std::vector<cv::Point> getBestLine(const TextContours contours, std::vector<CharPointInfo> charPoints);
}; };
}
#endif /* OPENALPR_LINEFINDER_H */ #endif /* OPENALPR_LINEFINDER_H */

View File

@@ -22,22 +22,25 @@
using namespace std; using namespace std;
using namespace cv; using namespace cv;
PlateMask::PlateMask(PipelineData* pipeline_data) { namespace alpr
{
PlateMask::PlateMask(PipelineData* pipeline_data) {
this->pipeline_data = pipeline_data; this->pipeline_data = pipeline_data;
this->hasPlateMask = false; this->hasPlateMask = false;
} }
PlateMask::~PlateMask() { PlateMask::~PlateMask() {
} }
cv::Mat PlateMask::getMask() { cv::Mat PlateMask::getMask() {
return this->plateMask; return this->plateMask;
} }
void PlateMask::findOuterBoxMask( vector<TextContours > contours ) void PlateMask::findOuterBoxMask( vector<TextContours > contours )
{ {
double min_parent_area = pipeline_data->config->templateHeightPx * pipeline_data->config->templateWidthPx * 0.10; // Needs to be at least 10% of the plate area to be considered. double min_parent_area = pipeline_data->config->templateHeightPx * pipeline_data->config->templateWidthPx * 0.10; // Needs to be at least 10% of the plate area to be considered.
int winningIndex = -1; int winningIndex = -1;
@@ -196,4 +199,6 @@ void PlateMask::findOuterBoxMask( vector<TextContours > contours )
bitwise_not(fullMask, fullMask); bitwise_not(fullMask, fullMask);
this->plateMask = fullMask; this->plateMask = fullMask;
}
} }

View File

@@ -24,8 +24,11 @@
#include "pipeline_data.h" #include "pipeline_data.h"
#include "textcontours.h" #include "textcontours.h"
class PlateMask { namespace alpr
public: {
class PlateMask {
public:
PlateMask(PipelineData* pipeline_data); PlateMask(PipelineData* pipeline_data);
virtual ~PlateMask(); virtual ~PlateMask();
@@ -35,13 +38,14 @@ public:
void findOuterBoxMask(std::vector<TextContours > contours); void findOuterBoxMask(std::vector<TextContours > contours);
private: private:
PipelineData* pipeline_data; PipelineData* pipeline_data;
cv::Mat plateMask; cv::Mat plateMask;
}; };
}
#endif /* OPENALPR_PLATEMASK_H */ #endif /* OPENALPR_PLATEMASK_H */

View File

@@ -22,23 +22,25 @@
using namespace std; using namespace std;
using namespace cv; using namespace cv;
namespace alpr
{
TextContours::TextContours() { TextContours::TextContours() {
} }
TextContours::TextContours(cv::Mat threshold) { TextContours::TextContours(cv::Mat threshold) {
load(threshold); load(threshold);
} }
TextContours::~TextContours() { TextContours::~TextContours() {
} }
void TextContours::load(cv::Mat threshold) { void TextContours::load(cv::Mat threshold) {
Mat tempThreshold(threshold.size(), CV_8U); Mat tempThreshold(threshold.size(), CV_8U);
threshold.copyTo(tempThreshold); threshold.copyTo(tempThreshold);
@@ -53,17 +55,17 @@ void TextContours::load(cv::Mat threshold) {
this->width = threshold.cols; this->width = threshold.cols;
this->height = threshold.rows; this->height = threshold.rows;
} }
uint TextContours::size() { uint TextContours::size() {
return contours.size(); return contours.size();
} }
int TextContours::getGoodIndicesCount() int TextContours::getGoodIndicesCount()
{ {
int count = 0; int count = 0;
for (uint i = 0; i < goodIndices.size(); i++) for (uint i = 0; i < goodIndices.size(); i++)
{ {
@@ -72,11 +74,11 @@ int TextContours::getGoodIndicesCount()
} }
return count; return count;
} }
std::vector<bool> TextContours::getIndicesCopy() std::vector<bool> TextContours::getIndicesCopy()
{ {
vector<bool> copyArray; vector<bool> copyArray;
for (uint i = 0; i < goodIndices.size(); i++) for (uint i = 0; i < goodIndices.size(); i++)
{ {
@@ -85,10 +87,10 @@ std::vector<bool> TextContours::getIndicesCopy()
} }
return copyArray; return copyArray;
} }
void TextContours::setIndices(std::vector<bool> newIndices) void TextContours::setIndices(std::vector<bool> newIndices)
{ {
if (newIndices.size() == goodIndices.size()) if (newIndices.size() == goodIndices.size())
{ {
for (uint i = 0; i < newIndices.size(); i++) for (uint i = 0; i < newIndices.size(); i++)
@@ -98,16 +100,16 @@ void TextContours::setIndices(std::vector<bool> newIndices)
{ {
assert("Invalid set operation on indices"); assert("Invalid set operation on indices");
} }
} }
Mat TextContours::drawDebugImage() { Mat TextContours::drawDebugImage() {
Mat img_contours = Mat::zeros(Size(width, height), CV_8U); Mat img_contours = Mat::zeros(Size(width, height), CV_8U);
return drawDebugImage(img_contours); return drawDebugImage(img_contours);
} }
Mat TextContours::drawDebugImage(Mat baseImage) { Mat TextContours::drawDebugImage(Mat baseImage) {
Mat img_contours(baseImage.size(), CV_8U); Mat img_contours(baseImage.size(), CV_8U);
baseImage.copyTo(img_contours); baseImage.copyTo(img_contours);
@@ -132,7 +134,7 @@ Mat TextContours::drawDebugImage(Mat baseImage) {
return img_contours; return img_contours;
}
} }

View File

@@ -23,8 +23,11 @@
#include <vector> #include <vector>
#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/imgproc/imgproc.hpp"
class TextContours { namespace alpr
public: {
class TextContours {
public:
TextContours(); TextContours();
TextContours(cv::Mat threshold); TextContours(cv::Mat threshold);
virtual ~TextContours(); virtual ~TextContours();
@@ -47,10 +50,12 @@ public:
cv::Mat drawDebugImage(); cv::Mat drawDebugImage();
cv::Mat drawDebugImage(cv::Mat baseImage); cv::Mat drawDebugImage(cv::Mat baseImage);
private: private:
}; };
}
#endif /* TEXTCONTOURS_H */ #endif /* TEXTCONTOURS_H */

View File

@@ -24,7 +24,10 @@
using namespace cv; using namespace cv;
TextLine::TextLine(std::vector<cv::Point2f> textArea, std::vector<cv::Point2f> linePolygon) { namespace alpr
{
TextLine::TextLine(std::vector<cv::Point2f> textArea, std::vector<cv::Point2f> linePolygon) {
std::vector<Point> textAreaInts, linePolygonInts; std::vector<Point> textAreaInts, linePolygonInts;
for (uint i = 0; i < textArea.size(); i++) for (uint i = 0; i < textArea.size(); i++)
@@ -33,19 +36,19 @@ TextLine::TextLine(std::vector<cv::Point2f> textArea, std::vector<cv::Point2f> l
linePolygonInts.push_back(Point(round(linePolygon[i].x), round(linePolygon[i].y))); linePolygonInts.push_back(Point(round(linePolygon[i].x), round(linePolygon[i].y)));
initialize(textAreaInts, linePolygonInts); initialize(textAreaInts, linePolygonInts);
} }
TextLine::TextLine(std::vector<cv::Point> textArea, std::vector<cv::Point> linePolygon) { TextLine::TextLine(std::vector<cv::Point> textArea, std::vector<cv::Point> linePolygon) {
initialize(textArea, linePolygon); initialize(textArea, linePolygon);
} }
TextLine::~TextLine() { TextLine::~TextLine() {
} }
void TextLine::initialize(std::vector<cv::Point> textArea, std::vector<cv::Point> linePolygon) { void TextLine::initialize(std::vector<cv::Point> textArea, std::vector<cv::Point> linePolygon) {
if (textArea.size() > 0) if (textArea.size() > 0)
{ {
if (this->textArea.size() > 0) if (this->textArea.size() > 0)
@@ -79,10 +82,10 @@ void TextLine::initialize(std::vector<cv::Point> textArea, std::vector<cv::Point
this->angle = (topLine.angle + bottomLine.angle) / 2; this->angle = (topLine.angle + bottomLine.angle) / 2;
} }
} }
cv::Mat TextLine::drawDebugImage(cv::Mat baseImage) { cv::Mat TextLine::drawDebugImage(cv::Mat baseImage) {
cv::Mat debugImage(baseImage.size(), baseImage.type()); cv::Mat debugImage(baseImage.size(), baseImage.type());
baseImage.copyTo(debugImage); baseImage.copyTo(debugImage);
@@ -104,4 +107,6 @@ cv::Mat TextLine::drawDebugImage(cv::Mat baseImage) {
return debugImage; return debugImage;
}
} }

View File

@@ -24,8 +24,11 @@
#include "utility.h" #include "utility.h"
#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/imgproc/imgproc.hpp"
class TextLine { namespace alpr
public: {
class TextLine {
public:
TextLine(std::vector<cv::Point> textArea, std::vector<cv::Point> linePolygon); TextLine(std::vector<cv::Point> textArea, std::vector<cv::Point> linePolygon);
TextLine(std::vector<cv::Point2f> textArea, std::vector<cv::Point2f> linePolygon); TextLine(std::vector<cv::Point2f> textArea, std::vector<cv::Point2f> linePolygon);
virtual ~TextLine(); virtual ~TextLine();
@@ -45,10 +48,12 @@ public:
float angle; float angle;
cv::Mat drawDebugImage(cv::Mat baseImage); cv::Mat drawDebugImage(cv::Mat baseImage);
private: private:
void initialize(std::vector<cv::Point> textArea, std::vector<cv::Point> linePolygon); void initialize(std::vector<cv::Point> textArea, std::vector<cv::Point> linePolygon);
}; };
}
#endif /* OPENALPR_TEXTLINE_H */ #endif /* OPENALPR_TEXTLINE_H */

View File

@@ -22,30 +22,33 @@
using namespace std; using namespace std;
using namespace cv; using namespace cv;
Transformation::Transformation(Mat bigImage, Mat smallImage, Rect regionInBigImage) { namespace alpr
{
Transformation::Transformation(Mat bigImage, Mat smallImage, Rect regionInBigImage) {
this->bigImage = bigImage; this->bigImage = bigImage;
this->smallImage = smallImage; this->smallImage = smallImage;
this->regionInBigImage = regionInBigImage; this->regionInBigImage = regionInBigImage;
} }
Transformation::~Transformation() { Transformation::~Transformation() {
} }
// 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> Transformation::transformSmallPointsToBigImage(vector<Point> points) vector<Point2f> Transformation::transformSmallPointsToBigImage(vector<Point> points)
{ {
vector<Point2f> floatPoints; vector<Point2f> floatPoints;
for (unsigned int i = 0; i < points.size(); i++) for (unsigned int i = 0; i < points.size(); i++)
floatPoints.push_back(points[i]); floatPoints.push_back(points[i]);
return transformSmallPointsToBigImage(floatPoints); return transformSmallPointsToBigImage(floatPoints);
} }
// 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> Transformation::transformSmallPointsToBigImage(vector<Point2f> points) vector<Point2f> Transformation::transformSmallPointsToBigImage(vector<Point2f> points)
{ {
vector<Point2f> bigPoints; vector<Point2f> bigPoints;
for (uint i = 0; i < points.size(); i++) for (uint i = 0; i < points.size(); i++)
{ {
@@ -59,11 +62,11 @@ vector<Point2f> Transformation::transformSmallPointsToBigImage(vector<Point2f> p
} }
return bigPoints; return bigPoints;
} }
Mat Transformation::getTransformationMatrix(vector<Point2f> corners, Size outputImageSize) Mat Transformation::getTransformationMatrix(vector<Point2f> corners, Size outputImageSize)
{ {
// Corners of the destination image // Corners of the destination image
vector<Point2f> quad_pts; vector<Point2f> quad_pts;
quad_pts.push_back(Point2f(0, 0)); quad_pts.push_back(Point2f(0, 0));
@@ -72,20 +75,20 @@ Mat Transformation::getTransformationMatrix(vector<Point2f> corners, Size output
quad_pts.push_back(Point2f(0, outputImageSize.height)); quad_pts.push_back(Point2f(0, outputImageSize.height));
return getTransformationMatrix(corners, quad_pts); return getTransformationMatrix(corners, quad_pts);
} }
Mat Transformation::getTransformationMatrix(vector<Point2f> corners, vector<Point2f> outputCorners) Mat Transformation::getTransformationMatrix(vector<Point2f> corners, vector<Point2f> outputCorners)
{ {
// Get transformation matrix // Get transformation matrix
Mat transmtx = getPerspectiveTransform(corners, outputCorners); Mat transmtx = getPerspectiveTransform(corners, outputCorners);
return transmtx; return transmtx;
} }
Mat Transformation::crop(Size outputImageSize, Mat transformationMatrix) Mat Transformation::crop(Size outputImageSize, Mat transformationMatrix)
{ {
Mat deskewed(outputImageSize, this->bigImage.type()); Mat deskewed(outputImageSize, this->bigImage.type());
@@ -97,27 +100,27 @@ Mat Transformation::crop(Size outputImageSize, Mat transformationMatrix)
return deskewed; return deskewed;
} }
vector<Point2f> Transformation::remapSmallPointstoCrop(vector<Point> smallPoints, cv::Mat transformationMatrix) vector<Point2f> Transformation::remapSmallPointstoCrop(vector<Point> smallPoints, cv::Mat transformationMatrix)
{ {
vector<Point2f> floatPoints; vector<Point2f> floatPoints;
for (unsigned int i = 0; i < smallPoints.size(); i++) for (unsigned int i = 0; i < smallPoints.size(); i++)
floatPoints.push_back(smallPoints[i]); floatPoints.push_back(smallPoints[i]);
return remapSmallPointstoCrop(floatPoints, transformationMatrix); return remapSmallPointstoCrop(floatPoints, transformationMatrix);
} }
vector<Point2f> Transformation::remapSmallPointstoCrop(vector<Point2f> smallPoints, cv::Mat transformationMatrix) vector<Point2f> Transformation::remapSmallPointstoCrop(vector<Point2f> smallPoints, cv::Mat transformationMatrix)
{ {
vector<Point2f> remappedPoints; vector<Point2f> remappedPoints;
perspectiveTransform(smallPoints, remappedPoints, transformationMatrix); perspectiveTransform(smallPoints, remappedPoints, transformationMatrix);
return remappedPoints; return remappedPoints;
} }
Size Transformation::getCropSize(vector<Point2f> areaCorners, Size targetSize) Size Transformation::getCropSize(vector<Point2f> areaCorners, Size targetSize)
{ {
// Figure out the approximate width/height of the license plate region, so we can maintain the aspect ratio. // Figure out the approximate width/height of the license plate region, so we can maintain the aspect ratio.
LineSegment leftEdge(round(areaCorners[3].x), round(areaCorners[3].y), round(areaCorners[0].x), round(areaCorners[0].y)); LineSegment leftEdge(round(areaCorners[3].x), round(areaCorners[3].y), round(areaCorners[0].x), round(areaCorners[0].y));
LineSegment rightEdge(round(areaCorners[2].x), round(areaCorners[2].y), round(areaCorners[1].x), round(areaCorners[1].y)); LineSegment rightEdge(round(areaCorners[2].x), round(areaCorners[2].y), round(areaCorners[1].x), round(areaCorners[1].y));
@@ -136,4 +139,6 @@ Size Transformation::getCropSize(vector<Point2f> areaCorners, Size targetSize)
} }
return Size(width, height); return Size(width, height);
}
} }

View File

@@ -23,8 +23,11 @@
#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/imgproc/imgproc.hpp"
#include "utility.h" #include "utility.h"
class Transformation { namespace alpr
public: {
class Transformation {
public:
Transformation(cv::Mat bigImage, cv::Mat smallImage, cv::Rect regionInBigImage); Transformation(cv::Mat bigImage, cv::Mat smallImage, cv::Rect regionInBigImage);
virtual ~Transformation(); virtual ~Transformation();
@@ -40,12 +43,14 @@ public:
cv::Size getCropSize(std::vector<cv::Point2f> areaCorners, cv::Size targetSize); cv::Size getCropSize(std::vector<cv::Point2f> areaCorners, cv::Size targetSize);
private: private:
cv::Mat bigImage; cv::Mat bigImage;
cv::Mat smallImage; cv::Mat smallImage;
cv::Rect regionInBigImage; cv::Rect regionInBigImage;
}; };
}
#endif /* OPENALPR_TRANSFORMATION_H */ #endif /* OPENALPR_TRANSFORMATION_H */

View File

@@ -24,8 +24,11 @@
using namespace cv; using namespace cv;
using namespace std; using namespace std;
Rect expandRect(Rect original, int expandXPixels, int expandYPixels, int maxX, int maxY) namespace alpr
{ {
Rect expandRect(Rect original, int expandXPixels, int expandYPixels, int maxX, int maxY)
{
Rect expandedRegion = Rect(original); Rect expandedRegion = Rect(original);
float halfX = round((float) expandXPixels / 2.0); float halfX = round((float) expandXPixels / 2.0);
@@ -45,10 +48,10 @@ Rect expandRect(Rect original, int expandXPixels, int expandYPixels, int maxX, i
expandedRegion.height = maxY - expandedRegion.y; expandedRegion.height = maxY - expandedRegion.y;
return expandedRegion; return expandedRegion;
} }
Mat drawImageDashboard(vector<Mat> images, int imageType, uint numColumns) Mat drawImageDashboard(vector<Mat> images, int imageType, uint numColumns)
{ {
uint numRows = ceil((float) images.size() / (float) numColumns); uint 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);
@@ -65,10 +68,10 @@ Mat drawImageDashboard(vector<Mat> images, int imageType, uint numColumns)
} }
return dashboard; return dashboard;
} }
Mat addLabel(Mat input, string label) Mat addLabel(Mat input, string label)
{ {
const int border_size = 1; const int border_size = 1;
const Scalar border_color(0,0,255); const Scalar border_color(0,0,255);
const int extraHeight = 20; const int extraHeight = 20;
@@ -89,10 +92,10 @@ Mat addLabel(Mat input, string label)
rectangle(newImage, Point(0,0), Point(newImage.cols - 1, newImage.rows -1), border_color, border_size); rectangle(newImage, Point(0,0), Point(newImage.cols - 1, newImage.rows -1), border_color, border_size);
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)
@@ -101,19 +104,19 @@ void drawAndWait(cv::Mat* frame)
} }
cv::destroyWindow("Temp Window"); cv::destroyWindow("Temp Window");
} }
void displayImage(Config* config, string windowName, cv::Mat frame) void displayImage(Config* config, string windowName, cv::Mat frame)
{ {
if (config->debugShowImages) if (config->debugShowImages)
{ {
imshow(windowName, frame); imshow(windowName, frame);
cv::waitKey(5); cv::waitKey(5);
} }
} }
vector<Mat> produceThresholds(const Mat img_gray, Config* config) vector<Mat> produceThresholds(const Mat img_gray, Config* config)
{ {
const int THRESHOLD_COUNT = 3; const int THRESHOLD_COUNT = 3;
//Mat img_equalized = equalizeBrightness(img_gray); //Mat img_equalized = equalizeBrightness(img_gray);
@@ -163,10 +166,10 @@ vector<Mat> produceThresholds(const Mat img_gray, Config* config)
return thresholds; return thresholds;
//threshold(img_equalized, img_threshold, 100, 255, THRESH_BINARY); //threshold(img_equalized, img_threshold, 100, 255, THRESH_BINARY);
} }
double median(int array[], int arraySize) double median(int array[], int arraySize)
{ {
if (arraySize == 0) if (arraySize == 0)
{ {
//std::cerr << "Median calculation requested on empty array" << endl; //std::cerr << "Median calculation requested on empty array" << endl;
@@ -175,10 +178,10 @@ double median(int array[], int arraySize)
std::sort(&array[0], &array[arraySize]); std::sort(&array[0], &array[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)
{ {
// Divide the image by its morphologically closed counterpart // Divide the image by its morphologically closed counterpart
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(19,19)); Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(19,19));
Mat closed; Mat closed;
@@ -190,18 +193,18 @@ Mat equalizeBrightness(Mat img)
img.convertTo(img, CV_8U); // convert back to unsigned int img.convertTo(img, CV_8U); // convert back to unsigned int
return img; return img;
} }
void drawRotatedRect(Mat* img, RotatedRect rect, Scalar color, int thickness) void drawRotatedRect(Mat* img, RotatedRect rect, Scalar color, int thickness)
{ {
Point2f rect_points[4]; Point2f rect_points[4];
rect.points( rect_points ); rect.points( rect_points );
for( int j = 0; j < 4; j++ ) for( int j = 0; j < 4; j++ )
line( *img, rect_points[j], rect_points[(j+1)%4], color, thickness, 8 ); line( *img, rect_points[j], rect_points[(j+1)%4], color, thickness, 8 );
} }
void fillMask(Mat img, const Mat mask, Scalar color) void fillMask(Mat img, const Mat mask, Scalar color)
{ {
for (int row = 0; row < img.rows; row++) for (int row = 0; row < img.rows; row++)
{ {
for (int col = 0; col < img.cols; col++) for (int col = 0; col < img.cols; col++)
@@ -218,10 +221,10 @@ void fillMask(Mat img, const Mat mask, Scalar color)
} }
} }
} }
} }
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);
Point tr(rect.x + rect.width, rect.y); Point tr(rect.x + rect.width, rect.y);
Point bl(rect.x, rect.y + rect.height); Point bl(rect.x, rect.y + rect.height);
@@ -229,26 +232,26 @@ void drawX(Mat img, Rect rect, Scalar color, int thickness)
line(img, tl, br, color, thickness); line(img, tl, br, color, thickness);
line(img, bl, tr, color, thickness); line(img, bl, tr, color, thickness);
} }
double distanceBetweenPoints(Point p1, Point p2) double distanceBetweenPoints(Point p1, Point p2)
{ {
float asquared = (p2.x - p1.x)*(p2.x - p1.x); float asquared = (p2.x - p1.x)*(p2.x - p1.x);
float bsquared = (p2.y - p1.y)*(p2.y - p1.y); float bsquared = (p2.y - p1.y)*(p2.y - p1.y);
return sqrt(asquared + bsquared); return sqrt(asquared + bsquared);
} }
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)
@@ -259,25 +262,25 @@ Size getSizeMaintainingAspect(Mat inputImg, int maxWidth, int maxHeight)
{ {
return Size(maxWidth, maxWidth / aspect); return Size(maxWidth, maxWidth / aspect);
} }
} }
LineSegment::LineSegment() LineSegment::LineSegment()
{ {
init(0, 0, 0, 0); init(0, 0, 0, 0);
} }
LineSegment::LineSegment(Point p1, Point p2) LineSegment::LineSegment(Point p1, Point p2)
{ {
init(p1.x, p1.y, p2.x, p2.y); init(p1.x, p1.y, p2.x, p2.y);
} }
LineSegment::LineSegment(int x1, int y1, int x2, int y2) LineSegment::LineSegment(int x1, int y1, int x2, int y2)
{ {
init(x1, y1, x2, y2); init(x1, y1, x2, y2);
} }
void LineSegment::init(int x1, int y1, int x2, int y2) 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);
@@ -289,20 +292,20 @@ void LineSegment::init(int x1, int y1, int x2, int y2)
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)
{ {
return slope * (x - p2.x) + p2.y; return slope * (x - p2.x) + p2.y;
} }
Point LineSegment::closestPointOnSegmentTo(Point p) Point LineSegment::closestPointOnSegmentTo(Point p)
{ {
float top = (p.x - p1.x) * (p2.x - p1.x) + (p.y - p1.y)*(p2.y - p1.y); float top = (p.x - p1.x) * (p2.x - p1.x) + (p.y - p1.y)*(p2.y - p1.y);
float bottom = distanceBetweenPoints(p2, p1); float bottom = distanceBetweenPoints(p2, p1);
@@ -314,10 +317,10 @@ Point LineSegment::closestPointOnSegmentTo(Point p)
float y = p1.y + u * (p2.y - p1.y); float y = p1.y + u * (p2.y - p1.y);
return Point(x, y); return Point(x, y);
} }
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;
@@ -346,10 +349,10 @@ Point LineSegment::intersection(LineSegment line)
} }
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
if (p1.x == p2.x) if (p1.x == p2.x)
{ {
@@ -362,10 +365,10 @@ Point LineSegment::midpoint()
int midY = getPointAt(midX); int midY = getPointAt(midX);
return Point(midX, midY); return Point(midX, midY);
} }
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);
@@ -379,12 +382,12 @@ LineSegment LineSegment::getParallelLine(float distance)
p2.x + offsetX, p2.y + offsetY); p2.x + offsetX, p2.y + offsetY);
return result; return result;
} }
// Given a contour and a mask, this function determines what percentage of the contour (area) // Given a contour and a mask, this function determines what percentage of the contour (area)
// is inside the masked area. // is inside the masked area.
float getContourAreaPercentInsideMask(cv::Mat mask, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, int contourIndex) float getContourAreaPercentInsideMask(cv::Mat mask, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, int contourIndex)
{ {
Mat innerArea = Mat::zeros(mask.size(), CV_8U); Mat innerArea = Mat::zeros(mask.size(), CV_8U);
@@ -410,27 +413,29 @@ float getContourAreaPercentInsideMask(cv::Mat mask, std::vector<std::vector<cv::
return ((float) endingPixels) / ((float) startingPixels); return ((float) endingPixels) / ((float) startingPixels);
} }
std::string toString(int value) std::string toString(int value)
{ {
stringstream ss; stringstream ss;
ss << value; ss << value;
return ss.str(); return ss.str();
} }
std::string toString(uint value) std::string toString(uint value)
{ {
return toString((int) value); return toString((int) value);
} }
std::string toString(float value) std::string toString(float value)
{ {
stringstream ss; stringstream ss;
ss << value; ss << value;
return ss.str(); return ss.str();
} }
std::string toString(double value) std::string toString(double value)
{ {
stringstream ss; stringstream ss;
ss << value; ss << value;
return ss.str(); return ss.str();
}
} }

View File

@@ -33,18 +33,11 @@
#include <vector> #include <vector>
#include "config.h" #include "config.h"
/* namespace alpr
struct LineSegment
{ {
float x1;
float y1;
float x2;
float y2;
};
*/
class LineSegment class LineSegment
{ {
public: public:
cv::Point p1, p2; cv::Point p1, p2;
@@ -78,40 +71,42 @@ class LineSegment
return ss.str() ; return ss.str() ;
} }
}; };
double median(int array[], int arraySize); double median(int array[], int arraySize);
std::vector<cv::Mat> produceThresholds(const cv::Mat img_gray, Config* config); std::vector<cv::Mat> produceThresholds(const cv::Mat img_gray, Config* config);
cv::Mat drawImageDashboard(std::vector<cv::Mat> images, int imageType, uint numColumns); cv::Mat drawImageDashboard(std::vector<cv::Mat> images, int imageType, uint numColumns);
void displayImage(Config* config, std::string windowName, cv::Mat frame); void displayImage(Config* config, std::string windowName, cv::Mat frame);
void drawAndWait(cv::Mat* frame); void drawAndWait(cv::Mat* frame);
double distanceBetweenPoints(cv::Point p1, cv::Point p2); double distanceBetweenPoints(cv::Point p1, cv::Point p2);
void drawRotatedRect(cv::Mat* img, cv::RotatedRect rect, cv::Scalar color, int thickness); void drawRotatedRect(cv::Mat* img, cv::RotatedRect rect, cv::Scalar color, int thickness);
void drawX(cv::Mat img, cv::Rect rect, cv::Scalar color, int thickness); void drawX(cv::Mat img, cv::Rect rect, cv::Scalar color, int thickness);
void fillMask(cv::Mat img, const cv::Mat mask, cv::Scalar color); void fillMask(cv::Mat img, const cv::Mat mask, cv::Scalar color);
float angleBetweenPoints(cv::Point p1, cv::Point p2); float angleBetweenPoints(cv::Point p1, cv::Point p2);
cv::Size getSizeMaintainingAspect(cv::Mat inputImg, int maxWidth, int maxHeight); cv::Size getSizeMaintainingAspect(cv::Mat inputImg, int maxWidth, int maxHeight);
float getContourAreaPercentInsideMask(cv::Mat mask, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, int contourIndex); float getContourAreaPercentInsideMask(cv::Mat mask, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, int contourIndex);
cv::Mat equalizeBrightness(cv::Mat img); cv::Mat equalizeBrightness(cv::Mat img);
cv::Rect expandRect(cv::Rect original, int expandXPixels, int expandYPixels, int maxX, int maxY); cv::Rect expandRect(cv::Rect original, int expandXPixels, int expandYPixels, int maxX, int maxY);
cv::Mat addLabel(cv::Mat input, std::string label); cv::Mat addLabel(cv::Mat input, std::string label);
std::string toString(int value); std::string toString(int value);
std::string toString(uint value); std::string toString(uint value);
std::string toString(float value); std::string toString(float value);
std::string toString(double value); std::string toString(double value);
}
#endif // OPENALPR_UTILITY_H #endif // OPENALPR_UTILITY_H

View File

@@ -14,6 +14,7 @@
using namespace std; using namespace std;
using namespace alpr;
TEST_CASE( "JSON Serialization/Deserialization", "[json]" ) { TEST_CASE( "JSON Serialization/Deserialization", "[json]" ) {

View File

@@ -11,6 +11,7 @@
using namespace std; using namespace std;
using namespace cv; using namespace cv;
using namespace alpr;

View File

@@ -19,6 +19,7 @@
#include "videobuffer.h" #include "videobuffer.h"
using namespace alpr;
void imageCollectionThread(void* arg); void imageCollectionThread(void* arg);
void getALPRImages(cv::VideoCapture cap, VideoDispatcher* dispatcher); void getALPRImages(cv::VideoCapture cap, VideoDispatcher* dispatcher);