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/fileappender.h>
using namespace alpr;
// prototypes
void streamRecognitionThread(void* arg);
bool writeToQueue(std::string jsonResult);

View File

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

View File

@@ -35,6 +35,7 @@
using namespace std;
using namespace cv;
using namespace alpr;
// Given a directory full of lp images (named [statecode]#.png) crop out the alphanumeric characters.
// These will be used to train the OCR

View File

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

View File

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

View File

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

View File

@@ -32,6 +32,7 @@
using namespace std;
using namespace cv;
using namespace alpr;
// Given a directory full of lp images (named [statecode]#.png) crop out the alphanumeric characters.
// These will be used to train the OCR

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -24,8 +24,10 @@ void plateAnalysisThread(void* arg);
using namespace std;
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 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->defaultRegion = "";
}
AlprImpl::~AlprImpl()
{
}
AlprImpl::~AlprImpl()
{
delete config;
if (plateDetector != ALPR_NULL_PTR)
@@ -59,23 +61,23 @@ AlprImpl::~AlprImpl()
if (ocr != ALPR_NULL_PTR)
delete ocr;
}
}
bool AlprImpl::isLoaded()
{
bool AlprImpl::isLoaded()
{
return config->loaded;
}
}
AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img)
{
AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img)
{
std::vector<cv::Rect> regionsOfInterest;
regionsOfInterest.push_back(cv::Rect(0, 0, img.cols, img.rows));
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;
getTime(&startTime);
@@ -249,19 +251,19 @@ AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img, std::vector<cv::Rect
}
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);
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;
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));
}
}
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);
return fullDetails.results;
}
}
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;
}
string AlprImpl::toJson( const AlprResults results )
{
string AlprImpl::toJson( const AlprResults results )
{
cJSON *root, *jsonResults;
root = cJSON_CreateObject();
@@ -343,12 +345,12 @@ string AlprImpl::toJson( const AlprResults results )
free(out);
return response;
}
}
cJSON* AlprImpl::createJsonObj(const AlprPlateResult* result)
{
cJSON* AlprImpl::createJsonObj(const AlprPlateResult* result)
{
cJSON *root, *coords, *candidates;
root=cJSON_CreateObject();
@@ -388,9 +390,9 @@ cJSON* AlprImpl::createJsonObj(const AlprPlateResult* result)
}
return root;
}
}
AlprResults AlprImpl::fromJson(std::string json) {
AlprResults AlprImpl::fromJson(std::string json) {
AlprResults allResults;
cJSON* root = cJSON_Parse(json.c_str());
@@ -468,27 +470,28 @@ AlprResults AlprImpl::fromJson(std::string json) {
return allResults;
}
}
void AlprImpl::setDetectRegion(bool detectRegion)
{
void AlprImpl::setDetectRegion(bool detectRegion)
{
this->detectRegion = detectRegion;
}
void AlprImpl::setTopN(int topn)
{
}
void AlprImpl::setTopN(int topn)
{
this->topN = topn;
}
void AlprImpl::setDefaultRegion(string region)
{
}
void AlprImpl::setDefaultRegion(string region)
{
this->defaultRegion = region;
}
}
std::string AlprImpl::getVersion()
{
std::string AlprImpl::getVersion()
{
std::stringstream ss;
ss << OPENALPR_MAJOR_VERSION << "." << OPENALPR_MINOR_VERSION << "." << OPENALPR_PATCH_VERSION;
return ss.str();
}
}
}

View File

@@ -52,15 +52,17 @@
#define ALPR_NULL_PTR 0
struct AlprFullDetails
namespace alpr
{
struct AlprFullDetails
{
std::vector<PlateRegion> plateRegions;
AlprResults results;
};
};
class AlprImpl
{
class AlprImpl
{
public:
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);
static cJSON* createJsonObj(const AlprPlateResult* result);
};
};
}
#endif // OPENALPR_ALPRIMPL_H

View File

@@ -36,13 +36,15 @@
using namespace std;
using namespace cv;
// *************************************************************
// 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)
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)
{
float m,s,max_s;
long sum, sum_sq;
uchar foo;
@@ -102,15 +104,15 @@ float calcLocalStats (Mat &im, Mat &map_m, Mat &map_s, int winx, int winy)
}
return max_s;
}
}
/**********************************************************
/**********************************************************
* 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)
{
{
float dR = BINARIZEWOLF_DEFAULTDR;
float m, s, max_s;
@@ -242,4 +244,6 @@ void NiblackSauvolaWolfJolion (Mat im, Mat output, NiblackVersion version,
outputdatarow[x]=0;
}
}
}
}

View File

@@ -26,23 +26,27 @@
#include <iostream>
#include "opencv2/opencv.hpp"
enum NiblackVersion
namespace alpr
{
enum NiblackVersion
{
NIBLACK=0,
SAUVOLA,
WOLFJOLION,
};
};
#define BINARIZEWOLF_VERSION "2.3 (February 26th, 2013)"
#define BINARIZEWOLF_DEFAULTDR 128
#define BINARIZEWOLF_VERSION "2.3 (February 26th, 2013)"
#define BINARIZEWOLF_DEFAULTDR 128
#define uget(x,y) at<unsigned char>(y,x)
#define uset(x,y,v) at<unsigned char>(y,x)=v;
#define fget(x,y) at<float>(y,x)
#define fset(x,y,v) at<float>(y,x)=v;
#define uget(x,y) at<unsigned char>(y,x)
#define uset(x,y,v) at<unsigned char>(y,x)=v;
#define fget(x,y) at<float>(y,x)
#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);
}
#endif // OPENALPR_BINARIZEWOLF_H

View File

@@ -22,8 +22,12 @@
using namespace cv;
using namespace std;
ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config)
namespace alpr
{
ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config)
{
timespec startTime;
getTime(&startTime);
@@ -52,14 +56,14 @@ ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config)
getTime(&endTime);
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
for (int row = 0; row < image.rows; row++)
{
@@ -82,21 +86,21 @@ bool ColorFilter::imageIsGrayscale(Mat image)
}
return true;
}
}
void ColorFilter::preprocessImage()
{
void ColorFilter::preprocessImage()
{
// Equalize the brightness on the HSV channel "V"
vector<Mat> channels;
split(this->hsv,channels);
Mat img_equalized = equalizeBrightness(channels[2]);
merge(channels,this->hsv);
}
}
// 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.
void ColorFilter::findCharColors()
{
// 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.
void ColorFilter::findCharColors()
{
int MINIMUM_SATURATION = 45;
if (this->debug)
@@ -362,12 +366,12 @@ void ColorFilter::findCharColors()
Mat dashboard = drawImageDashboard(debugImagesSet, imgDebugHueOnly.type(), 3);
displayImage(config, "Color Filter Images", dashboard);
}
}
}
// Goes through an array of values, picks the winner based on the highest percentage of other values that are within the maxValDifference
// Return -1 if it fails.
int ColorFilter::getMajorityOpinion(vector<float> values, float minPercentAgreement, float 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.
int ColorFilter::getMajorityOpinion(vector<float> values, float minPercentAgreement, float maxValDifference)
{
float bestPercentAgreement = 0;
float lowestOverallDiff = 1000000000;
int bestPercentAgreementIndex = -1;
@@ -395,4 +399,6 @@ int ColorFilter::getMajorityOpinion(vector<float> values, float minPercentAgreem
}
return bestPercentAgreementIndex;
}
}

View File

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

View File

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

View File

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

View File

@@ -22,42 +22,45 @@
using namespace cv;
using namespace std;
Detector::Detector(Config* config)
namespace alpr
{
Detector::Detector(Config* config)
{
this->config = config;
this->scale_factor = 1.0f;
}
}
Detector::~Detector()
{
Detector::~Detector()
{
}
}
bool Detector::isLoaded()
{
bool Detector::isLoaded()
{
return this->loaded;
}
}
vector<PlateRegion> Detector::detect(cv::Mat frame)
{
vector<PlateRegion> Detector::detect(cv::Mat frame)
{
std::vector<cv::Rect> regionsOfInterest;
regionsOfInterest.push_back(Rect(0, 0, frame.cols, frame.rows));
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
std::vector<PlateRegion> 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.
// 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
@@ -106,4 +109,6 @@ vector<PlateRegion> Detector::aggregateRegions(vector<Rect> regions)
return topLevelRegions;
}
}

View File

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

View File

@@ -22,7 +22,10 @@
using namespace cv;
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;
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;
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);
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)
@@ -115,4 +118,6 @@ vector<PlateRegion> DetectorCPU::doCascade(Mat frame, std::vector<cv::Rect> regi
return orderedRegions;
}
}

View File

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

View File

@@ -1,7 +1,11 @@
#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 "config.h"
Detector* createDetector(Config* config);
namespace alpr
{
Detector* createDetector(Config* config);
}
#endif /* OPENALPR_DETECTORFACTORY_H */

View File

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

View File

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

View File

@@ -22,9 +22,12 @@
using namespace cv;
using namespace std;
PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, PipelineData* pipelineData, vector<TextLine> textLines) :
tlc(textLines)
namespace alpr
{
PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, PipelineData* pipelineData, vector<TextLine> textLines) :
tlc(textLines)
{
this->pipelineData = pipelineData;
if (pipelineData->config->debugPlateCorners)
@@ -38,14 +41,14 @@ PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, PipelineData*
this->bestVerticalScore = 9999999999999;
}
}
PlateCorners::~PlateCorners()
{
}
PlateCorners::~PlateCorners()
{
}
vector<Point> PlateCorners::findPlateCorners()
{
vector<Point> PlateCorners::findPlateCorners()
{
if (pipelineData->config->debugPlateCorners)
cout << "PlateCorners::findPlateCorners" << endl;
@@ -120,10 +123,10 @@ vector<Point> PlateCorners::findPlateCorners()
}
return corners;
}
}
void PlateCorners::scoreVerticals(int v1, int v2)
{
void PlateCorners::scoreVerticals(int v1, int v2)
{
ScoreKeeper scoreKeeper;
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);
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.
// If any segments are missing, extrapolate the missing pieces
void PlateCorners::scoreHorizontals(int h1, int h2)
{
}
// Score a collection of lines as a possible license plate region.
// If any segments are missing, extrapolate the missing pieces
void PlateCorners::scoreHorizontals(int h1, int h2)
{
ScoreKeeper scoreKeeper;
@@ -235,7 +238,7 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
if (h1 == NO_LINE && h2 == NO_LINE)
{
// return;
// return;
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);
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
class PlateCorners
namespace alpr
{
class PlateCorners
{
public:
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 scoreVerticals( int v1, int v2 );
};
};
}
#endif // OPENALPR_PLATELINES_H

View File

@@ -24,23 +24,25 @@ using namespace std;
const float MIN_CONFIDENCE = 0.3;
PlateLines::PlateLines(PipelineData* pipelineData)
namespace alpr
{
PlateLines::PlateLines(PipelineData* pipelineData)
{
this->pipelineData = pipelineData;
this->debug = pipelineData->config->debugPlateLines;
if (debug)
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)
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;
}
}
}
vector<PlateLine> PlateLines::getLines(Mat edges, float sensitivityMultiplier, bool vertical)
{
vector<PlateLine> PlateLines::getLines(Mat edges, float sensitivityMultiplier, bool vertical)
{
if (this->debug)
cout << "PlateLines::getLines" << endl;
@@ -219,10 +221,10 @@ vector<PlateLine> PlateLines::getLines(Mat edges, float sensitivityMultiplier, b
}
return filteredLines;
}
}
Mat PlateLines::customGrayscaleConversion(Mat src)
{
Mat PlateLines::customGrayscaleConversion(Mat src)
{
Mat img_hsv;
cvtColor(src,img_hsv,CV_BGR2HSV);
@@ -249,4 +251,6 @@ Mat PlateLines::customGrayscaleConversion(Mat src)
//displayImage(config, "Hue", hue);
return grayscale;
}
}

View File

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

View File

@@ -21,23 +21,26 @@
#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
weight_ids.push_back(weight_id);
scores.push_back(score);
weights.push_back(weight);
}
}
float ScoreKeeper::getTotal() {
float ScoreKeeper::getTotal() {
float score = 0;
@@ -47,9 +50,9 @@ float ScoreKeeper::getTotal() {
}
return score;
}
}
void ScoreKeeper::printDebugScores() {
void ScoreKeeper::printDebugScores() {
int longest_weight_id = 0;
for (unsigned int i = 0; i < weight_ids.size(); i++)
@@ -72,4 +75,6 @@ void ScoreKeeper::printDebugScores() {
std::cout << "Total: " << total << std::endl;
}
}

View File

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

View File

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

View File

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

View File

@@ -22,12 +22,15 @@
using namespace cv;
using namespace std;
//const int DEFAULT_QUERY_FEATURES = 305;
//const int DEFAULT_TRAINING_FEATURES = 305;
const float MAX_DISTANCE_TO_MATCH = 100.0f;
FeatureMatcher::FeatureMatcher(Config* config)
namespace alpr
{
//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->descriptorMatcher = DescriptorMatcher::create( "BruteForce-HammingLUT" );
@@ -37,10 +40,10 @@ FeatureMatcher::FeatureMatcher(Config* config)
this->detector = new FastFeatureDetector(10, true);
this->extractor = new BRISK(10, 1, 0.9);
}
}
FeatureMatcher::~FeatureMatcher()
{
FeatureMatcher::~FeatureMatcher()
{
for (uint i = 0; i < trainingImgKeypoints.size(); i++)
trainingImgKeypoints[i].clear();
trainingImgKeypoints.clear();
@@ -48,26 +51,26 @@ FeatureMatcher::~FeatureMatcher()
descriptorMatcher.release();
detector.release();
extractor.release();
}
}
bool FeatureMatcher::isLoaded()
{
bool FeatureMatcher::isLoaded()
{
if( detector.empty() || extractor.empty() || descriptorMatcher.empty() )
{
return false;
}
return true;
}
}
int FeatureMatcher::numTrainingElements()
{
int FeatureMatcher::numTrainingElements()
{
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<vector<DMatch> > matchesKnn;
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);
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.resize(objectIds.size());
//cout << "starting matcher" << matchesKnn.size() << endl;
@@ -143,12 +146,12 @@ void FeatureMatcher::_surfStyleMatching(const Mat& queryDescriptors, vector<vect
//matches12.push_back(match);
//}
}
}
}
// 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
void FeatureMatcher::crisscrossFiltering(const vector<KeyPoint> queryKeypoints, const vector<DMatch> inputMatches, vector<DMatch> &outputMatches)
{
// 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
void FeatureMatcher::crisscrossFiltering(const vector<KeyPoint> queryKeypoints, const vector<DMatch> inputMatches, vector<DMatch> &outputMatches)
{
Rect crissCrossAreaVertical(0, 0, config->stateIdImageWidthPx, config->stateIdimageHeightPx * 2);
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]]);
}
}
}
}
// Returns true if successful, false otherwise
bool FeatureMatcher::loadRecognitionSet(string country)
{
// Returns true if successful, false otherwise
bool FeatureMatcher::loadRecognitionSet(string country)
{
std::ostringstream out;
out << config->getKeypointsRuntimeDir() << "/" << country << "/";
string country_dir = out.str();
@@ -279,12 +282,12 @@ bool FeatureMatcher::loadRecognitionSet(string country)
}
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
)
{
{
RecognitionResult result;
result.haswinner = false;
@@ -387,4 +390,6 @@ RecognitionResult FeatureMatcher::recognize( const Mat& queryImg, bool drawOnIma
}
return result;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -32,26 +32,29 @@
#define SKIP_CHAR "~"
struct Letter
namespace alpr
{
struct Letter
{
std::string letter;
int charposition;
float totalscore;
int occurences;
};
};
struct PPResult
{
struct PPResult
{
std::string letters;
float totalscore;
bool matchesTemplate;
};
};
bool wordCompare( const PPResult &left, const PPResult &right );
bool letterCompare( const Letter &left, const Letter &right );
bool wordCompare( const PPResult &left, const PPResult &right );
bool letterCompare( const Letter &left, const Letter &right );
class RegexRule
{
class RegexRule
{
public:
RegexRule(std::string region, std::string pattern);
@@ -65,10 +68,10 @@ class RegexRule
std::string regex;
std::string region;
std::vector<int> skipPositions;
};
};
class PostProcess
{
class PostProcess
{
public:
PostProcess(Config* config);
~PostProcess();
@@ -103,25 +106,7 @@ class PostProcess
std::vector<int> getMaxDepth(int topn);
int getPermutationCount(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

View File

@@ -24,8 +24,11 @@
using namespace cv;
using namespace std;
CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data)
namespace alpr
{
CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data)
{
this->pipeline_data = pipeline_data;
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));
}
// if (this->config->debugCharSegmenter && pipeline_data->textLines.size() > 0)
// {
// Mat img_contours(charAnalysis->bestThreshold.size(), CV_8U);
// charAnalysis->bestThreshold.copyTo(img_contours);
// cvtColor(img_contours, img_contours, CV_GRAY2RGB);
//
// vector<vector<Point> > allowedContours;
// for (uint i = 0; i < charAnalysis->bestContours.size(); i++)
// {
// if (charAnalysis->bestContours.goodIndices[i])
// allowedContours.push_back(charAnalysis->bestContours.contours[i]);
// }
//
// drawContours(img_contours, charAnalysis->bestContours.contours,
// -1, // draw all contours
// cv::Scalar(255,0,0), // in blue
// 1); // with a thickness of 1
//
// drawContours(img_contours, allowedContours,
// -1, // draw all contours
// cv::Scalar(0,255,0), // in green
// 1); // with a thickness of 1
//
//
// 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);
//
//
// Mat bordered = addLabel(img_contours, "Best Contours");
// imgDbgGeneral.push_back(bordered);
// }
// if (this->config->debugCharSegmenter && pipeline_data->textLines.size() > 0)
// {
// Mat img_contours(charAnalysis->bestThreshold.size(), CV_8U);
// charAnalysis->bestThreshold.copyTo(img_contours);
// cvtColor(img_contours, img_contours, CV_GRAY2RGB);
//
// vector<vector<Point> > allowedContours;
// for (uint i = 0; i < charAnalysis->bestContours.size(); i++)
// {
// if (charAnalysis->bestContours.goodIndices[i])
// allowedContours.push_back(charAnalysis->bestContours.contours[i]);
// }
//
// drawContours(img_contours, charAnalysis->bestContours.contours,
// -1, // draw all contours
// cv::Scalar(255,0,0), // in blue
// 1); // with a thickness of 1
//
// drawContours(img_contours, allowedContours,
// -1, // draw all contours
// cv::Scalar(0,255,0), // in green
// 1); // with a thickness of 1
//
//
// 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);
//
//
// Mat bordered = addLabel(img_contours, "Best Contours");
// imgDbgGeneral.push_back(bordered);
// }
@@ -130,7 +133,7 @@ CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data)
allHistograms.push_back(addLabel(histoCopy, label));
}
//
//
float score = 0;
vector<Rect> charBoxes = getHistogramBoxes(vertHistogram, avgCharWidth, avgCharHeight, &score);
@@ -226,17 +229,17 @@ CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data)
getTime(&endTime);
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
// 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)
{
// Given a histogram and the horizontal line boundaries, respond with an array of boxes where the characters are
// Scores the histogram quality as well based on num chars, char volume, and even separation
vector<Rect> CharacterSegmenter::getHistogramBoxes(VerticalHistogram histogram, float avgCharWidth, float avgCharHeight, float* score)
{
float MIN_HISTOGRAM_HEIGHT = avgCharHeight * config->segmentationMinCharHeightPercent;
float MAX_SEGMENT_WIDTH = avgCharWidth * config->segmentationMaxCharWidthvsAverage;
@@ -287,10 +290,10 @@ vector<Rect> CharacterSegmenter::getHistogramBoxes(VerticalHistogram histogram,
}
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;
// 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;
}
}
vector<Rect> CharacterSegmenter::get1DHits(Mat img, int yOffset)
{
vector<Rect> CharacterSegmenter::get1DHits(Mat img, int yOffset)
{
vector<Rect> hits;
bool onSegment = false;
@@ -434,10 +437,10 @@ vector<Rect> CharacterSegmenter::get1DHits(Mat img, int yOffset)
}
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_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;
for (uint i = 0; i < charBoxes.size(); i++)
@@ -511,10 +514,10 @@ vector<Rect> CharacterSegmenter::combineCloseBoxes( vector<Rect> charBoxes, floa
}
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_WIDTH_PX = 3;
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);
//Mat element = getStructuringElement( 1,
// Size( 2 + 1, 2+1 ),
// Point( 1, 1 ) );
// Size( 2 + 1, 2+1 ),
// Point( 1, 1 ) );
//dilate(thresholds[i], tempImg, element);
//morphologyEx(thresholds[i], tempImg, MORPH_CLOSE, element);
//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));
}
}
}
}
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)
// Consider it a bad news bear. REmove the whole area.
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"));
//bitwise_and(colorMask, thresholdCopy2, thresholdCopy2);
//tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask + Thresh + Color"));
//
// Mat tmpytmp = addLabel(thresholdCopy2, "box Mask + Thresh + Color");
// Mat tmpx = drawImageDashboard(tmpDebug, tmpytmp.type(), 1);
//
// Mat tmpytmp = addLabel(thresholdCopy2, "box Mask + Thresh + Color");
// Mat tmpx = drawImageDashboard(tmpDebug, tmpytmp.type(), 1);
//drawAndWait( &tmpx );
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;
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,
// clear all data for every box #3.
@@ -799,10 +802,10 @@ vector<Rect> CharacterSegmenter::filterMostlyEmptyBoxes(vector<Mat> thresholds,
}
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;
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;
bool onSegment = false;
@@ -1007,12 +1010,12 @@ int CharacterSegmenter::getLongestBlobLengthBetweenLines(Mat img, int col)
}
return longestBlobLength;
}
}
// 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
int CharacterSegmenter::isSkinnyLineInsideBox(Mat threshold, Rect box, vector<vector<Point> > contours, vector<Vec4i> hierarchy, float avgCharWidth, float avgCharHeight)
{
// 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
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;
// 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;
}
}
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);
for (uint i = 0; i < charBoxes.size(); i++)
rectangle(mask, charBoxes[i], Scalar(255, 255, 255), -1);
return mask;
}
}

View File

@@ -29,20 +29,21 @@
#include "textdetection/textcontours.h"
#include "pipeline_data.h"
//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
namespace alpr
{
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:
CharacterSegmenter(PipelineData* pipeline_data);
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);
};
};
}
#endif // OPENALPR_CHARACTERSEGMENTER_H

View File

@@ -19,18 +19,21 @@
#include "segment.h"
Segment::Segment(cv::Rect newSegment)
namespace alpr
{
Segment::Segment(cv::Rect 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
const float WIDTH_LENIENCY_MIN = 0.25;
const float WIDTH_LENIENCY_MAX = 0.20;
@@ -46,5 +49,6 @@ bool Segment::matches(cv::Rect newSegment)
return true;
return false;
}
}
}

View File

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

View File

@@ -19,23 +19,26 @@
#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);
}
}
bool SegmentationGroup::equals(SegmentationGroup otherGroup)
{
bool SegmentationGroup::equals(SegmentationGroup otherGroup)
{
if (segmentIDs.size() != otherGroup.segmentIDs.size())
return false;
@@ -46,4 +49,6 @@ bool SegmentationGroup::equals(SegmentationGroup otherGroup)
}
return true;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,16 +1,19 @@
#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) {
return true;
}
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())
{
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;
}
}
}
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())
return false;
@@ -32,10 +35,10 @@ bool hasEndingInsensitive(const std::string& fullString, const std::string& endi
if (tolower(fullString[i]) != tolower(ending[i - startidx]))
return false;
return true;
}
}
bool DirectoryExists( const char* pzPath )
{
bool DirectoryExists( const char* pzPath )
{
if ( pzPath == NULL) return false;
DIR *pDir;
@@ -50,10 +53,10 @@ bool DirectoryExists( const char* pzPath )
}
return bExists;
}
}
bool fileExists( const char* pzPath )
{
bool fileExists( const char* pzPath )
{
if (pzPath == NULL) return false;
bool fExists = false;
@@ -61,10 +64,10 @@ bool fileExists( const char* pzPath )
fExists = f.is_open();
f.close();
return fExists;
}
}
std::vector<std::string> getFilesInDir(const char* dirPath)
{
std::vector<std::string> getFilesInDir(const char* dirPath)
{
DIR *dir;
std::vector<std::string> files;
@@ -88,10 +91,10 @@ std::vector<std::string> getFilesInDir(const char* dirPath)
}
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 )
if( tolower( *lit ) < tolower( *rit ) )
return true;
@@ -100,10 +103,12 @@ bool stringCompare( const std::string &left, const std::string &right )
if( left.size() < right.size() )
return true;
return false;
}
}
std::string filenameWithoutExtension(std::string filename)
{
std::string filenameWithoutExtension(std::string filename)
{
int lastindex = filename.find_last_of(".");
return filename.substr(0, lastindex);
}
}

View File

@@ -17,16 +17,21 @@
#include <string.h>
#include <vector>
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);
namespace alpr
{
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 );
bool fileExists( const char* pzPath );
std::vector<std::string> getFilesInDir(const char* dirPath);
std::string filenameWithoutExtension(std::string filename);
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

View File

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

View File

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

View File

@@ -1,12 +1,15 @@
#include "timing.h"
timespec diff(timespec start, timespec end);
#ifdef WINDOWS
// Windows timing code
LARGE_INTEGER getFILETIMEoffset()
namespace alpr
{
timespec diff(timespec start, timespec end);
#ifdef WINDOWS
// Windows timing code
LARGE_INTEGER getFILETIMEoffset()
{
SYSTEMTIME s;
FILETIME f;
LARGE_INTEGER t;
@@ -23,10 +26,10 @@ LARGE_INTEGER getFILETIMEoffset()
t.QuadPart <<= 32;
t.QuadPart |= f.dwLowDateTime;
return (t);
}
}
int clock_gettime(int X, timespec *tv)
{
int clock_gettime(int X, timespec *tv)
{
LARGE_INTEGER t;
FILETIME f;
double microseconds;
@@ -66,23 +69,23 @@ int clock_gettime(int X, timespec *tv)
tv->tv_sec = t.QuadPart / 1000000;
tv->tv_usec = t.QuadPart % 1000000;
return (0);
}
}
void getTime(timespec* time)
{
void getTime(timespec* time)
{
clock_gettime(0, time);
}
}
double diffclock(timespec time1,timespec time2)
{
double diffclock(timespec time1,timespec time2)
{
timespec delta = diff(time1,time2);
double milliseconds = (delta.tv_sec * 1000) + (((double) delta.tv_usec) / 1000.0);
return milliseconds;
}
}
timespec diff(timespec start, timespec end)
{
timespec diff(timespec start, timespec end)
{
timespec temp;
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;
}
return temp;
}
}
long getEpochTime()
{
long getEpochTime()
{
return std::time(0) * 1000;
}
}
#else
#else
void getTime(timespec* time)
{
#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time
void getTime(timespec* time)
{
#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
@@ -115,21 +118,21 @@ void getTime(timespec* time)
mach_port_deallocate(mach_task_self(), cclock);
time->tv_sec = mts.tv_sec;
time->tv_nsec = mts.tv_nsec;
#else
#else
clock_gettime(CLOCK_MONOTONIC, time);
#endif
}
#endif
}
double diffclock(timespec time1,timespec time2)
{
double diffclock(timespec time1,timespec time2)
{
timespec delta = diff(time1,time2);
double milliseconds = (delta.tv_sec * 1000) + (((double) delta.tv_nsec) / 1000000.0);
return milliseconds;
}
}
timespec diff(timespec start, timespec end)
{
timespec diff(timespec start, timespec end)
{
timespec temp;
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;
}
return temp;
}
}
long getEpochTime()
{
long getEpochTime()
{
struct timeval tp;
gettimeofday(&tp, NULL);
long ms = tp.tv_sec * 1000 + tp.tv_usec / 1000;
return ms;
}
#endif
}
#endif

View File

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

View File

@@ -25,10 +25,13 @@
using namespace cv;
using namespace std;
bool sort_text_line(TextLine i, TextLine j) { return (i.topLine.p1.y < j.topLine.p1.y); }
CharacterAnalysis::CharacterAnalysis(PipelineData* pipeline_data)
namespace alpr
{
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->config = pipeline_data->config;
@@ -38,15 +41,15 @@ CharacterAnalysis::CharacterAnalysis(PipelineData* pipeline_data)
cout << "Starting CharacterAnalysis identification" << endl;
this->analyze();
}
}
CharacterAnalysis::~CharacterAnalysis()
{
CharacterAnalysis::~CharacterAnalysis()
{
}
}
void CharacterAnalysis::analyze()
{
void CharacterAnalysis::analyze()
{
pipeline_data->clearThresholds();
pipeline_data->thresholds = produceThresholds(pipeline_data->crop_gray, config);
@@ -234,13 +237,13 @@ void CharacterAnalysis::analyze()
tempDash.push_back(bestVal);
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);
for (uint i = 0; i < bestContours.size(); i++)
@@ -260,11 +263,11 @@ Mat CharacterAnalysis::getCharacterMask()
}
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_MAX_HEIGHT = round (((float) img.rows) * (config->charAnalysisMinPercent + config->charAnalysisHeightRange));
static int HEIGHT_STEP = round (((float) img.rows) * config->charAnalysisHeightStepSize);
@@ -303,11 +306,11 @@ void CharacterAnalysis::filter(Mat img, TextContours& textContours)
}
textContours.setIndices(bestIndices);
}
}
// 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)
{
// 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)
{
float idealAspect=config->charWidthMM / config->charHeightMM;
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++)
{
@@ -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
// returns a vector of indices corresponding to valid contours
void CharacterAnalysis::filterByParentContour( TextContours& textContours)
{
// 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
void CharacterAnalysis::filterByParentContour( TextContours& textContours)
{
vector<int> parentIDs;
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 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_OF_CHARS_INSIDE_PLATE_MASK = 0.6;
@@ -570,10 +573,10 @@ void CharacterAnalysis::filterByOuterMask(TextContours& textContours)
return;
}
}
}
bool CharacterAnalysis::isPlateInverted()
{
bool CharacterAnalysis::isPlateInverted()
{
Mat charMask = getCharacterMask();
@@ -586,10 +589,10 @@ bool CharacterAnalysis::isPlateInverted()
return true;
return false;
}
}
bool CharacterAnalysis::verifySize(Mat r, float minHeightPx, float maxHeightPx)
{
bool CharacterAnalysis::verifySize(Mat r, float minHeightPx, float maxHeightPx)
{
//Char sizes 45x90
float aspect=config->charWidthMM / config->charHeightMM;
float charAspect= (float)r.cols/(float)r.rows;
@@ -612,10 +615,10 @@ bool CharacterAnalysis::verifySize(Mat r, float minHeightPx, float maxHeightPx)
return true;
else
return false;
}
}
vector<Point> CharacterAnalysis::getCharArea(LineSegment topLine, LineSegment bottomLine)
{
vector<Point> CharacterAnalysis::getCharArea(LineSegment topLine, LineSegment bottomLine)
{
const int MAX = 100000;
const int MIN= -1;
@@ -650,4 +653,6 @@ vector<Point> CharacterAnalysis::getCharArea(LineSegment topLine, LineSegment bo
}
return charArea;
}
}

View File

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

View File

@@ -26,15 +26,18 @@
using namespace std;
using namespace cv;
LineFinder::LineFinder(PipelineData* pipeline_data) {
this->pipeline_data = pipeline_data;
}
LineFinder::~LineFinder() {
}
vector<vector<Point> > LineFinder::findLines(Mat image, const TextContours contours)
namespace alpr
{
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;
vector<vector<Point> > linesFound;
@@ -83,12 +86,12 @@ vector<vector<Point> > LineFinder::findLines(Mat image, const TextContours conto
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
vector<Point> LineFinder::getBestLine(const TextContours contours, vector<CharPointInfo> charPoints)
{
// 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> bestStripe;
// 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
// if (abs(top.angle) <= pipeline_data->config->maxPlateAngleDegrees &&
// abs(bottom.angle) <= pipeline_data->config->maxPlateAngleDegrees)
// {
// topLines.push_back(top);
// bottomLines.push_back(bottom);
// }
// if (abs(top.angle) <= pipeline_data->config->maxPlateAngleDegrees &&
// abs(bottom.angle) <= pipeline_data->config->maxPlateAngleDegrees)
// {
// topLines.push_back(top);
// bottomLines.push_back(bottom);
// }
LineSegment parallelBot = top.getParallelLine(medianCharHeight * -1);
LineSegment parallelTop = bottom.getParallelLine(medianCharHeight);
@@ -230,9 +233,9 @@ vector<Point> LineFinder::getBestLine(const TextContours contours, vector<CharPo
return bestStripe;
}
}
CharPointInfo::CharPointInfo(vector<Point> contour, int index) {
CharPointInfo::CharPointInfo(vector<Point> contour, int index) {
this->contourIndex = index;
@@ -250,4 +253,6 @@ CharPointInfo::CharPointInfo(vector<Point> contour, int index) {
this->bottom = Point(x,y);
}
}

View File

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

View File

@@ -22,22 +22,25 @@
using namespace std;
using namespace cv;
PlateMask::PlateMask(PipelineData* pipeline_data) {
namespace alpr
{
PlateMask::PlateMask(PipelineData* pipeline_data) {
this->pipeline_data = pipeline_data;
this->hasPlateMask = false;
}
}
PlateMask::~PlateMask() {
}
PlateMask::~PlateMask() {
}
cv::Mat PlateMask::getMask() {
cv::Mat PlateMask::getMask() {
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.
int winningIndex = -1;
@@ -196,4 +199,6 @@ void PlateMask::findOuterBoxMask( vector<TextContours > contours )
bitwise_not(fullMask, fullMask);
this->plateMask = fullMask;
}
}

View File

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

View File

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

View File

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

View File

@@ -24,7 +24,10 @@
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;
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)));
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);
}
}
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 (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;
}
}
}
cv::Mat TextLine::drawDebugImage(cv::Mat baseImage) {
cv::Mat TextLine::drawDebugImage(cv::Mat baseImage) {
cv::Mat debugImage(baseImage.size(), baseImage.type());
baseImage.copyTo(debugImage);
@@ -104,4 +107,6 @@ cv::Mat TextLine::drawDebugImage(cv::Mat baseImage) {
return debugImage;
}
}

View File

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

View File

@@ -22,30 +22,33 @@
using namespace std;
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->smallImage = smallImage;
this->regionInBigImage = regionInBigImage;
}
}
Transformation::~Transformation() {
}
Transformation::~Transformation() {
}
// Re-maps the coordinates from the smallImage to the coordinate space of the bigImage.
vector<Point2f> Transformation::transformSmallPointsToBigImage(vector<Point> points)
{
// Re-maps the coordinates from the smallImage to the coordinate space of the bigImage.
vector<Point2f> Transformation::transformSmallPointsToBigImage(vector<Point> points)
{
vector<Point2f> floatPoints;
for (unsigned int i = 0; i < points.size(); i++)
floatPoints.push_back(points[i]);
return transformSmallPointsToBigImage(floatPoints);
}
}
// Re-maps the coordinates from the smallImage to the coordinate space of the bigImage.
vector<Point2f> Transformation::transformSmallPointsToBigImage(vector<Point2f> points)
{
// Re-maps the coordinates from the smallImage to the coordinate space of the bigImage.
vector<Point2f> Transformation::transformSmallPointsToBigImage(vector<Point2f> points)
{
vector<Point2f> bigPoints;
for (uint i = 0; i < points.size(); i++)
{
@@ -59,11 +62,11 @@ vector<Point2f> Transformation::transformSmallPointsToBigImage(vector<Point2f> p
}
return bigPoints;
}
}
Mat Transformation::getTransformationMatrix(vector<Point2f> corners, Size outputImageSize)
{
Mat Transformation::getTransformationMatrix(vector<Point2f> corners, Size outputImageSize)
{
// Corners of the destination image
vector<Point2f> quad_pts;
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));
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
Mat transmtx = getPerspectiveTransform(corners, outputCorners);
return transmtx;
}
}
Mat Transformation::crop(Size outputImageSize, Mat transformationMatrix)
{
Mat Transformation::crop(Size outputImageSize, Mat transformationMatrix)
{
Mat deskewed(outputImageSize, this->bigImage.type());
@@ -97,27 +100,27 @@ Mat Transformation::crop(Size outputImageSize, Mat transformationMatrix)
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;
for (unsigned int i = 0; i < smallPoints.size(); i++)
floatPoints.push_back(smallPoints[i]);
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;
perspectiveTransform(smallPoints, remappedPoints, transformationMatrix);
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.
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));
@@ -136,4 +139,6 @@ Size Transformation::getCropSize(vector<Point2f> areaCorners, Size targetSize)
}
return Size(width, height);
}
}

View File

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

View File

@@ -24,8 +24,11 @@
using namespace cv;
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);
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;
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);
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;
}
}
Mat addLabel(Mat input, string label)
{
Mat addLabel(Mat input, string label)
{
const int border_size = 1;
const Scalar border_color(0,0,255);
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);
return newImage;
}
}
void drawAndWait(cv::Mat* frame)
{
void drawAndWait(cv::Mat* frame)
{
cv::imshow("Temp Window", *frame);
while (cv::waitKey(50) == -1)
@@ -101,19 +104,19 @@ void drawAndWait(cv::Mat* frame)
}
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)
{
imshow(windowName, frame);
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;
//Mat img_equalized = equalizeBrightness(img_gray);
@@ -163,10 +166,10 @@ vector<Mat> produceThresholds(const Mat img_gray, Config* config)
return thresholds;
//threshold(img_equalized, img_threshold, 100, 255, THRESH_BINARY);
}
}
double median(int array[], int arraySize)
{
double median(int array[], int arraySize)
{
if (arraySize == 0)
{
//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]);
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
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(19,19));
Mat closed;
@@ -190,18 +193,18 @@ Mat equalizeBrightness(Mat img)
img.convertTo(img, CV_8U); // convert back to unsigned int
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];
rect.points( rect_points );
for( int j = 0; j < 4; j++ )
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 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 tr(rect.x + rect.width, rect.y);
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, 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 bsquared = (p2.y - p1.y)*(p2.y - p1.y);
return sqrt(asquared + bsquared);
}
}
float angleBetweenPoints(Point p1, Point p2)
{
float angleBetweenPoints(Point p1, Point p2)
{
int deltaY = p2.y - p1.y;
int deltaX = p2.x - p1.x;
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);
if (maxWidth / aspect > maxHeight)
@@ -259,25 +262,25 @@ Size getSizeMaintainingAspect(Mat inputImg, int maxWidth, int maxHeight)
{
return Size(maxWidth, maxWidth / aspect);
}
}
}
LineSegment::LineSegment()
{
LineSegment::LineSegment()
{
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);
}
}
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);
}
}
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->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->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;
}
}
float LineSegment::getPointAt(float x)
{
float LineSegment::getPointAt(float x)
{
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 bottom = distanceBetweenPoints(p2, p1);
@@ -314,10 +317,10 @@ Point LineSegment::closestPointOnSegmentTo(Point p)
float y = p1.y + u * (p2.y - p1.y);
return Point(x, y);
}
}
Point LineSegment::intersection(LineSegment line)
{
Point LineSegment::intersection(LineSegment line)
{
float c1, c2;
float intersection_X = -1, intersection_Y= -1;
@@ -346,10 +349,10 @@ Point LineSegment::intersection(LineSegment line)
}
return Point(intersection_X, intersection_Y);
}
}
Point LineSegment::midpoint()
{
Point LineSegment::midpoint()
{
// Handle the case where the line is vertical
if (p1.x == p2.x)
{
@@ -362,10 +365,10 @@ Point LineSegment::midpoint()
int midY = getPointAt(midX);
return Point(midX, midY);
}
}
LineSegment LineSegment::getParallelLine(float distance)
{
LineSegment LineSegment::getParallelLine(float distance)
{
float diff_x = p2.x - p1.x;
float diff_y = p2.y - p1.y;
float angle = atan2( diff_x, diff_y);
@@ -379,12 +382,12 @@ LineSegment LineSegment::getParallelLine(float distance)
p2.x + offsetX, p2.y + offsetY);
return result;
}
}
// Given a contour and a mask, this function determines what percentage of the contour (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)
{
// Given a contour and a mask, this function determines what percentage of the contour (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)
{
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);
}
}
std::string toString(int value)
{
std::string toString(int value)
{
stringstream ss;
ss << value;
return ss.str();
}
std::string toString(uint value)
{
}
std::string toString(uint value)
{
return toString((int) value);
}
std::string toString(float value)
{
}
std::string toString(float value)
{
stringstream ss;
ss << value;
return ss.str();
}
std::string toString(double value)
{
}
std::string toString(double value)
{
stringstream ss;
ss << value;
return ss.str();
}
}

View File

@@ -33,18 +33,11 @@
#include <vector>
#include "config.h"
/*
struct LineSegment
namespace alpr
{
float x1;
float y1;
float x2;
float y2;
};
*/
class LineSegment
{
class LineSegment
{
public:
cv::Point p1, p2;
@@ -78,40 +71,42 @@ class LineSegment
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 drawAndWait(cv::Mat* frame);
void displayImage(Config* config, std::string windowName, 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 fillMask(cv::Mat img, const cv::Mat mask, cv::Scalar color);
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);
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(uint value);
std::string toString(float value);
std::string toString(double value);
std::string toString(int value);
std::string toString(uint value);
std::string toString(float value);
std::string toString(double value);
}
#endif // OPENALPR_UTILITY_H

View File

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

View File

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

View File

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