mirror of
https://github.com/kerberos-io/openalpr-base.git
synced 2025-10-06 06:46:53 +08:00
Wrapped OpenALPR library in "alpr" namespace. Resolves issue #60.
This commit is contained in:
@@ -21,6 +21,8 @@
|
|||||||
#include <log4cplus/consoleappender.h>
|
#include <log4cplus/consoleappender.h>
|
||||||
#include <log4cplus/fileappender.h>
|
#include <log4cplus/fileappender.h>
|
||||||
|
|
||||||
|
using namespace alpr;
|
||||||
|
|
||||||
// prototypes
|
// prototypes
|
||||||
void streamRecognitionThread(void* arg);
|
void streamRecognitionThread(void* arg);
|
||||||
bool writeToQueue(std::string jsonResult);
|
bool writeToQueue(std::string jsonResult);
|
||||||
|
@@ -33,6 +33,8 @@
|
|||||||
#include "video/videobuffer.h"
|
#include "video/videobuffer.h"
|
||||||
#include "alpr.h"
|
#include "alpr.h"
|
||||||
|
|
||||||
|
using namespace alpr;
|
||||||
|
|
||||||
const std::string MAIN_WINDOW_NAME = "ALPR main window";
|
const std::string MAIN_WINDOW_NAME = "ALPR main window";
|
||||||
|
|
||||||
const bool SAVE_LAST_VIDEO_STILL = false;
|
const bool SAVE_LAST_VIDEO_STILL = false;
|
||||||
|
@@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
using namespace alpr;
|
||||||
|
|
||||||
// Given a directory full of lp images (named [statecode]#.png) crop out the alphanumeric characters.
|
// Given a directory full of lp images (named [statecode]#.png) crop out the alphanumeric characters.
|
||||||
// These will be used to train the OCR
|
// These will be used to train the OCR
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
#include "benchmark_utils.h"
|
#include "benchmark_utils.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace alpr;
|
||||||
|
|
||||||
vector<string> filterByExtension(vector<string> fileList, string extension)
|
vector<string> filterByExtension(vector<string> fileList, string extension)
|
||||||
{
|
{
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
using namespace alpr;
|
||||||
|
|
||||||
|
|
||||||
EndToEndTest::EndToEndTest(string inputDir, string outputDir)
|
EndToEndTest::EndToEndTest(string inputDir, string outputDir)
|
||||||
|
@@ -15,8 +15,8 @@ class EndToEndTest
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool rectMatches(cv::Rect actualPlate, PlateRegion candidate);
|
bool rectMatches(cv::Rect actualPlate, alpr::PlateRegion candidate);
|
||||||
int totalRectCount(PlateRegion rootCandidate);
|
int totalRectCount(alpr::PlateRegion rootCandidate);
|
||||||
|
|
||||||
std::string inputDir;
|
std::string inputDir;
|
||||||
std::string outputDir;
|
std::string outputDir;
|
||||||
|
@@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
using namespace alpr;
|
||||||
|
|
||||||
// Given a directory full of lp images (named [statecode]#.png) crop out the alphanumeric characters.
|
// Given a directory full of lp images (named [statecode]#.png) crop out the alphanumeric characters.
|
||||||
// These will be used to train the OCR
|
// These will be used to train the OCR
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
using namespace alpr;
|
||||||
|
|
||||||
// Takes a directory full of single char images, and plops them on a big tif files
|
// Takes a directory full of single char images, and plops them on a big tif files
|
||||||
// Also creates a box file so Tesseract can recognize it
|
// Also creates a box file so Tesseract can recognize it
|
||||||
|
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
using namespace alpr;
|
||||||
|
|
||||||
// Given a directory full of pre-cropped images, identify the state that each image belongs to.
|
// Given a directory full of pre-cropped images, identify the state that each image belongs to.
|
||||||
// This is used to sort our own positive image database as a first step before grabbing characters to use to train the OCR.
|
// This is used to sort our own positive image database as a first step before grabbing characters to use to train the OCR.
|
||||||
|
@@ -58,6 +58,7 @@ const int UP_ARROW_KEY= 82;
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
using namespace alpr;
|
||||||
|
|
||||||
static bool ldragging = false;
|
static bool ldragging = false;
|
||||||
static int xPos1 = 0;
|
static int xPos1 = 0;
|
||||||
|
@@ -20,20 +20,23 @@
|
|||||||
#include "alpr.h"
|
#include "alpr.h"
|
||||||
#include "alpr_impl.h"
|
#include "alpr_impl.h"
|
||||||
|
|
||||||
// ALPR code
|
namespace alpr
|
||||||
|
|
||||||
Alpr::Alpr(const std::string country, const std::string configFile, const std::string runtimeDir)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// ALPR code
|
||||||
|
|
||||||
|
Alpr::Alpr(const std::string country, const std::string configFile, const std::string runtimeDir)
|
||||||
|
{
|
||||||
impl = new AlprImpl(country, configFile, runtimeDir);
|
impl = new AlprImpl(country, configFile, runtimeDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
Alpr::~Alpr()
|
Alpr::~Alpr()
|
||||||
{
|
{
|
||||||
delete impl;
|
delete impl;
|
||||||
}
|
}
|
||||||
|
|
||||||
AlprResults Alpr::recognize(std::string filepath)
|
AlprResults Alpr::recognize(std::string filepath)
|
||||||
{
|
{
|
||||||
|
|
||||||
std::ifstream ifs(filepath.c_str(), std::ios::binary|std::ios::ate);
|
std::ifstream ifs(filepath.c_str(), std::ios::binary|std::ios::ate);
|
||||||
std::ifstream::pos_type pos = ifs.tellg();
|
std::ifstream::pos_type pos = ifs.tellg();
|
||||||
@@ -44,52 +47,53 @@ AlprResults Alpr::recognize(std::string filepath)
|
|||||||
ifs.read(&buffer[0], pos);
|
ifs.read(&buffer[0], pos);
|
||||||
|
|
||||||
return this->recognize( buffer );
|
return this->recognize( buffer );
|
||||||
}
|
}
|
||||||
|
|
||||||
AlprResults Alpr::recognize(std::vector<char> imageBytes)
|
AlprResults Alpr::recognize(std::vector<char> imageBytes)
|
||||||
{
|
{
|
||||||
std::vector<AlprRegionOfInterest> regionsOfInterest;
|
std::vector<AlprRegionOfInterest> regionsOfInterest;
|
||||||
return impl->recognize(imageBytes, regionsOfInterest);
|
return impl->recognize(imageBytes, regionsOfInterest);
|
||||||
}
|
}
|
||||||
|
|
||||||
AlprResults Alpr::recognize(unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight, std::vector<AlprRegionOfInterest> regionsOfInterest)
|
AlprResults Alpr::recognize(unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight, std::vector<AlprRegionOfInterest> regionsOfInterest)
|
||||||
{
|
{
|
||||||
return impl->recognize(pixelData, bytesPerPixel, imgWidth, imgHeight, regionsOfInterest);
|
return impl->recognize(pixelData, bytesPerPixel, imgWidth, imgHeight, regionsOfInterest);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Alpr::toJson( AlprResults results )
|
std::string Alpr::toJson( AlprResults results )
|
||||||
{
|
{
|
||||||
return AlprImpl::toJson(results);
|
return AlprImpl::toJson(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
AlprResults Alpr::fromJson(std::string json) {
|
AlprResults Alpr::fromJson(std::string json) {
|
||||||
return AlprImpl::fromJson(json);
|
return AlprImpl::fromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Alpr::setDetectRegion(bool detectRegion)
|
void Alpr::setDetectRegion(bool detectRegion)
|
||||||
{
|
{
|
||||||
impl->setDetectRegion(detectRegion);
|
impl->setDetectRegion(detectRegion);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Alpr::setTopN(int topN)
|
void Alpr::setTopN(int topN)
|
||||||
{
|
{
|
||||||
impl->setTopN(topN);
|
impl->setTopN(topN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Alpr::setDefaultRegion(std::string region)
|
void Alpr::setDefaultRegion(std::string region)
|
||||||
{
|
{
|
||||||
impl->setDefaultRegion(region);
|
impl->setDefaultRegion(region);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Alpr::isLoaded()
|
bool Alpr::isLoaded()
|
||||||
{
|
{
|
||||||
return impl->isLoaded();
|
return impl->isLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Alpr::getVersion()
|
std::string Alpr::getVersion()
|
||||||
{
|
{
|
||||||
return AlprImpl::getVersion();
|
return AlprImpl::getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -24,23 +24,26 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
struct AlprPlate
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct AlprPlate
|
||||||
|
{
|
||||||
std::string characters;
|
std::string characters;
|
||||||
float overall_confidence;
|
float overall_confidence;
|
||||||
|
|
||||||
bool matches_template;
|
bool matches_template;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AlprCoordinate
|
struct AlprCoordinate
|
||||||
{
|
{
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AlprRegionOfInterest
|
class AlprRegionOfInterest
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AlprRegionOfInterest();
|
AlprRegionOfInterest();
|
||||||
AlprRegionOfInterest(int x, int y, int width, int height)
|
AlprRegionOfInterest(int x, int y, int width, int height)
|
||||||
{
|
{
|
||||||
@@ -54,10 +57,10 @@ public:
|
|||||||
int y;
|
int y;
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AlprPlateResult
|
class AlprPlateResult
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AlprPlateResult() {};
|
AlprPlateResult() {};
|
||||||
virtual ~AlprPlateResult() {};
|
virtual ~AlprPlateResult() {};
|
||||||
@@ -72,10 +75,10 @@ class AlprPlateResult
|
|||||||
|
|
||||||
int regionConfidence;
|
int regionConfidence;
|
||||||
std::string region;
|
std::string region;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AlprResults
|
class AlprResults
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AlprResults() {};
|
AlprResults() {};
|
||||||
virtual ~AlprResults() {};
|
virtual ~AlprResults() {};
|
||||||
@@ -89,12 +92,12 @@ class AlprResults
|
|||||||
|
|
||||||
std::vector<AlprRegionOfInterest> regionsOfInterest;
|
std::vector<AlprRegionOfInterest> regionsOfInterest;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class AlprImpl;
|
class AlprImpl;
|
||||||
class Alpr
|
class Alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Alpr(const std::string country, const std::string configFile = "", const std::string runtimeDir = "");
|
Alpr(const std::string country, const std::string configFile = "", const std::string runtimeDir = "");
|
||||||
@@ -123,6 +126,7 @@ class Alpr
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
AlprImpl* impl;
|
AlprImpl* impl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
#endif // OPENALPR_APLR_H
|
#endif // OPENALPR_APLR_H
|
||||||
|
@@ -24,8 +24,10 @@ void plateAnalysisThread(void* arg);
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
AlprImpl::AlprImpl(const std::string country, const std::string configFile, const std::string runtimeDir)
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
AlprImpl::AlprImpl(const std::string country, const std::string configFile, const std::string runtimeDir)
|
||||||
|
{
|
||||||
config = new Config(country, configFile, runtimeDir);
|
config = new Config(country, configFile, runtimeDir);
|
||||||
|
|
||||||
// Config file or runtime dir not found. Don't process any further.
|
// Config file or runtime dir not found. Don't process any further.
|
||||||
@@ -46,9 +48,9 @@ AlprImpl::AlprImpl(const std::string country, const std::string configFile, cons
|
|||||||
this->topN = DEFAULT_TOPN;
|
this->topN = DEFAULT_TOPN;
|
||||||
this->defaultRegion = "";
|
this->defaultRegion = "";
|
||||||
|
|
||||||
}
|
}
|
||||||
AlprImpl::~AlprImpl()
|
AlprImpl::~AlprImpl()
|
||||||
{
|
{
|
||||||
delete config;
|
delete config;
|
||||||
|
|
||||||
if (plateDetector != ALPR_NULL_PTR)
|
if (plateDetector != ALPR_NULL_PTR)
|
||||||
@@ -59,23 +61,23 @@ AlprImpl::~AlprImpl()
|
|||||||
|
|
||||||
if (ocr != ALPR_NULL_PTR)
|
if (ocr != ALPR_NULL_PTR)
|
||||||
delete ocr;
|
delete ocr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AlprImpl::isLoaded()
|
bool AlprImpl::isLoaded()
|
||||||
{
|
{
|
||||||
return config->loaded;
|
return config->loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img)
|
AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img)
|
||||||
{
|
{
|
||||||
std::vector<cv::Rect> regionsOfInterest;
|
std::vector<cv::Rect> regionsOfInterest;
|
||||||
regionsOfInterest.push_back(cv::Rect(0, 0, img.cols, img.rows));
|
regionsOfInterest.push_back(cv::Rect(0, 0, img.cols, img.rows));
|
||||||
|
|
||||||
return this->recognizeFullDetails(img, regionsOfInterest);
|
return this->recognizeFullDetails(img, regionsOfInterest);
|
||||||
}
|
}
|
||||||
|
|
||||||
AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img, std::vector<cv::Rect> regionsOfInterest)
|
AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img, std::vector<cv::Rect> regionsOfInterest)
|
||||||
{
|
{
|
||||||
timespec startTime;
|
timespec startTime;
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
|
|
||||||
@@ -249,19 +251,19 @@ AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img, std::vector<cv::Rect
|
|||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AlprResults AlprImpl::recognize( std::vector<char> imageBytes, std::vector<AlprRegionOfInterest> regionsOfInterest )
|
AlprResults AlprImpl::recognize( std::vector<char> imageBytes, std::vector<AlprRegionOfInterest> regionsOfInterest )
|
||||||
{
|
{
|
||||||
cv::Mat img = cv::imdecode(cv::Mat(imageBytes), 1);
|
cv::Mat img = cv::imdecode(cv::Mat(imageBytes), 1);
|
||||||
|
|
||||||
return this->recognize(img, this->convertRects(regionsOfInterest));
|
return this->recognize(img, this->convertRects(regionsOfInterest));
|
||||||
}
|
}
|
||||||
|
|
||||||
AlprResults AlprImpl::recognize( unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight, std::vector<AlprRegionOfInterest> regionsOfInterest)
|
AlprResults AlprImpl::recognize( unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight, std::vector<AlprRegionOfInterest> regionsOfInterest)
|
||||||
{
|
{
|
||||||
|
|
||||||
int arraySize = imgWidth * imgHeight * bytesPerPixel;
|
int arraySize = imgWidth * imgHeight * bytesPerPixel;
|
||||||
cv::Mat imgData = cv::Mat(arraySize, 1, CV_8U, pixelData);
|
cv::Mat imgData = cv::Mat(arraySize, 1, CV_8U, pixelData);
|
||||||
@@ -275,14 +277,14 @@ AlprResults AlprImpl::recognize( unsigned char* pixelData, int bytesPerPixel, in
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this->recognize(img, this->convertRects(regionsOfInterest));
|
return this->recognize(img, this->convertRects(regionsOfInterest));
|
||||||
}
|
}
|
||||||
|
|
||||||
AlprResults AlprImpl::recognize(cv::Mat img, std::vector<cv::Rect> regionsOfInterest)
|
AlprResults AlprImpl::recognize(cv::Mat img, std::vector<cv::Rect> regionsOfInterest)
|
||||||
{
|
{
|
||||||
|
|
||||||
AlprFullDetails fullDetails = recognizeFullDetails(img, regionsOfInterest);
|
AlprFullDetails fullDetails = recognizeFullDetails(img, regionsOfInterest);
|
||||||
return fullDetails.results;
|
return fullDetails.results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<cv::Rect> AlprImpl::convertRects(std::vector<AlprRegionOfInterest> regionsOfInterest)
|
std::vector<cv::Rect> AlprImpl::convertRects(std::vector<AlprRegionOfInterest> regionsOfInterest)
|
||||||
@@ -296,8 +298,8 @@ AlprResults AlprImpl::recognize(cv::Mat img, std::vector<cv::Rect> regionsOfInte
|
|||||||
return rectRegions;
|
return rectRegions;
|
||||||
}
|
}
|
||||||
|
|
||||||
string AlprImpl::toJson( const AlprResults results )
|
string AlprImpl::toJson( const AlprResults results )
|
||||||
{
|
{
|
||||||
cJSON *root, *jsonResults;
|
cJSON *root, *jsonResults;
|
||||||
root = cJSON_CreateObject();
|
root = cJSON_CreateObject();
|
||||||
|
|
||||||
@@ -343,12 +345,12 @@ string AlprImpl::toJson( const AlprResults results )
|
|||||||
|
|
||||||
free(out);
|
free(out);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cJSON* AlprImpl::createJsonObj(const AlprPlateResult* result)
|
cJSON* AlprImpl::createJsonObj(const AlprPlateResult* result)
|
||||||
{
|
{
|
||||||
cJSON *root, *coords, *candidates;
|
cJSON *root, *coords, *candidates;
|
||||||
|
|
||||||
root=cJSON_CreateObject();
|
root=cJSON_CreateObject();
|
||||||
@@ -388,9 +390,9 @@ cJSON* AlprImpl::createJsonObj(const AlprPlateResult* result)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
AlprResults AlprImpl::fromJson(std::string json) {
|
AlprResults AlprImpl::fromJson(std::string json) {
|
||||||
AlprResults allResults;
|
AlprResults allResults;
|
||||||
|
|
||||||
cJSON* root = cJSON_Parse(json.c_str());
|
cJSON* root = cJSON_Parse(json.c_str());
|
||||||
@@ -468,27 +470,28 @@ AlprResults AlprImpl::fromJson(std::string json) {
|
|||||||
|
|
||||||
|
|
||||||
return allResults;
|
return allResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AlprImpl::setDetectRegion(bool detectRegion)
|
void AlprImpl::setDetectRegion(bool detectRegion)
|
||||||
{
|
{
|
||||||
this->detectRegion = detectRegion;
|
this->detectRegion = detectRegion;
|
||||||
}
|
}
|
||||||
void AlprImpl::setTopN(int topn)
|
void AlprImpl::setTopN(int topn)
|
||||||
{
|
{
|
||||||
this->topN = topn;
|
this->topN = topn;
|
||||||
}
|
}
|
||||||
void AlprImpl::setDefaultRegion(string region)
|
void AlprImpl::setDefaultRegion(string region)
|
||||||
{
|
{
|
||||||
this->defaultRegion = region;
|
this->defaultRegion = region;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string AlprImpl::getVersion()
|
std::string AlprImpl::getVersion()
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
ss << OPENALPR_MAJOR_VERSION << "." << OPENALPR_MINOR_VERSION << "." << OPENALPR_PATCH_VERSION;
|
ss << OPENALPR_MAJOR_VERSION << "." << OPENALPR_MINOR_VERSION << "." << OPENALPR_PATCH_VERSION;
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
@@ -52,15 +52,17 @@
|
|||||||
|
|
||||||
#define ALPR_NULL_PTR 0
|
#define ALPR_NULL_PTR 0
|
||||||
|
|
||||||
|
namespace alpr
|
||||||
struct AlprFullDetails
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct AlprFullDetails
|
||||||
|
{
|
||||||
std::vector<PlateRegion> plateRegions;
|
std::vector<PlateRegion> plateRegions;
|
||||||
AlprResults results;
|
AlprResults results;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AlprImpl
|
class AlprImpl
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AlprImpl(const std::string country, const std::string configFile = "", const std::string runtimeDir = "");
|
AlprImpl(const std::string country, const std::string configFile = "", const std::string runtimeDir = "");
|
||||||
@@ -100,8 +102,8 @@ class AlprImpl
|
|||||||
std::vector<cv::Rect> convertRects(std::vector<AlprRegionOfInterest> regionsOfInterest);
|
std::vector<cv::Rect> convertRects(std::vector<AlprRegionOfInterest> regionsOfInterest);
|
||||||
|
|
||||||
static cJSON* createJsonObj(const AlprPlateResult* result);
|
static cJSON* createJsonObj(const AlprPlateResult* result);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif // OPENALPR_ALPRIMPL_H
|
#endif // OPENALPR_ALPRIMPL_H
|
@@ -36,13 +36,15 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
// *************************************************************
|
namespace alpr
|
||||||
// glide a window across the image and
|
|
||||||
// create two maps: mean and standard deviation.
|
|
||||||
// *************************************************************
|
|
||||||
|
|
||||||
float calcLocalStats (Mat &im, Mat &map_m, Mat &map_s, int winx, int winy)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// *************************************************************
|
||||||
|
// glide a window across the image and
|
||||||
|
// create two maps: mean and standard deviation.
|
||||||
|
// *************************************************************
|
||||||
|
float calcLocalStats (Mat &im, Mat &map_m, Mat &map_s, int winx, int winy)
|
||||||
|
{
|
||||||
float m,s,max_s;
|
float m,s,max_s;
|
||||||
long sum, sum_sq;
|
long sum, sum_sq;
|
||||||
uchar foo;
|
uchar foo;
|
||||||
@@ -102,15 +104,15 @@ float calcLocalStats (Mat &im, Mat &map_m, Mat &map_s, int winx, int winy)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return max_s;
|
return max_s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**********************************************************
|
/**********************************************************
|
||||||
* The binarization routine
|
* The binarization routine
|
||||||
**********************************************************/
|
**********************************************************/
|
||||||
|
|
||||||
void NiblackSauvolaWolfJolion (Mat im, Mat output, NiblackVersion version,
|
void NiblackSauvolaWolfJolion (Mat im, Mat output, NiblackVersion version,
|
||||||
int winx, int winy, float k)
|
int winx, int winy, float k)
|
||||||
{
|
{
|
||||||
float dR = BINARIZEWOLF_DEFAULTDR;
|
float dR = BINARIZEWOLF_DEFAULTDR;
|
||||||
|
|
||||||
float m, s, max_s;
|
float m, s, max_s;
|
||||||
@@ -242,4 +244,6 @@ void NiblackSauvolaWolfJolion (Mat im, Mat output, NiblackVersion version,
|
|||||||
outputdatarow[x]=0;
|
outputdatarow[x]=0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -26,23 +26,27 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "opencv2/opencv.hpp"
|
#include "opencv2/opencv.hpp"
|
||||||
|
|
||||||
|
namespace alpr
|
||||||
enum NiblackVersion
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
enum NiblackVersion
|
||||||
|
{
|
||||||
NIBLACK=0,
|
NIBLACK=0,
|
||||||
SAUVOLA,
|
SAUVOLA,
|
||||||
WOLFJOLION,
|
WOLFJOLION,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define BINARIZEWOLF_VERSION "2.3 (February 26th, 2013)"
|
#define BINARIZEWOLF_VERSION "2.3 (February 26th, 2013)"
|
||||||
#define BINARIZEWOLF_DEFAULTDR 128
|
#define BINARIZEWOLF_DEFAULTDR 128
|
||||||
|
|
||||||
#define uget(x,y) at<unsigned char>(y,x)
|
#define uget(x,y) at<unsigned char>(y,x)
|
||||||
#define uset(x,y,v) at<unsigned char>(y,x)=v;
|
#define uset(x,y,v) at<unsigned char>(y,x)=v;
|
||||||
#define fget(x,y) at<float>(y,x)
|
#define fget(x,y) at<float>(y,x)
|
||||||
#define fset(x,y,v) at<float>(y,x)=v;
|
#define fset(x,y,v) at<float>(y,x)=v;
|
||||||
|
|
||||||
void NiblackSauvolaWolfJolion (cv::Mat im, cv::Mat output, NiblackVersion version,
|
void NiblackSauvolaWolfJolion (cv::Mat im, cv::Mat output, NiblackVersion version,
|
||||||
int winx, int winy, float k);
|
int winx, int winy, float k);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // OPENALPR_BINARIZEWOLF_H
|
#endif // OPENALPR_BINARIZEWOLF_H
|
||||||
|
@@ -22,8 +22,12 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config)
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config)
|
||||||
|
{
|
||||||
timespec startTime;
|
timespec startTime;
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
|
|
||||||
@@ -52,14 +56,14 @@ ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config)
|
|||||||
getTime(&endTime);
|
getTime(&endTime);
|
||||||
cout << " -- ColorFilter Time: " << diffclock(startTime, endTime) << "ms." << endl;
|
cout << " -- ColorFilter Time: " << diffclock(startTime, endTime) << "ms." << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorFilter::~ColorFilter()
|
ColorFilter::~ColorFilter()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ColorFilter::imageIsGrayscale(Mat image)
|
bool ColorFilter::imageIsGrayscale(Mat image)
|
||||||
{
|
{
|
||||||
// Check whether the original image is grayscale. If it is, we shouldn't attempt any color filter
|
// Check whether the original image is grayscale. If it is, we shouldn't attempt any color filter
|
||||||
for (int row = 0; row < image.rows; row++)
|
for (int row = 0; row < image.rows; row++)
|
||||||
{
|
{
|
||||||
@@ -82,21 +86,21 @@ bool ColorFilter::imageIsGrayscale(Mat image)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ColorFilter::preprocessImage()
|
void ColorFilter::preprocessImage()
|
||||||
{
|
{
|
||||||
// Equalize the brightness on the HSV channel "V"
|
// Equalize the brightness on the HSV channel "V"
|
||||||
vector<Mat> channels;
|
vector<Mat> channels;
|
||||||
split(this->hsv,channels);
|
split(this->hsv,channels);
|
||||||
Mat img_equalized = equalizeBrightness(channels[2]);
|
Mat img_equalized = equalizeBrightness(channels[2]);
|
||||||
merge(channels,this->hsv);
|
merge(channels,this->hsv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the hue/sat/val for areas that we believe are license plate characters
|
// Gets the hue/sat/val for areas that we believe are license plate characters
|
||||||
// Then uses that to filter the whole image and provide a mask.
|
// Then uses that to filter the whole image and provide a mask.
|
||||||
void ColorFilter::findCharColors()
|
void ColorFilter::findCharColors()
|
||||||
{
|
{
|
||||||
int MINIMUM_SATURATION = 45;
|
int MINIMUM_SATURATION = 45;
|
||||||
|
|
||||||
if (this->debug)
|
if (this->debug)
|
||||||
@@ -362,12 +366,12 @@ void ColorFilter::findCharColors()
|
|||||||
Mat dashboard = drawImageDashboard(debugImagesSet, imgDebugHueOnly.type(), 3);
|
Mat dashboard = drawImageDashboard(debugImagesSet, imgDebugHueOnly.type(), 3);
|
||||||
displayImage(config, "Color Filter Images", dashboard);
|
displayImage(config, "Color Filter Images", dashboard);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Goes through an array of values, picks the winner based on the highest percentage of other values that are within the maxValDifference
|
// Goes through an array of values, picks the winner based on the highest percentage of other values that are within the maxValDifference
|
||||||
// Return -1 if it fails.
|
// Return -1 if it fails.
|
||||||
int ColorFilter::getMajorityOpinion(vector<float> values, float minPercentAgreement, float maxValDifference)
|
int ColorFilter::getMajorityOpinion(vector<float> values, float minPercentAgreement, float maxValDifference)
|
||||||
{
|
{
|
||||||
float bestPercentAgreement = 0;
|
float bestPercentAgreement = 0;
|
||||||
float lowestOverallDiff = 1000000000;
|
float lowestOverallDiff = 1000000000;
|
||||||
int bestPercentAgreementIndex = -1;
|
int bestPercentAgreementIndex = -1;
|
||||||
@@ -395,4 +399,6 @@ int ColorFilter::getMajorityOpinion(vector<float> values, float minPercentAgreem
|
|||||||
}
|
}
|
||||||
|
|
||||||
return bestPercentAgreementIndex;
|
return bestPercentAgreementIndex;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -27,10 +27,12 @@
|
|||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
namespace alpr
|
||||||
class ColorFilter
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class ColorFilter
|
||||||
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ColorFilter(cv::Mat image, cv::Mat characterMask, Config* config);
|
ColorFilter(cv::Mat image, cv::Mat characterMask, Config* config);
|
||||||
virtual ~ColorFilter();
|
virtual ~ColorFilter();
|
||||||
@@ -52,6 +54,7 @@ class ColorFilter
|
|||||||
|
|
||||||
bool imageIsGrayscale(cv::Mat image);
|
bool imageIsGrayscale(cv::Mat image);
|
||||||
int getMajorityOpinion(std::vector<float> values, float minPercentAgreement, float maxValDifference);
|
int getMajorityOpinion(std::vector<float> values, float minPercentAgreement, float maxValDifference);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
#endif // OPENALPR_COLORFILTER_H
|
#endif // OPENALPR_COLORFILTER_H
|
||||||
|
@@ -21,9 +21,12 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
Config::Config(const std::string country, const std::string config_file, const std::string runtime_dir)
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
Config::Config(const std::string country, const std::string config_file, const std::string runtime_dir)
|
||||||
|
{
|
||||||
|
|
||||||
string debug_message = "";
|
string debug_message = "";
|
||||||
|
|
||||||
this->loaded = false;
|
this->loaded = false;
|
||||||
@@ -116,14 +119,14 @@ Config::Config(const std::string country, const std::string config_file, const s
|
|||||||
}
|
}
|
||||||
|
|
||||||
this->loaded = true;
|
this->loaded = true;
|
||||||
}
|
}
|
||||||
Config::~Config()
|
Config::~Config()
|
||||||
{
|
{
|
||||||
delete ini;
|
delete ini;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::loadValues(string country)
|
void Config::loadValues(string country)
|
||||||
{
|
{
|
||||||
|
|
||||||
runtimeBaseDir = getString("common", "runtime_dir", "/usr/share/openalpr/runtime_data");
|
runtimeBaseDir = getString("common", "runtime_dir", "/usr/share/openalpr/runtime_data");
|
||||||
|
|
||||||
@@ -196,10 +199,10 @@ void Config::loadValues(string country)
|
|||||||
debugShowImages = getBoolean("debug", "show_images", false);
|
debugShowImages = getBoolean("debug", "show_images", false);
|
||||||
debugPauseOnFrame = getBoolean("debug", "pause_on_frame", false);
|
debugPauseOnFrame = getBoolean("debug", "pause_on_frame", false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::debugOff()
|
void Config::debugOff()
|
||||||
{
|
{
|
||||||
debugGeneral = false;
|
debugGeneral = false;
|
||||||
debugTiming = false;
|
debugTiming = false;
|
||||||
debugStateId = false;
|
debugStateId = false;
|
||||||
@@ -211,31 +214,31 @@ void Config::debugOff()
|
|||||||
debugOcr = false;
|
debugOcr = false;
|
||||||
debugPostProcess = false;
|
debugPostProcess = false;
|
||||||
debugPauseOnFrame = false;
|
debugPauseOnFrame = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string Config::getCascadeRuntimeDir()
|
string Config::getCascadeRuntimeDir()
|
||||||
{
|
{
|
||||||
return this->runtimeBaseDir + CASCADE_DIR;
|
return this->runtimeBaseDir + CASCADE_DIR;
|
||||||
}
|
}
|
||||||
string Config::getKeypointsRuntimeDir()
|
string Config::getKeypointsRuntimeDir()
|
||||||
{
|
{
|
||||||
return this->runtimeBaseDir + KEYPOINTS_DIR;
|
return this->runtimeBaseDir + KEYPOINTS_DIR;
|
||||||
}
|
}
|
||||||
string Config::getPostProcessRuntimeDir()
|
string Config::getPostProcessRuntimeDir()
|
||||||
{
|
{
|
||||||
return this->runtimeBaseDir + POSTPROCESS_DIR;
|
return this->runtimeBaseDir + POSTPROCESS_DIR;
|
||||||
}
|
}
|
||||||
string Config::getTessdataPrefix()
|
string Config::getTessdataPrefix()
|
||||||
{
|
{
|
||||||
return this->runtimeBaseDir + "/ocr/";
|
return this->runtimeBaseDir + "/ocr/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
float Config::getFloat(string section, string key, float defaultValue)
|
float Config::getFloat(string section, string key, float defaultValue)
|
||||||
{
|
{
|
||||||
const char * pszValue = ini->GetValue(section.c_str(), key.c_str(), NULL /*default*/);
|
const char * pszValue = ini->GetValue(section.c_str(), key.c_str(), NULL /*default*/);
|
||||||
if (pszValue == NULL)
|
if (pszValue == NULL)
|
||||||
{
|
{
|
||||||
@@ -245,9 +248,9 @@ float Config::getFloat(string section, string key, float defaultValue)
|
|||||||
|
|
||||||
float val = atof(pszValue);
|
float val = atof(pszValue);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
int Config::getInt(string section, string key, int defaultValue)
|
int Config::getInt(string section, string key, int defaultValue)
|
||||||
{
|
{
|
||||||
const char * pszValue = ini->GetValue(section.c_str(), key.c_str(), NULL /*default*/);
|
const char * pszValue = ini->GetValue(section.c_str(), key.c_str(), NULL /*default*/);
|
||||||
if (pszValue == NULL)
|
if (pszValue == NULL)
|
||||||
{
|
{
|
||||||
@@ -257,9 +260,9 @@ int Config::getInt(string section, string key, int defaultValue)
|
|||||||
|
|
||||||
int val = atoi(pszValue);
|
int val = atoi(pszValue);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
bool Config::getBoolean(string section, string key, bool defaultValue)
|
bool Config::getBoolean(string section, string key, bool defaultValue)
|
||||||
{
|
{
|
||||||
const char * pszValue = ini->GetValue(section.c_str(), key.c_str(), NULL /*default*/);
|
const char * pszValue = ini->GetValue(section.c_str(), key.c_str(), NULL /*default*/);
|
||||||
if (pszValue == NULL)
|
if (pszValue == NULL)
|
||||||
{
|
{
|
||||||
@@ -269,9 +272,9 @@ bool Config::getBoolean(string section, string key, bool defaultValue)
|
|||||||
|
|
||||||
int val = atoi(pszValue);
|
int val = atoi(pszValue);
|
||||||
return val != 0;
|
return val != 0;
|
||||||
}
|
}
|
||||||
string Config::getString(string section, string key, string defaultValue)
|
string Config::getString(string section, string key, string defaultValue)
|
||||||
{
|
{
|
||||||
const char * pszValue = ini->GetValue(section.c_str(), key.c_str(), NULL /*default*/);
|
const char * pszValue = ini->GetValue(section.c_str(), key.c_str(), NULL /*default*/);
|
||||||
if (pszValue == NULL)
|
if (pszValue == NULL)
|
||||||
{
|
{
|
||||||
@@ -281,4 +284,5 @@ string Config::getString(string section, string key, string defaultValue)
|
|||||||
|
|
||||||
string val = string(pszValue);
|
string val = string(pszValue);
|
||||||
return val;
|
return val;
|
||||||
|
}
|
||||||
}
|
}
|
@@ -33,10 +33,12 @@
|
|||||||
#include <stdlib.h> /* getenv */
|
#include <stdlib.h> /* getenv */
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
namespace alpr
|
||||||
class Config
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class Config
|
||||||
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Config(const std::string country, const std::string config_file = "", const std::string runtime_dir = "");
|
Config(const std::string country, const std::string config_file = "", const std::string runtime_dir = "");
|
||||||
virtual ~Config();
|
virtual ~Config();
|
||||||
@@ -118,7 +120,7 @@ class Config
|
|||||||
std::string getPostProcessRuntimeDir();
|
std::string getPostProcessRuntimeDir();
|
||||||
std::string getTessdataPrefix();
|
std::string getTessdataPrefix();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CSimpleIniA* ini;
|
CSimpleIniA* ini;
|
||||||
|
|
||||||
std::string runtimeBaseDir;
|
std::string runtimeBaseDir;
|
||||||
@@ -129,7 +131,7 @@ private:
|
|||||||
float getFloat(std::string section, std::string key, float defaultValue);
|
float getFloat(std::string section, std::string key, float defaultValue);
|
||||||
std::string getString(std::string section, std::string key, std::string defaultValue);
|
std::string getString(std::string section, std::string key, std::string defaultValue);
|
||||||
bool getBoolean(std::string section, std::string key, bool defaultValue);
|
bool getBoolean(std::string section, std::string key, bool defaultValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
#endif // OPENALPR_CONFIG_H
|
#endif // OPENALPR_CONFIG_H
|
@@ -22,42 +22,45 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
Detector::Detector(Config* config)
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
Detector::Detector(Config* config)
|
||||||
|
{
|
||||||
this->config = config;
|
this->config = config;
|
||||||
this->scale_factor = 1.0f;
|
this->scale_factor = 1.0f;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Detector::~Detector()
|
Detector::~Detector()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Detector::isLoaded()
|
bool Detector::isLoaded()
|
||||||
{
|
{
|
||||||
return this->loaded;
|
return this->loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<PlateRegion> Detector::detect(cv::Mat frame)
|
vector<PlateRegion> Detector::detect(cv::Mat frame)
|
||||||
{
|
{
|
||||||
std::vector<cv::Rect> regionsOfInterest;
|
std::vector<cv::Rect> regionsOfInterest;
|
||||||
regionsOfInterest.push_back(Rect(0, 0, frame.cols, frame.rows));
|
regionsOfInterest.push_back(Rect(0, 0, frame.cols, frame.rows));
|
||||||
return this->detect(frame, regionsOfInterest);
|
return this->detect(frame, regionsOfInterest);
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<PlateRegion> Detector::detect(Mat frame, std::vector<cv::Rect> regionsOfInterest)
|
vector<PlateRegion> Detector::detect(Mat frame, std::vector<cv::Rect> regionsOfInterest)
|
||||||
{
|
{
|
||||||
// Must be implemented by subclass
|
// Must be implemented by subclass
|
||||||
std::vector<PlateRegion> rois;
|
std::vector<PlateRegion> rois;
|
||||||
return rois;
|
return rois;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool rectHasLargerArea(cv::Rect a, cv::Rect b) { return a.area() < b.area(); };
|
bool rectHasLargerArea(cv::Rect a, cv::Rect b) { return a.area() < b.area(); };
|
||||||
|
|
||||||
vector<PlateRegion> Detector::aggregateRegions(vector<Rect> regions)
|
vector<PlateRegion> Detector::aggregateRegions(vector<Rect> regions)
|
||||||
{
|
{
|
||||||
// Combines overlapping regions into a parent->child order.
|
// Combines overlapping regions into a parent->child order.
|
||||||
// The largest regions will be parents, and they will have children if they are within them.
|
// The largest regions will be parents, and they will have children if they are within them.
|
||||||
// This way, when processing regions later, we can process the parents first, and only delve into the children
|
// This way, when processing regions later, we can process the parents first, and only delve into the children
|
||||||
@@ -106,4 +109,6 @@ vector<PlateRegion> Detector::aggregateRegions(vector<Rect> regions)
|
|||||||
|
|
||||||
|
|
||||||
return topLevelRegions;
|
return topLevelRegions;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -28,15 +28,18 @@
|
|||||||
#include "support/timing.h"
|
#include "support/timing.h"
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
|
|
||||||
struct PlateRegion
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct PlateRegion
|
||||||
|
{
|
||||||
cv::Rect rect;
|
cv::Rect rect;
|
||||||
std::vector<PlateRegion> children;
|
std::vector<PlateRegion> children;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class Detector
|
class Detector
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Detector(Config* config);
|
Detector(Config* config);
|
||||||
@@ -56,6 +59,8 @@ class Detector
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // OPENALPR_REGIONDETECTOR_H
|
#endif // OPENALPR_REGIONDETECTOR_H
|
||||||
|
@@ -22,7 +22,10 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
DetectorCPU::DetectorCPU(Config* config) : Detector(config) {
|
namespace alpr
|
||||||
|
{
|
||||||
|
|
||||||
|
DetectorCPU::DetectorCPU(Config* config) : Detector(config) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -35,14 +38,14 @@ DetectorCPU::DetectorCPU(Config* config) : Detector(config) {
|
|||||||
this->loaded = false;
|
this->loaded = false;
|
||||||
printf("--(!)Error loading classifier\n");
|
printf("--(!)Error loading classifier\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DetectorCPU::~DetectorCPU() {
|
DetectorCPU::~DetectorCPU() {
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<PlateRegion> DetectorCPU::detect(Mat frame, std::vector<cv::Rect> regionsOfInterest)
|
vector<PlateRegion> DetectorCPU::detect(Mat frame, std::vector<cv::Rect> regionsOfInterest)
|
||||||
{
|
{
|
||||||
|
|
||||||
Mat frame_gray;
|
Mat frame_gray;
|
||||||
cvtColor( frame, frame_gray, CV_BGR2GRAY );
|
cvtColor( frame, frame_gray, CV_BGR2GRAY );
|
||||||
@@ -50,10 +53,10 @@ vector<PlateRegion> DetectorCPU::detect(Mat frame, std::vector<cv::Rect> regions
|
|||||||
vector<PlateRegion> detectedRegions = doCascade(frame_gray, regionsOfInterest);
|
vector<PlateRegion> detectedRegions = doCascade(frame_gray, regionsOfInterest);
|
||||||
|
|
||||||
return detectedRegions;
|
return detectedRegions;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<PlateRegion> DetectorCPU::doCascade(Mat frame, std::vector<cv::Rect> regionsOfInterest)
|
vector<PlateRegion> DetectorCPU::doCascade(Mat frame, std::vector<cv::Rect> regionsOfInterest)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
if (frame.cols > config->maxDetectionInputWidth)
|
if (frame.cols > config->maxDetectionInputWidth)
|
||||||
@@ -115,4 +118,6 @@ vector<PlateRegion> DetectorCPU::doCascade(Mat frame, std::vector<cv::Rect> regi
|
|||||||
|
|
||||||
return orderedRegions;
|
return orderedRegions;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -31,19 +31,24 @@
|
|||||||
|
|
||||||
#include "detector.h"
|
#include "detector.h"
|
||||||
|
|
||||||
class DetectorCPU : public Detector {
|
namespace alpr
|
||||||
public:
|
{
|
||||||
|
|
||||||
|
class DetectorCPU : public Detector {
|
||||||
|
public:
|
||||||
DetectorCPU(Config* config);
|
DetectorCPU(Config* config);
|
||||||
virtual ~DetectorCPU();
|
virtual ~DetectorCPU();
|
||||||
|
|
||||||
std::vector<PlateRegion> detect(cv::Mat frame, std::vector<cv::Rect> regionsOfInterest);
|
std::vector<PlateRegion> detect(cv::Mat frame, std::vector<cv::Rect> regionsOfInterest);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
cv::CascadeClassifier plate_cascade;
|
cv::CascadeClassifier plate_cascade;
|
||||||
|
|
||||||
std::vector<PlateRegion> doCascade(cv::Mat frame, std::vector<cv::Rect> regionsOfInterest);
|
std::vector<PlateRegion> doCascade(cv::Mat frame, std::vector<cv::Rect> regionsOfInterest);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* OPENALPR_DETECTORCPU_H */
|
#endif /* OPENALPR_DETECTORCPU_H */
|
||||||
|
|
||||||
|
@@ -1,7 +1,11 @@
|
|||||||
#include "detectorfactory.h"
|
#include "detectorfactory.h"
|
||||||
|
|
||||||
Detector* createDetector(Config* config)
|
namespace alpr
|
||||||
{
|
{
|
||||||
return new DetectorCPU(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Detector* createDetector(Config* config)
|
||||||
|
{
|
||||||
|
return new DetectorCPU(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -23,7 +23,11 @@
|
|||||||
#include "detectorcpu.h"
|
#include "detectorcpu.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
Detector* createDetector(Config* config);
|
namespace alpr
|
||||||
|
{
|
||||||
|
|
||||||
|
Detector* createDetector(Config* config);
|
||||||
|
|
||||||
|
}
|
||||||
#endif /* OPENALPR_DETECTORFACTORY_H */
|
#endif /* OPENALPR_DETECTORFACTORY_H */
|
||||||
|
|
||||||
|
@@ -23,20 +23,23 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
EdgeFinder::EdgeFinder(PipelineData* pipeline_data) {
|
namespace alpr
|
||||||
|
{
|
||||||
|
|
||||||
|
EdgeFinder::EdgeFinder(PipelineData* pipeline_data) {
|
||||||
|
|
||||||
this->pipeline_data = pipeline_data;
|
this->pipeline_data = pipeline_data;
|
||||||
|
|
||||||
// First re-crop the area from the original picture knowing the text position
|
// First re-crop the area from the original picture knowing the text position
|
||||||
this->confidence = 0;
|
this->confidence = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
EdgeFinder::~EdgeFinder() {
|
EdgeFinder::~EdgeFinder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<cv::Point2f> EdgeFinder::findEdgeCorners() {
|
std::vector<cv::Point2f> EdgeFinder::findEdgeCorners() {
|
||||||
|
|
||||||
TextLineCollection tlc(pipeline_data->textLines);
|
TextLineCollection tlc(pipeline_data->textLines);
|
||||||
|
|
||||||
@@ -85,10 +88,10 @@ std::vector<cv::Point2f> EdgeFinder::findEdgeCorners() {
|
|||||||
corners.push_back(Point(expandX + w, expandY + h));
|
corners.push_back(Point(expandX + w, expandY + h));
|
||||||
corners.push_back(Point(-1 * expandX, expandY + h));
|
corners.push_back(Point(-1 * expandX, expandY + h));
|
||||||
|
|
||||||
// for (int i = 0; i < 4; i++)
|
// for (int i = 0; i < 4; i++)
|
||||||
// {
|
// {
|
||||||
// std::cout << "CORNER: " << corners[i].x << " - " << corners[i].y << std::endl;
|
// std::cout << "CORNER: " << corners[i].x << " - " << corners[i].y << std::endl;
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-crop an image (from the original image) using the new coordinates
|
// Re-crop an image (from the original image) using the new coordinates
|
||||||
@@ -139,5 +142,6 @@ std::vector<cv::Point2f> EdgeFinder::findEdgeCorners() {
|
|||||||
|
|
||||||
return cornersInOriginalImg;
|
return cornersInOriginalImg;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
@@ -26,8 +26,11 @@
|
|||||||
#include "platelines.h"
|
#include "platelines.h"
|
||||||
#include "platecorners.h"
|
#include "platecorners.h"
|
||||||
|
|
||||||
class EdgeFinder {
|
namespace alpr
|
||||||
public:
|
{
|
||||||
|
|
||||||
|
class EdgeFinder {
|
||||||
|
public:
|
||||||
EdgeFinder(PipelineData* pipeline_data);
|
EdgeFinder(PipelineData* pipeline_data);
|
||||||
virtual ~EdgeFinder();
|
virtual ~EdgeFinder();
|
||||||
|
|
||||||
@@ -35,10 +38,11 @@ public:
|
|||||||
|
|
||||||
float confidence;
|
float confidence;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PipelineData* pipeline_data;
|
PipelineData* pipeline_data;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
#endif /* OPENALPR_EDGEFINDER_H */
|
#endif /* OPENALPR_EDGEFINDER_H */
|
||||||
|
|
||||||
|
@@ -22,9 +22,12 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, PipelineData* pipelineData, vector<TextLine> textLines) :
|
namespace alpr
|
||||||
tlc(textLines)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, PipelineData* pipelineData, vector<TextLine> textLines) :
|
||||||
|
tlc(textLines)
|
||||||
|
{
|
||||||
this->pipelineData = pipelineData;
|
this->pipelineData = pipelineData;
|
||||||
|
|
||||||
if (pipelineData->config->debugPlateCorners)
|
if (pipelineData->config->debugPlateCorners)
|
||||||
@@ -38,14 +41,14 @@ PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, PipelineData*
|
|||||||
this->bestVerticalScore = 9999999999999;
|
this->bestVerticalScore = 9999999999999;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PlateCorners::~PlateCorners()
|
PlateCorners::~PlateCorners()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<Point> PlateCorners::findPlateCorners()
|
vector<Point> PlateCorners::findPlateCorners()
|
||||||
{
|
{
|
||||||
if (pipelineData->config->debugPlateCorners)
|
if (pipelineData->config->debugPlateCorners)
|
||||||
cout << "PlateCorners::findPlateCorners" << endl;
|
cout << "PlateCorners::findPlateCorners" << endl;
|
||||||
|
|
||||||
@@ -120,10 +123,10 @@ vector<Point> PlateCorners::findPlateCorners()
|
|||||||
}
|
}
|
||||||
|
|
||||||
return corners;
|
return corners;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlateCorners::scoreVerticals(int v1, int v2)
|
void PlateCorners::scoreVerticals(int v1, int v2)
|
||||||
{
|
{
|
||||||
ScoreKeeper scoreKeeper;
|
ScoreKeeper scoreKeeper;
|
||||||
|
|
||||||
LineSegment left;
|
LineSegment left;
|
||||||
@@ -216,11 +219,11 @@ void PlateCorners::scoreVerticals(int v1, int v2)
|
|||||||
bestLeft = LineSegment(left.p1.x, left.p1.y, left.p2.x, left.p2.y);
|
bestLeft = LineSegment(left.p1.x, left.p1.y, left.p2.x, left.p2.y);
|
||||||
bestRight = LineSegment(right.p1.x, right.p1.y, right.p2.x, right.p2.y);
|
bestRight = LineSegment(right.p1.x, right.p1.y, right.p2.x, right.p2.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Score a collection of lines as a possible license plate region.
|
// Score a collection of lines as a possible license plate region.
|
||||||
// If any segments are missing, extrapolate the missing pieces
|
// If any segments are missing, extrapolate the missing pieces
|
||||||
void PlateCorners::scoreHorizontals(int h1, int h2)
|
void PlateCorners::scoreHorizontals(int h1, int h2)
|
||||||
{
|
{
|
||||||
|
|
||||||
ScoreKeeper scoreKeeper;
|
ScoreKeeper scoreKeeper;
|
||||||
|
|
||||||
@@ -235,7 +238,7 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
|
|||||||
|
|
||||||
if (h1 == NO_LINE && h2 == NO_LINE)
|
if (h1 == NO_LINE && h2 == NO_LINE)
|
||||||
{
|
{
|
||||||
// return;
|
// return;
|
||||||
|
|
||||||
|
|
||||||
top = tlc.centerHorizontalLine.getParallelLine(idealPixelHeight / 2);
|
top = tlc.centerHorizontalLine.getParallelLine(idealPixelHeight / 2);
|
||||||
@@ -347,8 +350,8 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
|
|||||||
bestTop = LineSegment(top.p1.x, top.p1.y, top.p2.x, top.p2.y);
|
bestTop = LineSegment(top.p1.x, top.p1.y, top.p2.x, top.p2.y);
|
||||||
bestBottom = LineSegment(bottom.p1.x, bottom.p1.y, bottom.p2.x, bottom.p2.y);
|
bestBottom = LineSegment(bottom.p1.x, bottom.p1.y, bottom.p2.x, bottom.p2.y);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -40,11 +40,12 @@
|
|||||||
|
|
||||||
#define SCORING_LINE_CONFIDENCE_WEIGHT 18.0
|
#define SCORING_LINE_CONFIDENCE_WEIGHT 18.0
|
||||||
|
|
||||||
|
namespace alpr
|
||||||
|
|
||||||
class PlateCorners
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class PlateCorners
|
||||||
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PlateCorners(cv::Mat inputImage, PlateLines* plateLines, PipelineData* pipelineData, std::vector<TextLine> textLines) ;
|
PlateCorners(cv::Mat inputImage, PlateLines* plateLines, PipelineData* pipelineData, std::vector<TextLine> textLines) ;
|
||||||
|
|
||||||
@@ -74,6 +75,7 @@ class PlateCorners
|
|||||||
void scoreHorizontals( int h1, int h2 );
|
void scoreHorizontals( int h1, int h2 );
|
||||||
void scoreVerticals( int v1, int v2 );
|
void scoreVerticals( int v1, int v2 );
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
#endif // OPENALPR_PLATELINES_H
|
#endif // OPENALPR_PLATELINES_H
|
||||||
|
@@ -24,23 +24,25 @@ using namespace std;
|
|||||||
|
|
||||||
const float MIN_CONFIDENCE = 0.3;
|
const float MIN_CONFIDENCE = 0.3;
|
||||||
|
|
||||||
|
namespace alpr
|
||||||
PlateLines::PlateLines(PipelineData* pipelineData)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
PlateLines::PlateLines(PipelineData* pipelineData)
|
||||||
|
{
|
||||||
this->pipelineData = pipelineData;
|
this->pipelineData = pipelineData;
|
||||||
|
|
||||||
this->debug = pipelineData->config->debugPlateLines;
|
this->debug = pipelineData->config->debugPlateLines;
|
||||||
|
|
||||||
if (debug)
|
if (debug)
|
||||||
cout << "PlateLines constructor" << endl;
|
cout << "PlateLines constructor" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlateLines::~PlateLines()
|
PlateLines::~PlateLines()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlateLines::processImage(Mat inputImage, vector<TextLine> textLines, float sensitivity)
|
void PlateLines::processImage(Mat inputImage, vector<TextLine> textLines, float sensitivity)
|
||||||
{
|
{
|
||||||
if (this->debug)
|
if (this->debug)
|
||||||
cout << "PlateLines findLines" << endl;
|
cout << "PlateLines findLines" << endl;
|
||||||
|
|
||||||
@@ -131,13 +133,13 @@ void PlateLines::processImage(Mat inputImage, vector<TextLine> textLines, float
|
|||||||
cout << "Plate Lines Time: " << diffclock(startTime, endTime) << "ms." << endl;
|
cout << "Plate Lines Time: " << diffclock(startTime, endTime) << "ms." << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
vector<PlateLine> PlateLines::getLines(Mat edges, float sensitivityMultiplier, bool vertical)
|
vector<PlateLine> PlateLines::getLines(Mat edges, float sensitivityMultiplier, bool vertical)
|
||||||
{
|
{
|
||||||
if (this->debug)
|
if (this->debug)
|
||||||
cout << "PlateLines::getLines" << endl;
|
cout << "PlateLines::getLines" << endl;
|
||||||
|
|
||||||
@@ -219,10 +221,10 @@ vector<PlateLine> PlateLines::getLines(Mat edges, float sensitivityMultiplier, b
|
|||||||
}
|
}
|
||||||
|
|
||||||
return filteredLines;
|
return filteredLines;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat PlateLines::customGrayscaleConversion(Mat src)
|
Mat PlateLines::customGrayscaleConversion(Mat src)
|
||||||
{
|
{
|
||||||
Mat img_hsv;
|
Mat img_hsv;
|
||||||
cvtColor(src,img_hsv,CV_BGR2HSV);
|
cvtColor(src,img_hsv,CV_BGR2HSV);
|
||||||
|
|
||||||
@@ -249,4 +251,6 @@ Mat PlateLines::customGrayscaleConversion(Mat src)
|
|||||||
|
|
||||||
//displayImage(config, "Hue", hue);
|
//displayImage(config, "Hue", hue);
|
||||||
return grayscale;
|
return grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -27,14 +27,17 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "pipeline_data.h"
|
#include "pipeline_data.h"
|
||||||
|
|
||||||
struct PlateLine
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct PlateLine
|
||||||
|
{
|
||||||
LineSegment line;
|
LineSegment line;
|
||||||
float confidence;
|
float confidence;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PlateLines
|
class PlateLines
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PlateLines(PipelineData* pipelineData);
|
PlateLines(PipelineData* pipelineData);
|
||||||
@@ -55,6 +58,8 @@ class PlateLines
|
|||||||
cv::Mat customGrayscaleConversion(cv::Mat src);
|
cv::Mat customGrayscaleConversion(cv::Mat src);
|
||||||
void findLines(cv::Mat inputImage);
|
void findLines(cv::Mat inputImage);
|
||||||
std::vector<PlateLine> getLines(cv::Mat edges, float sensitivityMultiplier, bool vertical);
|
std::vector<PlateLine> getLines(cv::Mat edges, float sensitivityMultiplier, bool vertical);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // OPENALPR_PLATELINES_H
|
#endif // OPENALPR_PLATELINES_H
|
||||||
|
@@ -21,23 +21,26 @@
|
|||||||
|
|
||||||
#include "scorekeeper.h"
|
#include "scorekeeper.h"
|
||||||
|
|
||||||
ScoreKeeper::ScoreKeeper() {
|
namespace alpr
|
||||||
}
|
{
|
||||||
|
|
||||||
|
ScoreKeeper::ScoreKeeper() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ScoreKeeper::~ScoreKeeper() {
|
ScoreKeeper::~ScoreKeeper() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScoreKeeper::setScore(std::string weight_id, float score, float weight) {
|
void ScoreKeeper::setScore(std::string weight_id, float score, float weight) {
|
||||||
|
|
||||||
// Assume that we never set this value twice
|
// Assume that we never set this value twice
|
||||||
weight_ids.push_back(weight_id);
|
weight_ids.push_back(weight_id);
|
||||||
scores.push_back(score);
|
scores.push_back(score);
|
||||||
weights.push_back(weight);
|
weights.push_back(weight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
float ScoreKeeper::getTotal() {
|
float ScoreKeeper::getTotal() {
|
||||||
|
|
||||||
float score = 0;
|
float score = 0;
|
||||||
|
|
||||||
@@ -47,9 +50,9 @@ float ScoreKeeper::getTotal() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScoreKeeper::printDebugScores() {
|
void ScoreKeeper::printDebugScores() {
|
||||||
|
|
||||||
int longest_weight_id = 0;
|
int longest_weight_id = 0;
|
||||||
for (unsigned int i = 0; i < weight_ids.size(); i++)
|
for (unsigned int i = 0; i < weight_ids.size(); i++)
|
||||||
@@ -72,4 +75,6 @@ void ScoreKeeper::printDebugScores() {
|
|||||||
|
|
||||||
|
|
||||||
std::cout << "Total: " << total << std::endl;
|
std::cout << "Total: " << total << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -24,8 +24,11 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
class ScoreKeeper {
|
namespace alpr
|
||||||
public:
|
{
|
||||||
|
|
||||||
|
class ScoreKeeper {
|
||||||
|
public:
|
||||||
ScoreKeeper();
|
ScoreKeeper();
|
||||||
virtual ~ScoreKeeper();
|
virtual ~ScoreKeeper();
|
||||||
|
|
||||||
@@ -35,14 +38,16 @@ public:
|
|||||||
|
|
||||||
void printDebugScores();
|
void printDebugScores();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::vector<std::string> weight_ids;
|
std::vector<std::string> weight_ids;
|
||||||
std::vector<float> weights;
|
std::vector<float> weights;
|
||||||
|
|
||||||
std::vector<float> scores;
|
std::vector<float> scores;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* OPENALPR_SCOREKEEPER_H */
|
#endif /* OPENALPR_SCOREKEEPER_H */
|
||||||
|
|
||||||
|
@@ -10,7 +10,10 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
TextLineCollection::TextLineCollection(std::vector<TextLine> textLines) {
|
namespace alpr
|
||||||
|
{
|
||||||
|
|
||||||
|
TextLineCollection::TextLineCollection(std::vector<TextLine> textLines) {
|
||||||
|
|
||||||
|
|
||||||
charHeight = 0;
|
charHeight = 0;
|
||||||
@@ -50,9 +53,9 @@ TextLineCollection::TextLineCollection(std::vector<TextLine> textLines) {
|
|||||||
// Center Vertical Line
|
// Center Vertical Line
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cv::Mat TextLineCollection::getDebugImage(cv::Size imageSize) {
|
cv::Mat TextLineCollection::getDebugImage(cv::Size imageSize) {
|
||||||
|
|
||||||
Mat debugImage = Mat::zeros(imageSize, CV_8U);
|
Mat debugImage = Mat::zeros(imageSize, CV_8U);
|
||||||
line(debugImage, this->centerHorizontalLine.p1, this->centerHorizontalLine.p2, Scalar(255,255,255), 2);
|
line(debugImage, this->centerHorizontalLine.p1, this->centerHorizontalLine.p2, Scalar(255,255,255), 2);
|
||||||
@@ -60,11 +63,11 @@ cv::Mat TextLineCollection::getDebugImage(cv::Size imageSize) {
|
|||||||
|
|
||||||
return debugImage;
|
return debugImage;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Returns 1 for above, 0 for within, and -1 for below
|
// Returns 1 for above, 0 for within, and -1 for below
|
||||||
int TextLineCollection::isAboveText(LineSegment line) {
|
int TextLineCollection::isAboveText(LineSegment line) {
|
||||||
// Test four points (left and right corner of top and bottom line)
|
// Test four points (left and right corner of top and bottom line)
|
||||||
|
|
||||||
Point topLeft = line.closestPointOnSegmentTo(topCharArea.p1);
|
Point topLeft = line.closestPointOnSegmentTo(topCharArea.p1);
|
||||||
@@ -86,10 +89,10 @@ int TextLineCollection::isAboveText(LineSegment line) {
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns 1 for left, 0 for within, and -1 for to the right
|
// Returns 1 for left, 0 for within, and -1 for to the right
|
||||||
int TextLineCollection::isLeftOfText(LineSegment line) {
|
int TextLineCollection::isLeftOfText(LineSegment line) {
|
||||||
|
|
||||||
LineSegment leftSide = LineSegment(bottomCharArea.p1, topCharArea.p1);
|
LineSegment leftSide = LineSegment(bottomCharArea.p1, topCharArea.p1);
|
||||||
|
|
||||||
@@ -113,9 +116,9 @@ int TextLineCollection::isLeftOfText(LineSegment line) {
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextLineCollection::findCenterHorizontal() {
|
void TextLineCollection::findCenterHorizontal() {
|
||||||
// To find the center horizontal line:
|
// To find the center horizontal line:
|
||||||
// Find the longer of the lines (if multiline)
|
// Find the longer of the lines (if multiline)
|
||||||
// Get the nearest point on the bottom-most line for the
|
// Get the nearest point on the bottom-most line for the
|
||||||
@@ -139,9 +142,9 @@ void TextLineCollection::findCenterHorizontal() {
|
|||||||
|
|
||||||
this->centerHorizontalLine = LineSegment(leftMidpoint, rightMidpoint);
|
this->centerHorizontalLine = LineSegment(leftMidpoint, rightMidpoint);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextLineCollection::findCenterVertical() {
|
void TextLineCollection::findCenterVertical() {
|
||||||
// To find the center vertical line:
|
// To find the center vertical line:
|
||||||
// Choose the longest line (if multiline)
|
// Choose the longest line (if multiline)
|
||||||
// Get the midpoint
|
// Get the midpoint
|
||||||
@@ -157,4 +160,6 @@ void TextLineCollection::findCenterVertical() {
|
|||||||
this->centerVerticalLine = LineSegment(p1, p2);
|
this->centerVerticalLine = LineSegment(p1, p2);
|
||||||
else
|
else
|
||||||
this->centerVerticalLine = LineSegment(p2, p1);
|
this->centerVerticalLine = LineSegment(p2, p1);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -13,10 +13,12 @@
|
|||||||
#include "opencv2/imgproc/imgproc.hpp"
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
#include "textdetection/textline.h"
|
#include "textdetection/textline.h"
|
||||||
|
|
||||||
|
namespace alpr
|
||||||
class TextLineCollection
|
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
|
class TextLineCollection
|
||||||
|
{
|
||||||
|
public:
|
||||||
TextLineCollection(std::vector<TextLine> textLines);
|
TextLineCollection(std::vector<TextLine> textLines);
|
||||||
|
|
||||||
int isLeftOfText(LineSegment line);
|
int isLeftOfText(LineSegment line);
|
||||||
@@ -33,7 +35,7 @@ public:
|
|||||||
|
|
||||||
cv::Mat getDebugImage(cv::Size imageSize);
|
cv::Mat getDebugImage(cv::Size imageSize);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
LineSegment topCharArea;
|
LineSegment topCharArea;
|
||||||
LineSegment bottomCharArea;
|
LineSegment bottomCharArea;
|
||||||
@@ -43,7 +45,9 @@ private:
|
|||||||
|
|
||||||
void findCenterHorizontal();
|
void findCenterHorizontal();
|
||||||
void findCenterVertical();
|
void findCenterVertical();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* OPENALPR_TEXTLINECOLLECTION_H */
|
#endif /* OPENALPR_TEXTLINECOLLECTION_H */
|
||||||
|
|
||||||
|
@@ -22,12 +22,15 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
//const int DEFAULT_QUERY_FEATURES = 305;
|
namespace alpr
|
||||||
//const int DEFAULT_TRAINING_FEATURES = 305;
|
|
||||||
const float MAX_DISTANCE_TO_MATCH = 100.0f;
|
|
||||||
|
|
||||||
FeatureMatcher::FeatureMatcher(Config* config)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
//const int DEFAULT_QUERY_FEATURES = 305;
|
||||||
|
//const int DEFAULT_TRAINING_FEATURES = 305;
|
||||||
|
const float MAX_DISTANCE_TO_MATCH = 100.0f;
|
||||||
|
|
||||||
|
FeatureMatcher::FeatureMatcher(Config* config)
|
||||||
|
{
|
||||||
this->config = config;
|
this->config = config;
|
||||||
|
|
||||||
//this->descriptorMatcher = DescriptorMatcher::create( "BruteForce-HammingLUT" );
|
//this->descriptorMatcher = DescriptorMatcher::create( "BruteForce-HammingLUT" );
|
||||||
@@ -37,10 +40,10 @@ FeatureMatcher::FeatureMatcher(Config* config)
|
|||||||
|
|
||||||
this->detector = new FastFeatureDetector(10, true);
|
this->detector = new FastFeatureDetector(10, true);
|
||||||
this->extractor = new BRISK(10, 1, 0.9);
|
this->extractor = new BRISK(10, 1, 0.9);
|
||||||
}
|
}
|
||||||
|
|
||||||
FeatureMatcher::~FeatureMatcher()
|
FeatureMatcher::~FeatureMatcher()
|
||||||
{
|
{
|
||||||
for (uint i = 0; i < trainingImgKeypoints.size(); i++)
|
for (uint i = 0; i < trainingImgKeypoints.size(); i++)
|
||||||
trainingImgKeypoints[i].clear();
|
trainingImgKeypoints[i].clear();
|
||||||
trainingImgKeypoints.clear();
|
trainingImgKeypoints.clear();
|
||||||
@@ -48,26 +51,26 @@ FeatureMatcher::~FeatureMatcher()
|
|||||||
descriptorMatcher.release();
|
descriptorMatcher.release();
|
||||||
detector.release();
|
detector.release();
|
||||||
extractor.release();
|
extractor.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FeatureMatcher::isLoaded()
|
bool FeatureMatcher::isLoaded()
|
||||||
{
|
{
|
||||||
if( detector.empty() || extractor.empty() || descriptorMatcher.empty() )
|
if( detector.empty() || extractor.empty() || descriptorMatcher.empty() )
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FeatureMatcher::numTrainingElements()
|
int FeatureMatcher::numTrainingElements()
|
||||||
{
|
{
|
||||||
return billMapping.size();
|
return billMapping.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FeatureMatcher::surfStyleMatching( const Mat& queryDescriptors, vector<KeyPoint> queryKeypoints,
|
void FeatureMatcher::surfStyleMatching( const Mat& queryDescriptors, vector<KeyPoint> queryKeypoints,
|
||||||
vector<DMatch>& matches12 )
|
vector<DMatch>& matches12 )
|
||||||
{
|
{
|
||||||
vector<vector<DMatch> > matchesKnn;
|
vector<vector<DMatch> > matchesKnn;
|
||||||
|
|
||||||
this->descriptorMatcher->radiusMatch(queryDescriptors, matchesKnn, MAX_DISTANCE_TO_MATCH);
|
this->descriptorMatcher->radiusMatch(queryDescriptors, matchesKnn, MAX_DISTANCE_TO_MATCH);
|
||||||
@@ -76,10 +79,10 @@ void FeatureMatcher::surfStyleMatching( const Mat& queryDescriptors, vector<KeyP
|
|||||||
_surfStyleMatching(queryDescriptors, matchesKnn, tempMatches);
|
_surfStyleMatching(queryDescriptors, matchesKnn, tempMatches);
|
||||||
|
|
||||||
crisscrossFiltering(queryKeypoints, tempMatches, matches12);
|
crisscrossFiltering(queryKeypoints, tempMatches, matches12);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FeatureMatcher::_surfStyleMatching(const Mat& queryDescriptors, vector<vector<DMatch> > matchesKnn, vector<DMatch>& matches12)
|
void FeatureMatcher::_surfStyleMatching(const Mat& queryDescriptors, vector<vector<DMatch> > matchesKnn, vector<DMatch>& matches12)
|
||||||
{
|
{
|
||||||
//objectMatches.clear();
|
//objectMatches.clear();
|
||||||
//objectMatches.resize(objectIds.size());
|
//objectMatches.resize(objectIds.size());
|
||||||
//cout << "starting matcher" << matchesKnn.size() << endl;
|
//cout << "starting matcher" << matchesKnn.size() << endl;
|
||||||
@@ -143,12 +146,12 @@ void FeatureMatcher::_surfStyleMatching(const Mat& queryDescriptors, vector<vect
|
|||||||
//matches12.push_back(match);
|
//matches12.push_back(match);
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compares the matches keypoints for parallel lines. Removes matches that are criss-crossing too much
|
// Compares the matches keypoints for parallel lines. Removes matches that are criss-crossing too much
|
||||||
// We assume that license plates won't be upside-down or backwards. So expect lines to be closely parallel
|
// We assume that license plates won't be upside-down or backwards. So expect lines to be closely parallel
|
||||||
void FeatureMatcher::crisscrossFiltering(const vector<KeyPoint> queryKeypoints, const vector<DMatch> inputMatches, vector<DMatch> &outputMatches)
|
void FeatureMatcher::crisscrossFiltering(const vector<KeyPoint> queryKeypoints, const vector<DMatch> inputMatches, vector<DMatch> &outputMatches)
|
||||||
{
|
{
|
||||||
Rect crissCrossAreaVertical(0, 0, config->stateIdImageWidthPx, config->stateIdimageHeightPx * 2);
|
Rect crissCrossAreaVertical(0, 0, config->stateIdImageWidthPx, config->stateIdimageHeightPx * 2);
|
||||||
Rect crissCrossAreaHorizontal(0, 0, config->stateIdImageWidthPx * 2, config->stateIdimageHeightPx);
|
Rect crissCrossAreaHorizontal(0, 0, config->stateIdImageWidthPx * 2, config->stateIdimageHeightPx);
|
||||||
|
|
||||||
@@ -226,11 +229,11 @@ void FeatureMatcher::crisscrossFiltering(const vector<KeyPoint> queryKeypoints,
|
|||||||
outputMatches.push_back(matchesForOnePlate[matchIdx[j]]);
|
outputMatches.push_back(matchesForOnePlate[matchIdx[j]]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if successful, false otherwise
|
// Returns true if successful, false otherwise
|
||||||
bool FeatureMatcher::loadRecognitionSet(string country)
|
bool FeatureMatcher::loadRecognitionSet(string country)
|
||||||
{
|
{
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
out << config->getKeypointsRuntimeDir() << "/" << country << "/";
|
out << config->getKeypointsRuntimeDir() << "/" << country << "/";
|
||||||
string country_dir = out.str();
|
string country_dir = out.str();
|
||||||
@@ -279,12 +282,12 @@ bool FeatureMatcher::loadRecognitionSet(string country)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RecognitionResult FeatureMatcher::recognize( const Mat& queryImg, bool drawOnImage, Mat* outputImage,
|
RecognitionResult FeatureMatcher::recognize( const Mat& queryImg, bool drawOnImage, Mat* outputImage,
|
||||||
bool debug_on, vector<int> debug_matches_array
|
bool debug_on, vector<int> debug_matches_array
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
RecognitionResult result;
|
RecognitionResult result;
|
||||||
|
|
||||||
result.haswinner = false;
|
result.haswinner = false;
|
||||||
@@ -387,4 +390,6 @@ RecognitionResult FeatureMatcher::recognize( const Mat& queryImg, bool drawOnIma
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -30,16 +30,18 @@
|
|||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
namespace alpr
|
||||||
struct RecognitionResult
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct RecognitionResult
|
||||||
|
{
|
||||||
bool haswinner;
|
bool haswinner;
|
||||||
std::string winner;
|
std::string winner;
|
||||||
int confidence;
|
int confidence;
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
class FeatureMatcher
|
class FeatureMatcher
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FeatureMatcher(Config* config);
|
FeatureMatcher(Config* config);
|
||||||
@@ -72,6 +74,7 @@ class FeatureMatcher
|
|||||||
void surfStyleMatching( const cv::Mat& queryDescriptors, std::vector<cv::KeyPoint> queryKeypoints,
|
void surfStyleMatching( const cv::Mat& queryDescriptors, std::vector<cv::KeyPoint> queryKeypoints,
|
||||||
std::vector<cv::DMatch>& matches12 );
|
std::vector<cv::DMatch>& matches12 );
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
#endif // OPENALPR_FEATUREMATCHER_H
|
#endif // OPENALPR_FEATUREMATCHER_H
|
||||||
|
@@ -26,21 +26,24 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
LicensePlateCandidate::LicensePlateCandidate(PipelineData* pipeline_data)
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
LicensePlateCandidate::LicensePlateCandidate(PipelineData* pipeline_data)
|
||||||
|
{
|
||||||
this->pipeline_data = pipeline_data;
|
this->pipeline_data = pipeline_data;
|
||||||
this->config = pipeline_data->config;
|
this->config = pipeline_data->config;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LicensePlateCandidate::~LicensePlateCandidate()
|
LicensePlateCandidate::~LicensePlateCandidate()
|
||||||
{
|
{
|
||||||
delete charSegmenter;
|
delete charSegmenter;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must delete this pointer in parent class
|
// Must delete this pointer in parent class
|
||||||
void LicensePlateCandidate::recognize()
|
void LicensePlateCandidate::recognize()
|
||||||
{
|
{
|
||||||
charSegmenter = NULL;
|
charSegmenter = NULL;
|
||||||
|
|
||||||
pipeline_data->plate_area_confidence = 0;
|
pipeline_data->plate_area_confidence = 0;
|
||||||
@@ -121,6 +124,6 @@ void LicensePlateCandidate::recognize()
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -37,12 +37,12 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "pipeline_data.h"
|
#include "pipeline_data.h"
|
||||||
|
|
||||||
//vector<Rect> getCharacterRegions(Mat frame, vector<Rect> regionsOfInterest);
|
namespace alpr
|
||||||
//vector<RotatedRect> getCharSegmentsBetweenLines(Mat img, vector<vector<Point> > contours, LineSegment top, LineSegment bottom);
|
|
||||||
|
|
||||||
class LicensePlateCandidate
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class LicensePlateCandidate
|
||||||
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LicensePlateCandidate(PipelineData* pipeline_data);
|
LicensePlateCandidate(PipelineData* pipeline_data);
|
||||||
virtual ~LicensePlateCandidate();
|
virtual ~LicensePlateCandidate();
|
||||||
@@ -62,6 +62,7 @@ class LicensePlateCandidate
|
|||||||
|
|
||||||
cv::Size getCropSize(std::vector<cv::Point2f> areaCorners);
|
cv::Size getCropSize(std::vector<cv::Point2f> areaCorners);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
#endif // OPENALPR_LICENSEPLATECANDIDATE_H
|
#endif // OPENALPR_LICENSEPLATECANDIDATE_H
|
||||||
|
@@ -23,9 +23,12 @@ using namespace std;
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace tesseract;
|
using namespace tesseract;
|
||||||
|
|
||||||
OCR::OCR(Config* config)
|
namespace alpr
|
||||||
: postProcessor(config)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
OCR::OCR(Config* config)
|
||||||
|
: postProcessor(config)
|
||||||
|
{
|
||||||
const string EXPECTED_TESSERACT_VERSION = "3.03";
|
const string EXPECTED_TESSERACT_VERSION = "3.03";
|
||||||
|
|
||||||
this->config = config;
|
this->config = config;
|
||||||
@@ -41,15 +44,15 @@ OCR::OCR(Config* config)
|
|||||||
tesseract.Init(config->getTessdataPrefix().c_str(), config->ocrLanguage.c_str() );
|
tesseract.Init(config->getTessdataPrefix().c_str(), config->ocrLanguage.c_str() );
|
||||||
tesseract.SetVariable("save_blob_choices", "T");
|
tesseract.SetVariable("save_blob_choices", "T");
|
||||||
tesseract.SetPageSegMode(PSM_SINGLE_CHAR);
|
tesseract.SetPageSegMode(PSM_SINGLE_CHAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
OCR::~OCR()
|
OCR::~OCR()
|
||||||
{
|
{
|
||||||
tesseract.Clear();
|
tesseract.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OCR::performOCR(PipelineData* pipeline_data)
|
void OCR::performOCR(PipelineData* pipeline_data)
|
||||||
{
|
{
|
||||||
timespec startTime;
|
timespec startTime;
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
|
|
||||||
@@ -131,4 +134,6 @@ void OCR::performOCR(PipelineData* pipeline_data)
|
|||||||
getTime(&endTime);
|
getTime(&endTime);
|
||||||
cout << "OCR Time: " << diffclock(startTime, endTime) << "ms." << endl;
|
cout << "OCR Time: " << diffclock(startTime, endTime) << "ms." << endl;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -34,9 +34,12 @@
|
|||||||
|
|
||||||
#include "tesseract/baseapi.h"
|
#include "tesseract/baseapi.h"
|
||||||
|
|
||||||
class OCR
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class OCR
|
||||||
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
OCR(Config* config);
|
OCR(Config* config);
|
||||||
virtual ~OCR();
|
virtual ~OCR();
|
||||||
@@ -50,6 +53,8 @@ class OCR
|
|||||||
|
|
||||||
tesseract::TessBaseAPI tesseract;
|
tesseract::TessBaseAPI tesseract;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // OPENALPR_OCR_H
|
#endif // OPENALPR_OCR_H
|
||||||
|
@@ -3,8 +3,11 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
PipelineData::PipelineData(Mat colorImage, Rect regionOfInterest, Config* config)
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
PipelineData::PipelineData(Mat colorImage, Rect regionOfInterest, Config* config)
|
||||||
|
{
|
||||||
this->colorImg = colorImage;
|
this->colorImg = colorImage;
|
||||||
cvtColor(this->colorImg, this->grayImg, CV_BGR2GRAY);
|
cvtColor(this->colorImg, this->grayImg, CV_BGR2GRAY);
|
||||||
|
|
||||||
@@ -14,18 +17,20 @@ PipelineData::PipelineData(Mat colorImage, Rect regionOfInterest, Config* config
|
|||||||
this->region_confidence = 0;
|
this->region_confidence = 0;
|
||||||
|
|
||||||
plate_inverted = false;
|
plate_inverted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PipelineData::~PipelineData()
|
PipelineData::~PipelineData()
|
||||||
{
|
{
|
||||||
clearThresholds();
|
clearThresholds();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PipelineData::clearThresholds()
|
void PipelineData::clearThresholds()
|
||||||
{
|
{
|
||||||
for (uint i = 0; i < thresholds.size(); i++)
|
for (uint i = 0; i < thresholds.size(); i++)
|
||||||
{
|
{
|
||||||
thresholds[i].release();
|
thresholds[i].release();
|
||||||
}
|
}
|
||||||
thresholds.clear();
|
thresholds.clear();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -7,9 +7,12 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "textdetection/textline.h"
|
#include "textdetection/textline.h"
|
||||||
|
|
||||||
class PipelineData
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class PipelineData
|
||||||
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PipelineData(cv::Mat colorImage, cv::Rect regionOfInterest, Config* config);
|
PipelineData(cv::Mat colorImage, cv::Rect regionOfInterest, Config* config);
|
||||||
virtual ~PipelineData();
|
virtual ~PipelineData();
|
||||||
@@ -52,7 +55,8 @@ class PipelineData
|
|||||||
|
|
||||||
// OCR
|
// OCR
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // OPENALPR_PIPELINEDATA_H
|
#endif // OPENALPR_PIPELINEDATA_H
|
@@ -21,8 +21,12 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
PostProcess::PostProcess(Config* config)
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
PostProcess::PostProcess(Config* config)
|
||||||
|
{
|
||||||
this->config = config;
|
this->config = config;
|
||||||
|
|
||||||
stringstream filename;
|
stringstream filename;
|
||||||
@@ -53,10 +57,10 @@ PostProcess::PostProcess(Config* config)
|
|||||||
//vector<RegexRule> test = rules["base"];
|
//vector<RegexRule> test = rules["base"];
|
||||||
//for (int i = 0; i < test.size(); i++)
|
//for (int i = 0; i < test.size(); i++)
|
||||||
// cout << "Rule: " << test[i].regex << endl;
|
// cout << "Rule: " << test[i].regex << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
PostProcess::~PostProcess()
|
PostProcess::~PostProcess()
|
||||||
{
|
{
|
||||||
// TODO: Delete all entries in rules vector
|
// TODO: Delete all entries in rules vector
|
||||||
map<string, vector<RegexRule*> >::iterator iter;
|
map<string, vector<RegexRule*> >::iterator iter;
|
||||||
|
|
||||||
@@ -67,10 +71,10 @@ PostProcess::~PostProcess()
|
|||||||
delete iter->second[i];
|
delete iter->second[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcess::addLetter(string letter, int charposition, float score)
|
void PostProcess::addLetter(string letter, int charposition, float score)
|
||||||
{
|
{
|
||||||
if (score < config->postProcessMinConfidence)
|
if (score < config->postProcessMinConfidence)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -86,10 +90,10 @@ void PostProcess::addLetter(string letter, int charposition, float score)
|
|||||||
//{
|
//{
|
||||||
// insertLetter('O', charposition, score - 0.5);
|
// insertLetter('O', charposition, score - 0.5);
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcess::insertLetter(string letter, int charposition, float score)
|
void PostProcess::insertLetter(string letter, int charposition, float score)
|
||||||
{
|
{
|
||||||
score = score - config->postProcessMinConfidence;
|
score = score - config->postProcessMinConfidence;
|
||||||
|
|
||||||
int existingIndex = -1;
|
int existingIndex = -1;
|
||||||
@@ -126,10 +130,10 @@ void PostProcess::insertLetter(string letter, int charposition, float score)
|
|||||||
letters[charposition][existingIndex].occurences = letters[charposition][existingIndex].occurences + 1;
|
letters[charposition][existingIndex].occurences = letters[charposition][existingIndex].occurences + 1;
|
||||||
letters[charposition][existingIndex].totalscore = letters[charposition][existingIndex].totalscore + score;
|
letters[charposition][existingIndex].totalscore = letters[charposition][existingIndex].totalscore + score;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcess::clear()
|
void PostProcess::clear()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < letters.size(); i++)
|
for (int i = 0; i < letters.size(); i++)
|
||||||
{
|
{
|
||||||
letters[i].clear();
|
letters[i].clear();
|
||||||
@@ -143,10 +147,10 @@ void PostProcess::clear()
|
|||||||
|
|
||||||
bestChars = "";
|
bestChars = "";
|
||||||
matchesTemplate = false;
|
matchesTemplate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcess::analyze(string templateregion, int topn)
|
void PostProcess::analyze(string templateregion, int topn)
|
||||||
{
|
{
|
||||||
timespec startTime;
|
timespec startTime;
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
|
|
||||||
@@ -292,10 +296,10 @@ void PostProcess::analyze(string templateregion, int topn)
|
|||||||
|
|
||||||
if (this->config->debugPostProcess)
|
if (this->config->debugPostProcess)
|
||||||
cout << "PostProcess Analysis Complete: " << bestChars << " -- MATCH: " << matchesTemplate << endl;
|
cout << "PostProcess Analysis Complete: " << bestChars << " -- MATCH: " << matchesTemplate << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
float PostProcess::calculateMaxConfidenceScore()
|
float PostProcess::calculateMaxConfidenceScore()
|
||||||
{
|
{
|
||||||
// Take the best score for each char position and average it.
|
// Take the best score for each char position and average it.
|
||||||
|
|
||||||
float totalScore = 0;
|
float totalScore = 0;
|
||||||
@@ -314,20 +318,20 @@ float PostProcess::calculateMaxConfidenceScore()
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return totalScore / ((float) numScores);
|
return totalScore / ((float) numScores);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finds the minimum number of letters to include in the recursive sorting algorithm.
|
// Finds the minimum number of letters to include in the recursive sorting algorithm.
|
||||||
// For example, if I have letters
|
// For example, if I have letters
|
||||||
// A-200 B-100 C-100
|
// A-200 B-100 C-100
|
||||||
// X-99 Y-95 Z-90
|
// X-99 Y-95 Z-90
|
||||||
// Q-55 R-80
|
// Q-55 R-80
|
||||||
// And my topN value was 3, this would return:
|
// And my topN value was 3, this would return:
|
||||||
// 0, 1, 1
|
// 0, 1, 1
|
||||||
// Which represents:
|
// Which represents:
|
||||||
// A-200 B-100 C-100
|
// A-200 B-100 C-100
|
||||||
// Y-95 Z-90
|
// Y-95 Z-90
|
||||||
vector<int> PostProcess::getMaxDepth(int topn)
|
vector<int> PostProcess::getMaxDepth(int topn)
|
||||||
{
|
{
|
||||||
vector<int> depth;
|
vector<int> depth;
|
||||||
for (int i = 0; i < letters.size(); i++)
|
for (int i = 0; i < letters.size(); i++)
|
||||||
depth.push_back(0);
|
depth.push_back(0);
|
||||||
@@ -344,10 +348,10 @@ vector<int> PostProcess::getMaxDepth(int topn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return depth;
|
return depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PostProcess::getPermutationCount(vector<int> depth)
|
int PostProcess::getPermutationCount(vector<int> depth)
|
||||||
{
|
{
|
||||||
int permutationCount = 1;
|
int permutationCount = 1;
|
||||||
for (int i = 0; i < depth.size(); i++)
|
for (int i = 0; i < depth.size(); i++)
|
||||||
{
|
{
|
||||||
@@ -355,10 +359,10 @@ int PostProcess::getPermutationCount(vector<int> depth)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return permutationCount;
|
return permutationCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PostProcess::getNextLeastDrop(vector<int> depth)
|
int PostProcess::getNextLeastDrop(vector<int> depth)
|
||||||
{
|
{
|
||||||
int nextLeastDropCharPos = -1;
|
int nextLeastDropCharPos = -1;
|
||||||
float leastNextDrop = 99999999999;
|
float leastNextDrop = 99999999999;
|
||||||
|
|
||||||
@@ -377,15 +381,15 @@ int PostProcess::getNextLeastDrop(vector<int> depth)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return nextLeastDropCharPos;
|
return nextLeastDropCharPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
const vector<PPResult> PostProcess::getResults()
|
const vector<PPResult> PostProcess::getResults()
|
||||||
{
|
{
|
||||||
return this->allPossibilities;
|
return this->allPossibilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcess::findAllPermutations(vector<Letter> prevletters, int charPos, int substitutionsLeft)
|
void PostProcess::findAllPermutations(vector<Letter> prevletters, int charPos, int substitutionsLeft)
|
||||||
{
|
{
|
||||||
if (substitutionsLeft < 0)
|
if (substitutionsLeft < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -432,24 +436,24 @@ void PostProcess::findAllPermutations(vector<Letter> prevletters, int charPos, i
|
|||||||
// Just pass it along
|
// Just pass it along
|
||||||
findAllPermutations(prevletters, charPos + 1, substitutionsLeft);
|
findAllPermutations(prevletters, charPos + 1, substitutionsLeft);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wordCompare( const PPResult &left, const PPResult &right )
|
bool wordCompare( const PPResult &left, const PPResult &right )
|
||||||
{
|
{
|
||||||
if (left.totalscore < right.totalscore)
|
if (left.totalscore < right.totalscore)
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool letterCompare( const Letter &left, const Letter &right )
|
bool letterCompare( const Letter &left, const Letter &right )
|
||||||
{
|
{
|
||||||
if (left.totalscore < right.totalscore)
|
if (left.totalscore < right.totalscore)
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
RegexRule::RegexRule(string region, string pattern)
|
RegexRule::RegexRule(string region, string pattern)
|
||||||
{
|
{
|
||||||
this->original = pattern;
|
this->original = pattern;
|
||||||
this->region = region;
|
this->region = region;
|
||||||
|
|
||||||
@@ -487,18 +491,18 @@ RegexRule::RegexRule(string region, string pattern)
|
|||||||
//cout << "AA " << this->region << ": " << original << " regex: " << regex << endl;
|
//cout << "AA " << this->region << ": " << original << " regex: " << regex << endl;
|
||||||
//for (int z = 0; z < this->skipPositions.size(); z++)
|
//for (int z = 0; z < this->skipPositions.size(); z++)
|
||||||
// cout << "AA Skip position: " << skipPositions[z] << endl;
|
// cout << "AA Skip position: " << skipPositions[z] << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RegexRule::match(string text)
|
bool RegexRule::match(string text)
|
||||||
{
|
{
|
||||||
if (text.length() != numchars)
|
if (text.length() != numchars)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return trexp.Match(text.c_str());
|
return trexp.Match(text.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
string RegexRule::filterSkips(string text)
|
string RegexRule::filterSkips(string text)
|
||||||
{
|
{
|
||||||
string response = "";
|
string response = "";
|
||||||
for (int i = 0; i < text.size(); i++)
|
for (int i = 0; i < text.size(); i++)
|
||||||
{
|
{
|
||||||
@@ -517,4 +521,6 @@ string RegexRule::filterSkips(string text)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -32,26 +32,29 @@
|
|||||||
|
|
||||||
#define SKIP_CHAR "~"
|
#define SKIP_CHAR "~"
|
||||||
|
|
||||||
struct Letter
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct Letter
|
||||||
|
{
|
||||||
std::string letter;
|
std::string letter;
|
||||||
int charposition;
|
int charposition;
|
||||||
float totalscore;
|
float totalscore;
|
||||||
int occurences;
|
int occurences;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PPResult
|
struct PPResult
|
||||||
{
|
{
|
||||||
std::string letters;
|
std::string letters;
|
||||||
float totalscore;
|
float totalscore;
|
||||||
bool matchesTemplate;
|
bool matchesTemplate;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool wordCompare( const PPResult &left, const PPResult &right );
|
bool wordCompare( const PPResult &left, const PPResult &right );
|
||||||
bool letterCompare( const Letter &left, const Letter &right );
|
bool letterCompare( const Letter &left, const Letter &right );
|
||||||
|
|
||||||
class RegexRule
|
class RegexRule
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RegexRule(std::string region, std::string pattern);
|
RegexRule(std::string region, std::string pattern);
|
||||||
|
|
||||||
@@ -65,10 +68,10 @@ class RegexRule
|
|||||||
std::string regex;
|
std::string regex;
|
||||||
std::string region;
|
std::string region;
|
||||||
std::vector<int> skipPositions;
|
std::vector<int> skipPositions;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PostProcess
|
class PostProcess
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PostProcess(Config* config);
|
PostProcess(Config* config);
|
||||||
~PostProcess();
|
~PostProcess();
|
||||||
@@ -103,25 +106,7 @@ class PostProcess
|
|||||||
std::vector<int> getMaxDepth(int topn);
|
std::vector<int> getMaxDepth(int topn);
|
||||||
int getPermutationCount(std::vector<int> depth);
|
int getPermutationCount(std::vector<int> depth);
|
||||||
int getNextLeastDrop(std::vector<int> depth);
|
int getNextLeastDrop(std::vector<int> depth);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
}
|
||||||
class LetterScores
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LetterScores(int numCharPositions);
|
|
||||||
|
|
||||||
void addScore(char letter, int charposition, float score);
|
|
||||||
|
|
||||||
vector<char> getBestScore();
|
|
||||||
float getConfidence();
|
|
||||||
|
|
||||||
private:
|
|
||||||
int numCharPositions;
|
|
||||||
|
|
||||||
vector<char> letters;
|
|
||||||
vector<int> charpositions;
|
|
||||||
vector<float> scores;
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
#endif // OPENALPR_POSTPROCESS_H
|
#endif // OPENALPR_POSTPROCESS_H
|
||||||
|
@@ -24,8 +24,11 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data)
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data)
|
||||||
|
{
|
||||||
this->pipeline_data = pipeline_data;
|
this->pipeline_data = pipeline_data;
|
||||||
this->config = pipeline_data->config;
|
this->config = pipeline_data->config;
|
||||||
|
|
||||||
@@ -59,37 +62,37 @@ CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data)
|
|||||||
displayImage(config, "CharacterSegmenter Thresholds", drawImageDashboard(pipeline_data->thresholds, CV_8U, 3));
|
displayImage(config, "CharacterSegmenter Thresholds", drawImageDashboard(pipeline_data->thresholds, CV_8U, 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (this->config->debugCharSegmenter && pipeline_data->textLines.size() > 0)
|
// if (this->config->debugCharSegmenter && pipeline_data->textLines.size() > 0)
|
||||||
// {
|
// {
|
||||||
// Mat img_contours(charAnalysis->bestThreshold.size(), CV_8U);
|
// Mat img_contours(charAnalysis->bestThreshold.size(), CV_8U);
|
||||||
// charAnalysis->bestThreshold.copyTo(img_contours);
|
// charAnalysis->bestThreshold.copyTo(img_contours);
|
||||||
// cvtColor(img_contours, img_contours, CV_GRAY2RGB);
|
// cvtColor(img_contours, img_contours, CV_GRAY2RGB);
|
||||||
//
|
//
|
||||||
// vector<vector<Point> > allowedContours;
|
// vector<vector<Point> > allowedContours;
|
||||||
// for (uint i = 0; i < charAnalysis->bestContours.size(); i++)
|
// for (uint i = 0; i < charAnalysis->bestContours.size(); i++)
|
||||||
// {
|
// {
|
||||||
// if (charAnalysis->bestContours.goodIndices[i])
|
// if (charAnalysis->bestContours.goodIndices[i])
|
||||||
// allowedContours.push_back(charAnalysis->bestContours.contours[i]);
|
// allowedContours.push_back(charAnalysis->bestContours.contours[i]);
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// drawContours(img_contours, charAnalysis->bestContours.contours,
|
// drawContours(img_contours, charAnalysis->bestContours.contours,
|
||||||
// -1, // draw all contours
|
// -1, // draw all contours
|
||||||
// cv::Scalar(255,0,0), // in blue
|
// cv::Scalar(255,0,0), // in blue
|
||||||
// 1); // with a thickness of 1
|
// 1); // with a thickness of 1
|
||||||
//
|
//
|
||||||
// drawContours(img_contours, allowedContours,
|
// drawContours(img_contours, allowedContours,
|
||||||
// -1, // draw all contours
|
// -1, // draw all contours
|
||||||
// cv::Scalar(0,255,0), // in green
|
// cv::Scalar(0,255,0), // in green
|
||||||
// 1); // with a thickness of 1
|
// 1); // with a thickness of 1
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// line(img_contours, pipeline_data->textLines[0].linePolygon[0], pipeline_data->textLines[0].linePolygon[1], Scalar(255, 0, 255), 1);
|
// line(img_contours, pipeline_data->textLines[0].linePolygon[0], pipeline_data->textLines[0].linePolygon[1], Scalar(255, 0, 255), 1);
|
||||||
// line(img_contours, pipeline_data->textLines[0].linePolygon[3], pipeline_data->textLines[0].linePolygon[2], Scalar(255, 0, 255), 1);
|
// line(img_contours, pipeline_data->textLines[0].linePolygon[3], pipeline_data->textLines[0].linePolygon[2], Scalar(255, 0, 255), 1);
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Mat bordered = addLabel(img_contours, "Best Contours");
|
// Mat bordered = addLabel(img_contours, "Best Contours");
|
||||||
// imgDbgGeneral.push_back(bordered);
|
// imgDbgGeneral.push_back(bordered);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -130,7 +133,7 @@ CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data)
|
|||||||
allHistograms.push_back(addLabel(histoCopy, label));
|
allHistograms.push_back(addLabel(histoCopy, label));
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
float score = 0;
|
float score = 0;
|
||||||
vector<Rect> charBoxes = getHistogramBoxes(vertHistogram, avgCharWidth, avgCharHeight, &score);
|
vector<Rect> charBoxes = getHistogramBoxes(vertHistogram, avgCharWidth, avgCharHeight, &score);
|
||||||
|
|
||||||
@@ -226,17 +229,17 @@ CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data)
|
|||||||
getTime(&endTime);
|
getTime(&endTime);
|
||||||
cout << "Character Segmenter Time: " << diffclock(startTime, endTime) << "ms." << endl;
|
cout << "Character Segmenter Time: " << diffclock(startTime, endTime) << "ms." << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterSegmenter::~CharacterSegmenter()
|
CharacterSegmenter::~CharacterSegmenter()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given a histogram and the horizontal line boundaries, respond with an array of boxes where the characters are
|
// Given a histogram and the horizontal line boundaries, respond with an array of boxes where the characters are
|
||||||
// Scores the histogram quality as well based on num chars, char volume, and even separation
|
// Scores the histogram quality as well based on num chars, char volume, and even separation
|
||||||
vector<Rect> CharacterSegmenter::getHistogramBoxes(VerticalHistogram histogram, float avgCharWidth, float avgCharHeight, float* score)
|
vector<Rect> CharacterSegmenter::getHistogramBoxes(VerticalHistogram histogram, float avgCharWidth, float avgCharHeight, float* score)
|
||||||
{
|
{
|
||||||
float MIN_HISTOGRAM_HEIGHT = avgCharHeight * config->segmentationMinCharHeightPercent;
|
float MIN_HISTOGRAM_HEIGHT = avgCharHeight * config->segmentationMinCharHeightPercent;
|
||||||
|
|
||||||
float MAX_SEGMENT_WIDTH = avgCharWidth * config->segmentationMaxCharWidthvsAverage;
|
float MAX_SEGMENT_WIDTH = avgCharWidth * config->segmentationMaxCharWidthvsAverage;
|
||||||
@@ -287,10 +290,10 @@ vector<Rect> CharacterSegmenter::getHistogramBoxes(VerticalHistogram histogram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return charBoxes;
|
return charBoxes;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<Rect> CharacterSegmenter::getBestCharBoxes(Mat img, vector<Rect> charBoxes, float avgCharWidth)
|
vector<Rect> CharacterSegmenter::getBestCharBoxes(Mat img, vector<Rect> charBoxes, float avgCharWidth)
|
||||||
{
|
{
|
||||||
float MAX_SEGMENT_WIDTH = avgCharWidth * 1.65;
|
float MAX_SEGMENT_WIDTH = avgCharWidth * 1.65;
|
||||||
|
|
||||||
// This histogram is based on how many char boxes (from ALL of the many thresholded images) are covering each column
|
// This histogram is based on how many char boxes (from ALL of the many thresholded images) are covering each column
|
||||||
@@ -403,10 +406,10 @@ vector<Rect> CharacterSegmenter::getBestCharBoxes(Mat img, vector<Rect> charBoxe
|
|||||||
}
|
}
|
||||||
|
|
||||||
return bestBoxes;
|
return bestBoxes;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<Rect> CharacterSegmenter::get1DHits(Mat img, int yOffset)
|
vector<Rect> CharacterSegmenter::get1DHits(Mat img, int yOffset)
|
||||||
{
|
{
|
||||||
vector<Rect> hits;
|
vector<Rect> hits;
|
||||||
|
|
||||||
bool onSegment = false;
|
bool onSegment = false;
|
||||||
@@ -434,10 +437,10 @@ vector<Rect> CharacterSegmenter::get1DHits(Mat img, int yOffset)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return hits;
|
return hits;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterSegmenter::removeSmallContours(vector<Mat> thresholds, float avgCharHeight, TextLine textLine)
|
void CharacterSegmenter::removeSmallContours(vector<Mat> thresholds, float avgCharHeight, TextLine textLine)
|
||||||
{
|
{
|
||||||
//const float MIN_CHAR_AREA = 0.02 * avgCharWidth * avgCharHeight; // To clear out the tiny specks
|
//const float MIN_CHAR_AREA = 0.02 * avgCharWidth * avgCharHeight; // To clear out the tiny specks
|
||||||
const float MIN_CONTOUR_HEIGHT = 0.3 * avgCharHeight;
|
const float MIN_CONTOUR_HEIGHT = 0.3 * avgCharHeight;
|
||||||
|
|
||||||
@@ -467,10 +470,10 @@ void CharacterSegmenter::removeSmallContours(vector<Mat> thresholds, float avgCh
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<Rect> CharacterSegmenter::combineCloseBoxes( vector<Rect> charBoxes, float biggestCharWidth)
|
vector<Rect> CharacterSegmenter::combineCloseBoxes( vector<Rect> charBoxes, float biggestCharWidth)
|
||||||
{
|
{
|
||||||
vector<Rect> newCharBoxes;
|
vector<Rect> newCharBoxes;
|
||||||
|
|
||||||
for (uint i = 0; i < charBoxes.size(); i++)
|
for (uint i = 0; i < charBoxes.size(); i++)
|
||||||
@@ -511,10 +514,10 @@ vector<Rect> CharacterSegmenter::combineCloseBoxes( vector<Rect> charBoxes, floa
|
|||||||
}
|
}
|
||||||
|
|
||||||
return newCharBoxes;
|
return newCharBoxes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterSegmenter::cleanCharRegions(vector<Mat> thresholds, vector<Rect> charRegions)
|
void CharacterSegmenter::cleanCharRegions(vector<Mat> thresholds, vector<Rect> charRegions)
|
||||||
{
|
{
|
||||||
const float MIN_SPECKLE_HEIGHT_PERCENT = 0.13;
|
const float MIN_SPECKLE_HEIGHT_PERCENT = 0.13;
|
||||||
const float MIN_SPECKLE_WIDTH_PX = 3;
|
const float MIN_SPECKLE_WIDTH_PX = 3;
|
||||||
const float MIN_CONTOUR_AREA_PERCENT = 0.1;
|
const float MIN_CONTOUR_AREA_PERCENT = 0.1;
|
||||||
@@ -531,8 +534,8 @@ void CharacterSegmenter::cleanCharRegions(vector<Mat> thresholds, vector<Rect> c
|
|||||||
thresholds[i].copyTo(tempImg);
|
thresholds[i].copyTo(tempImg);
|
||||||
|
|
||||||
//Mat element = getStructuringElement( 1,
|
//Mat element = getStructuringElement( 1,
|
||||||
// Size( 2 + 1, 2+1 ),
|
// Size( 2 + 1, 2+1 ),
|
||||||
// Point( 1, 1 ) );
|
// Point( 1, 1 ) );
|
||||||
//dilate(thresholds[i], tempImg, element);
|
//dilate(thresholds[i], tempImg, element);
|
||||||
//morphologyEx(thresholds[i], tempImg, MORPH_CLOSE, element);
|
//morphologyEx(thresholds[i], tempImg, MORPH_CLOSE, element);
|
||||||
//drawAndWait(&tempImg);
|
//drawAndWait(&tempImg);
|
||||||
@@ -624,10 +627,10 @@ void CharacterSegmenter::cleanCharRegions(vector<Mat> thresholds, vector<Rect> c
|
|||||||
line(thresholds[i], Point(charRegions[j].x + charRegions[j].width + 1, charRegions[j].y), Point(charRegions[j].x + charRegions[j].width + 1, charRegions[j].y + charRegions[j].height), Scalar(0, 0, 0));
|
line(thresholds[i], Point(charRegions[j].x + charRegions[j].width + 1, charRegions[j].y), Point(charRegions[j].x + charRegions[j].width + 1, charRegions[j].y + charRegions[j].height), Scalar(0, 0, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterSegmenter::cleanBasedOnColor(vector<Mat> thresholds, Mat colorMask, vector<Rect> charRegions)
|
void CharacterSegmenter::cleanBasedOnColor(vector<Mat> thresholds, Mat colorMask, vector<Rect> charRegions)
|
||||||
{
|
{
|
||||||
// If I knock out x% of the contour area from this thing (after applying the color filter)
|
// If I knock out x% of the contour area from this thing (after applying the color filter)
|
||||||
// Consider it a bad news bear. REmove the whole area.
|
// Consider it a bad news bear. REmove the whole area.
|
||||||
const float MIN_PERCENT_CHUNK_REMOVED = 0.6;
|
const float MIN_PERCENT_CHUNK_REMOVED = 0.6;
|
||||||
@@ -662,9 +665,9 @@ void CharacterSegmenter::cleanBasedOnColor(vector<Mat> thresholds, Mat colorMask
|
|||||||
//tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask + Thresh"));
|
//tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask + Thresh"));
|
||||||
//bitwise_and(colorMask, thresholdCopy2, thresholdCopy2);
|
//bitwise_and(colorMask, thresholdCopy2, thresholdCopy2);
|
||||||
//tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask + Thresh + Color"));
|
//tmpDebug.push_back(addLabel(thresholdCopy2, "box Mask + Thresh + Color"));
|
||||||
//
|
//
|
||||||
// Mat tmpytmp = addLabel(thresholdCopy2, "box Mask + Thresh + Color");
|
// Mat tmpytmp = addLabel(thresholdCopy2, "box Mask + Thresh + Color");
|
||||||
// Mat tmpx = drawImageDashboard(tmpDebug, tmpytmp.type(), 1);
|
// Mat tmpx = drawImageDashboard(tmpDebug, tmpytmp.type(), 1);
|
||||||
//drawAndWait( &tmpx );
|
//drawAndWait( &tmpx );
|
||||||
|
|
||||||
cout << "Segmentation Filter Clean by Color: Removed Threshold " << i << " charregion " << j << endl;
|
cout << "Segmentation Filter Clean by Color: Removed Threshold " << i << " charregion " << j << endl;
|
||||||
@@ -676,10 +679,10 @@ void CharacterSegmenter::cleanBasedOnColor(vector<Mat> thresholds, Mat colorMask
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterSegmenter::cleanMostlyFullBoxes(vector<Mat> thresholds, const vector<Rect> charRegions)
|
void CharacterSegmenter::cleanMostlyFullBoxes(vector<Mat> thresholds, const vector<Rect> charRegions)
|
||||||
{
|
{
|
||||||
float MAX_FILLED = 0.95 * 255;
|
float MAX_FILLED = 0.95 * 255;
|
||||||
|
|
||||||
for (uint i = 0; i < charRegions.size(); i++)
|
for (uint i = 0; i < charRegions.size(); i++)
|
||||||
@@ -702,10 +705,10 @@ void CharacterSegmenter::cleanMostlyFullBoxes(vector<Mat> thresholds, const vect
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<Rect> CharacterSegmenter::filterMostlyEmptyBoxes(vector<Mat> thresholds, const vector<Rect> charRegions)
|
vector<Rect> CharacterSegmenter::filterMostlyEmptyBoxes(vector<Mat> thresholds, const vector<Rect> charRegions)
|
||||||
{
|
{
|
||||||
// Of the n thresholded images, if box 3 (for example) is empty in half (for example) of the thresholded images,
|
// Of the n thresholded images, if box 3 (for example) is empty in half (for example) of the thresholded images,
|
||||||
// clear all data for every box #3.
|
// clear all data for every box #3.
|
||||||
|
|
||||||
@@ -799,10 +802,10 @@ vector<Rect> CharacterSegmenter::filterMostlyEmptyBoxes(vector<Mat> thresholds,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return newCharRegions;
|
return newCharRegions;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterSegmenter::filterEdgeBoxes(vector<Mat> thresholds, const vector<Rect> charRegions, float avgCharWidth, float avgCharHeight)
|
void CharacterSegmenter::filterEdgeBoxes(vector<Mat> thresholds, const vector<Rect> charRegions, float avgCharWidth, float avgCharHeight)
|
||||||
{
|
{
|
||||||
const float MIN_ANGLE_FOR_ROTATION = 0.4;
|
const float MIN_ANGLE_FOR_ROTATION = 0.4;
|
||||||
int MIN_CONNECTED_EDGE_PIXELS = (avgCharHeight * 1.5);
|
int MIN_CONNECTED_EDGE_PIXELS = (avgCharHeight * 1.5);
|
||||||
|
|
||||||
@@ -959,10 +962,10 @@ void CharacterSegmenter::filterEdgeBoxes(vector<Mat> thresholds, const vector<Re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int CharacterSegmenter::getLongestBlobLengthBetweenLines(Mat img, int col)
|
int CharacterSegmenter::getLongestBlobLengthBetweenLines(Mat img, int col)
|
||||||
{
|
{
|
||||||
int longestBlobLength = 0;
|
int longestBlobLength = 0;
|
||||||
|
|
||||||
bool onSegment = false;
|
bool onSegment = false;
|
||||||
@@ -1007,12 +1010,12 @@ int CharacterSegmenter::getLongestBlobLengthBetweenLines(Mat img, int col)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return longestBlobLength;
|
return longestBlobLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks to see if a skinny, tall line (extending above or below the char Height) is inside the given box.
|
// Checks to see if a skinny, tall line (extending above or below the char Height) is inside the given box.
|
||||||
// Returns the contour index if true. -1 otherwise
|
// Returns the contour index if true. -1 otherwise
|
||||||
int CharacterSegmenter::isSkinnyLineInsideBox(Mat threshold, Rect box, vector<vector<Point> > contours, vector<Vec4i> hierarchy, float avgCharWidth, float avgCharHeight)
|
int CharacterSegmenter::isSkinnyLineInsideBox(Mat threshold, Rect box, vector<vector<Point> > contours, vector<Vec4i> hierarchy, float avgCharWidth, float avgCharHeight)
|
||||||
{
|
{
|
||||||
float MIN_EDGE_CONTOUR_HEIGHT = avgCharHeight * 1.25;
|
float MIN_EDGE_CONTOUR_HEIGHT = avgCharHeight * 1.25;
|
||||||
|
|
||||||
// Sometimes the threshold is smaller than the MIN_EDGE_CONTOUR_HEIGHT.
|
// Sometimes the threshold is smaller than the MIN_EDGE_CONTOUR_HEIGHT.
|
||||||
@@ -1067,15 +1070,15 @@ int CharacterSegmenter::isSkinnyLineInsideBox(Mat threshold, Rect box, vector<ve
|
|||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat CharacterSegmenter::getCharBoxMask(Mat img_threshold, vector<Rect> charBoxes)
|
Mat CharacterSegmenter::getCharBoxMask(Mat img_threshold, vector<Rect> charBoxes)
|
||||||
{
|
{
|
||||||
Mat mask = Mat::zeros(img_threshold.size(), CV_8U);
|
Mat mask = Mat::zeros(img_threshold.size(), CV_8U);
|
||||||
for (uint i = 0; i < charBoxes.size(); i++)
|
for (uint i = 0; i < charBoxes.size(); i++)
|
||||||
rectangle(mask, charBoxes[i], Scalar(255, 255, 255), -1);
|
rectangle(mask, charBoxes[i], Scalar(255, 255, 255), -1);
|
||||||
|
|
||||||
return mask;
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -29,20 +29,21 @@
|
|||||||
#include "textdetection/textcontours.h"
|
#include "textdetection/textcontours.h"
|
||||||
#include "pipeline_data.h"
|
#include "pipeline_data.h"
|
||||||
|
|
||||||
|
namespace alpr
|
||||||
//const float MIN_BOX_WIDTH_PX = 4; // 4 pixels
|
|
||||||
|
|
||||||
const cv::Scalar COLOR_DEBUG_EDGE(0,0,255); // Red
|
|
||||||
const cv::Scalar COLOR_DEBUG_SPECKLES(0,0,255); // Red
|
|
||||||
const cv::Scalar COLOR_DEBUG_MIN_HEIGHT(255,0,0); // Blue
|
|
||||||
const cv::Scalar COLOR_DEBUG_MIN_AREA(255,0,0); // Blue
|
|
||||||
const cv::Scalar COLOR_DEBUG_FULLBOX(255,255,0); // Blue-green
|
|
||||||
const cv::Scalar COLOR_DEBUG_COLORFILTER(255,0,255); // Magenta
|
|
||||||
const cv::Scalar COLOR_DEBUG_EMPTYFILTER(0,255,255); // Yellow
|
|
||||||
|
|
||||||
class CharacterSegmenter
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
const cv::Scalar COLOR_DEBUG_EDGE(0,0,255); // Red
|
||||||
|
const cv::Scalar COLOR_DEBUG_SPECKLES(0,0,255); // Red
|
||||||
|
const cv::Scalar COLOR_DEBUG_MIN_HEIGHT(255,0,0); // Blue
|
||||||
|
const cv::Scalar COLOR_DEBUG_MIN_AREA(255,0,0); // Blue
|
||||||
|
const cv::Scalar COLOR_DEBUG_FULLBOX(255,255,0); // Blue-green
|
||||||
|
const cv::Scalar COLOR_DEBUG_COLORFILTER(255,0,255); // Magenta
|
||||||
|
const cv::Scalar COLOR_DEBUG_EMPTYFILTER(0,255,255); // Yellow
|
||||||
|
|
||||||
|
class CharacterSegmenter
|
||||||
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CharacterSegmenter(PipelineData* pipeline_data);
|
CharacterSegmenter(PipelineData* pipeline_data);
|
||||||
virtual ~CharacterSegmenter();
|
virtual ~CharacterSegmenter();
|
||||||
@@ -81,6 +82,8 @@ class CharacterSegmenter
|
|||||||
|
|
||||||
int isSkinnyLineInsideBox(cv::Mat threshold, cv::Rect box, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, float avgCharWidth, float avgCharHeight);
|
int isSkinnyLineInsideBox(cv::Mat threshold, cv::Rect box, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, float avgCharWidth, float avgCharHeight);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // OPENALPR_CHARACTERSEGMENTER_H
|
#endif // OPENALPR_CHARACTERSEGMENTER_H
|
||||||
|
@@ -19,18 +19,21 @@
|
|||||||
|
|
||||||
#include "segment.h"
|
#include "segment.h"
|
||||||
|
|
||||||
Segment::Segment(cv::Rect newSegment)
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
Segment::Segment(cv::Rect newSegment)
|
||||||
|
{
|
||||||
this->segment = newSegment;
|
this->segment = newSegment;
|
||||||
}
|
}
|
||||||
|
|
||||||
Segment::~Segment()
|
Segment::~Segment()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Segment::matches(cv::Rect newSegment)
|
bool Segment::matches(cv::Rect newSegment)
|
||||||
{
|
{
|
||||||
// Compare the two segments with a given leniency
|
// Compare the two segments with a given leniency
|
||||||
const float WIDTH_LENIENCY_MIN = 0.25;
|
const float WIDTH_LENIENCY_MIN = 0.25;
|
||||||
const float WIDTH_LENIENCY_MAX = 0.20;
|
const float WIDTH_LENIENCY_MAX = 0.20;
|
||||||
@@ -46,5 +49,6 @@ bool Segment::matches(cv::Rect newSegment)
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
@@ -25,8 +25,11 @@
|
|||||||
|
|
||||||
#include "opencv2/imgproc/imgproc.hpp"
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
|
|
||||||
class Segment
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class Segment
|
||||||
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Segment(cv::Rect newSegment);
|
Segment(cv::Rect newSegment);
|
||||||
@@ -36,6 +39,8 @@ class Segment
|
|||||||
|
|
||||||
bool matches(cv::Rect newSegment);
|
bool matches(cv::Rect newSegment);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // OPENALPR_SEGMENTATIONGROUP_H
|
#endif // OPENALPR_SEGMENTATIONGROUP_H
|
||||||
|
@@ -19,23 +19,26 @@
|
|||||||
|
|
||||||
#include "segmentationgroup.h"
|
#include "segmentationgroup.h"
|
||||||
|
|
||||||
SegmentationGroup::SegmentationGroup()
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
SegmentationGroup::SegmentationGroup()
|
||||||
|
{
|
||||||
|
|
||||||
SegmentationGroup::~SegmentationGroup()
|
}
|
||||||
{
|
|
||||||
|
|
||||||
}
|
SegmentationGroup::~SegmentationGroup()
|
||||||
|
{
|
||||||
|
|
||||||
void SegmentationGroup::add(int segmentID)
|
}
|
||||||
{
|
|
||||||
|
void SegmentationGroup::add(int segmentID)
|
||||||
|
{
|
||||||
this->segmentIDs.push_back(segmentID);
|
this->segmentIDs.push_back(segmentID);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SegmentationGroup::equals(SegmentationGroup otherGroup)
|
bool SegmentationGroup::equals(SegmentationGroup otherGroup)
|
||||||
{
|
{
|
||||||
if (segmentIDs.size() != otherGroup.segmentIDs.size())
|
if (segmentIDs.size() != otherGroup.segmentIDs.size())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -46,4 +49,6 @@ bool SegmentationGroup::equals(SegmentationGroup otherGroup)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -27,10 +27,12 @@
|
|||||||
|
|
||||||
#include "segment.h"
|
#include "segment.h"
|
||||||
|
|
||||||
|
namespace alpr
|
||||||
class SegmentationGroup
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class SegmentationGroup
|
||||||
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SegmentationGroup();
|
SegmentationGroup();
|
||||||
virtual ~SegmentationGroup();
|
virtual ~SegmentationGroup();
|
||||||
@@ -45,6 +47,8 @@ class SegmentationGroup
|
|||||||
private:
|
private:
|
||||||
float strength; // Debuggin purposes -- how many threshold segmentations match this one perfectly
|
float strength; // Debuggin purposes -- how many threshold segmentations match this one perfectly
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // OPENALPR_SEGMENTATIONGROUP_H
|
#endif // OPENALPR_SEGMENTATIONGROUP_H
|
||||||
|
@@ -22,19 +22,22 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
VerticalHistogram::VerticalHistogram(Mat inputImage, Mat mask)
|
namespace alpr
|
||||||
{
|
{
|
||||||
analyzeImage(inputImage, mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
VerticalHistogram::~VerticalHistogram()
|
VerticalHistogram::VerticalHistogram(Mat inputImage, Mat mask)
|
||||||
{
|
{
|
||||||
|
analyzeImage(inputImage, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
VerticalHistogram::~VerticalHistogram()
|
||||||
|
{
|
||||||
histoImg.release();
|
histoImg.release();
|
||||||
colHeights.clear();
|
colHeights.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VerticalHistogram::analyzeImage(Mat inputImage, Mat mask)
|
void VerticalHistogram::analyzeImage(Mat inputImage, Mat mask)
|
||||||
{
|
{
|
||||||
highestPeak = 0;
|
highestPeak = 0;
|
||||||
lowestValley = inputImage.rows;
|
lowestValley = inputImage.rows;
|
||||||
|
|
||||||
@@ -62,10 +65,10 @@ void VerticalHistogram::analyzeImage(Mat inputImage, Mat mask)
|
|||||||
for (; columnCount > 0; columnCount--)
|
for (; columnCount > 0; columnCount--)
|
||||||
histoImg.at<uchar>(inputImage.rows - columnCount, col) = 255;
|
histoImg.at<uchar>(inputImage.rows - columnCount, col) = 255;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int VerticalHistogram::getLocalMinimum(int leftX, int rightX)
|
int VerticalHistogram::getLocalMinimum(int leftX, int rightX)
|
||||||
{
|
{
|
||||||
int minimum = histoImg.rows + 1;
|
int minimum = histoImg.rows + 1;
|
||||||
int lowestX = leftX;
|
int lowestX = leftX;
|
||||||
|
|
||||||
@@ -79,10 +82,10 @@ int VerticalHistogram::getLocalMinimum(int leftX, int rightX)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return lowestX;
|
return lowestX;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VerticalHistogram::getLocalMaximum(int leftX, int rightX)
|
int VerticalHistogram::getLocalMaximum(int leftX, int rightX)
|
||||||
{
|
{
|
||||||
int maximum = -1;
|
int maximum = -1;
|
||||||
int highestX = leftX;
|
int highestX = leftX;
|
||||||
|
|
||||||
@@ -96,15 +99,15 @@ int VerticalHistogram::getLocalMaximum(int leftX, int rightX)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return highestX;
|
return highestX;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VerticalHistogram::getHeightAt(int x)
|
int VerticalHistogram::getHeightAt(int x)
|
||||||
{
|
{
|
||||||
return colHeights[x];
|
return colHeights[x];
|
||||||
}
|
}
|
||||||
|
|
||||||
void VerticalHistogram::findValleys()
|
void VerticalHistogram::findValleys()
|
||||||
{
|
{
|
||||||
//int MINIMUM_PEAK_HEIGHT = (int) (((float) highestPeak) * 0.75);
|
//int MINIMUM_PEAK_HEIGHT = (int) (((float) highestPeak) * 0.75);
|
||||||
|
|
||||||
int totalWidth = colHeights.size();
|
int totalWidth = colHeights.size();
|
||||||
@@ -141,10 +144,10 @@ void VerticalHistogram::findValleys()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HistogramDirection VerticalHistogram::getHistogramDirection(uint index)
|
HistogramDirection VerticalHistogram::getHistogramDirection(uint index)
|
||||||
{
|
{
|
||||||
int EXTRA_WIDTH_TO_AVERAGE = 2;
|
int EXTRA_WIDTH_TO_AVERAGE = 2;
|
||||||
|
|
||||||
float trailingAverage = 0;
|
float trailingAverage = 0;
|
||||||
@@ -178,4 +181,6 @@ HistogramDirection VerticalHistogram::getHistogramDirection(uint index)
|
|||||||
return FALLING;
|
return FALLING;
|
||||||
else
|
else
|
||||||
return FLAT;
|
return FLAT;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -22,19 +22,21 @@
|
|||||||
|
|
||||||
#include "opencv2/imgproc/imgproc.hpp"
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
|
|
||||||
|
namespace alpr
|
||||||
struct Valley
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct Valley
|
||||||
|
{
|
||||||
int startIndex;
|
int startIndex;
|
||||||
int endIndex;
|
int endIndex;
|
||||||
int width;
|
int width;
|
||||||
int pixelsWithin;
|
int pixelsWithin;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum HistogramDirection { RISING, FALLING, FLAT };
|
enum HistogramDirection { RISING, FALLING, FLAT };
|
||||||
|
|
||||||
class VerticalHistogram
|
class VerticalHistogram
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VerticalHistogram(cv::Mat inputImage, cv::Mat mask);
|
VerticalHistogram(cv::Mat inputImage, cv::Mat mask);
|
||||||
@@ -59,6 +61,8 @@ class VerticalHistogram
|
|||||||
void findValleys();
|
void findValleys();
|
||||||
|
|
||||||
HistogramDirection getHistogramDirection(uint index);
|
HistogramDirection getHistogramDirection(uint index);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // OPENALPR_VERTICALHISTOGRAM_H
|
#endif // OPENALPR_VERTICALHISTOGRAM_H
|
||||||
|
@@ -23,8 +23,11 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
StateIdentifier::StateIdentifier(Config* config)
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
StateIdentifier::StateIdentifier(Config* config)
|
||||||
|
{
|
||||||
this->config = config;
|
this->config = config;
|
||||||
|
|
||||||
featureMatcher = new FeatureMatcher(config);
|
featureMatcher = new FeatureMatcher(config);
|
||||||
@@ -36,18 +39,18 @@ StateIdentifier::StateIdentifier(Config* config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
featureMatcher->loadRecognitionSet(config->country);
|
featureMatcher->loadRecognitionSet(config->country);
|
||||||
}
|
}
|
||||||
|
|
||||||
StateIdentifier::~StateIdentifier()
|
StateIdentifier::~StateIdentifier()
|
||||||
{
|
{
|
||||||
delete featureMatcher;
|
delete featureMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Attempts to recognize the plate. Returns a confidence level. Updates the region code and confidence
|
// Attempts to recognize the plate. Returns a confidence level. Updates the region code and confidence
|
||||||
// If region is found, returns true.
|
// If region is found, returns true.
|
||||||
bool StateIdentifier::recognize(PipelineData* pipeline_data)
|
bool StateIdentifier::recognize(PipelineData* pipeline_data)
|
||||||
{
|
{
|
||||||
timespec startTime;
|
timespec startTime;
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
|
|
||||||
@@ -86,4 +89,6 @@ bool StateIdentifier::recognize(PipelineData* pipeline_data)
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -27,9 +27,12 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "pipeline_data.h"
|
#include "pipeline_data.h"
|
||||||
|
|
||||||
class StateIdentifier
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class StateIdentifier
|
||||||
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StateIdentifier(Config* config);
|
StateIdentifier(Config* config);
|
||||||
virtual ~StateIdentifier();
|
virtual ~StateIdentifier();
|
||||||
@@ -45,6 +48,7 @@ class StateIdentifier
|
|||||||
|
|
||||||
FeatureMatcher* featureMatcher;
|
FeatureMatcher* featureMatcher;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
#endif // OPENALPR_STATEIDENTIFIER_H
|
#endif // OPENALPR_STATEIDENTIFIER_H
|
||||||
|
@@ -1,16 +1,19 @@
|
|||||||
#include "filesystem.h"
|
#include "filesystem.h"
|
||||||
|
|
||||||
bool startsWith(std::string const &fullString, std::string const &prefix)
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
bool startsWith(std::string const &fullString, std::string const &prefix)
|
||||||
|
{
|
||||||
if(fullString.substr(0, prefix.size()).compare(prefix) == 0) {
|
if(fullString.substr(0, prefix.size()).compare(prefix) == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasEnding (std::string const &fullString, std::string const &ending)
|
bool hasEnding (std::string const &fullString, std::string const &ending)
|
||||||
{
|
{
|
||||||
if (fullString.length() >= ending.length())
|
if (fullString.length() >= ending.length())
|
||||||
{
|
{
|
||||||
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
|
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
|
||||||
@@ -19,10 +22,10 @@ bool hasEnding (std::string const &fullString, std::string const &ending)
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasEndingInsensitive(const std::string& fullString, const std::string& ending)
|
bool hasEndingInsensitive(const std::string& fullString, const std::string& ending)
|
||||||
{
|
{
|
||||||
if (fullString.length() < ending.length())
|
if (fullString.length() < ending.length())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -32,10 +35,10 @@ bool hasEndingInsensitive(const std::string& fullString, const std::string& endi
|
|||||||
if (tolower(fullString[i]) != tolower(ending[i - startidx]))
|
if (tolower(fullString[i]) != tolower(ending[i - startidx]))
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DirectoryExists( const char* pzPath )
|
bool DirectoryExists( const char* pzPath )
|
||||||
{
|
{
|
||||||
if ( pzPath == NULL) return false;
|
if ( pzPath == NULL) return false;
|
||||||
|
|
||||||
DIR *pDir;
|
DIR *pDir;
|
||||||
@@ -50,10 +53,10 @@ bool DirectoryExists( const char* pzPath )
|
|||||||
}
|
}
|
||||||
|
|
||||||
return bExists;
|
return bExists;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fileExists( const char* pzPath )
|
bool fileExists( const char* pzPath )
|
||||||
{
|
{
|
||||||
if (pzPath == NULL) return false;
|
if (pzPath == NULL) return false;
|
||||||
|
|
||||||
bool fExists = false;
|
bool fExists = false;
|
||||||
@@ -61,10 +64,10 @@ bool fileExists( const char* pzPath )
|
|||||||
fExists = f.is_open();
|
fExists = f.is_open();
|
||||||
f.close();
|
f.close();
|
||||||
return fExists;
|
return fExists;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> getFilesInDir(const char* dirPath)
|
std::vector<std::string> getFilesInDir(const char* dirPath)
|
||||||
{
|
{
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
|
|
||||||
std::vector<std::string> files;
|
std::vector<std::string> files;
|
||||||
@@ -88,10 +91,10 @@ std::vector<std::string> getFilesInDir(const char* dirPath)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool stringCompare( const std::string &left, const std::string &right )
|
bool stringCompare( const std::string &left, const std::string &right )
|
||||||
{
|
{
|
||||||
for( std::string::const_iterator lit = left.begin(), rit = right.begin(); lit != left.end() && rit != right.end(); ++lit, ++rit )
|
for( std::string::const_iterator lit = left.begin(), rit = right.begin(); lit != left.end() && rit != right.end(); ++lit, ++rit )
|
||||||
if( tolower( *lit ) < tolower( *rit ) )
|
if( tolower( *lit ) < tolower( *rit ) )
|
||||||
return true;
|
return true;
|
||||||
@@ -100,10 +103,12 @@ bool stringCompare( const std::string &left, const std::string &right )
|
|||||||
if( left.size() < right.size() )
|
if( left.size() < right.size() )
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string filenameWithoutExtension(std::string filename)
|
std::string filenameWithoutExtension(std::string filename)
|
||||||
{
|
{
|
||||||
int lastindex = filename.find_last_of(".");
|
int lastindex = filename.find_last_of(".");
|
||||||
return filename.substr(0, lastindex);
|
return filename.substr(0, lastindex);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -17,16 +17,21 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
bool startsWith(std::string const &fullString, std::string const &prefix);
|
namespace alpr
|
||||||
bool hasEnding (std::string const &fullString, std::string const &ending);
|
{
|
||||||
bool hasEndingInsensitive(const std::string& fullString, const std::string& ending);
|
|
||||||
|
|
||||||
std::string filenameWithoutExtension(std::string filename);
|
bool startsWith(std::string const &fullString, std::string const &prefix);
|
||||||
|
bool hasEnding (std::string const &fullString, std::string const &ending);
|
||||||
|
bool hasEndingInsensitive(const std::string& fullString, const std::string& ending);
|
||||||
|
|
||||||
bool DirectoryExists( const char* pzPath );
|
std::string filenameWithoutExtension(std::string filename);
|
||||||
bool fileExists( const char* pzPath );
|
|
||||||
std::vector<std::string> getFilesInDir(const char* dirPath);
|
|
||||||
|
|
||||||
bool stringCompare( const std::string &left, const std::string &right );
|
bool DirectoryExists( const char* pzPath );
|
||||||
|
bool fileExists( const char* pzPath );
|
||||||
|
std::vector<std::string> getFilesInDir(const char* dirPath);
|
||||||
|
|
||||||
|
bool stringCompare( const std::string &left, const std::string &right );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // FILESYSTEM_H
|
#endif // FILESYSTEM_H
|
||||||
|
@@ -1,16 +1,19 @@
|
|||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
void sleep_ms(int sleepMs)
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
void sleep_ms(int sleepMs)
|
||||||
|
{
|
||||||
#ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
Sleep(sleepMs);
|
Sleep(sleepMs);
|
||||||
#else
|
#else
|
||||||
usleep(sleepMs * 1000); // usleep takes sleep time in us (1 millionth of a second)
|
usleep(sleepMs * 1000); // usleep takes sleep time in us (1 millionth of a second)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getExeDir()
|
std::string getExeDir()
|
||||||
{
|
{
|
||||||
#ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
TCHAR szEXEPath[2048];
|
TCHAR szEXEPath[2048];
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
@@ -42,4 +45,6 @@ std::string getExeDir()
|
|||||||
}
|
}
|
||||||
return directory;
|
return directory;
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -10,11 +10,13 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace alpr
|
||||||
|
{
|
||||||
|
|
||||||
|
void sleep_ms(int sleepMs);
|
||||||
|
|
||||||
|
std::string getExeDir();
|
||||||
|
|
||||||
void sleep_ms(int sleepMs);
|
}
|
||||||
|
|
||||||
std::string getExeDir();
|
|
||||||
|
|
||||||
#endif //OPENALPR_PLATFORM_H
|
#endif //OPENALPR_PLATFORM_H
|
||||||
|
@@ -1,12 +1,15 @@
|
|||||||
#include "timing.h"
|
#include "timing.h"
|
||||||
|
|
||||||
timespec diff(timespec start, timespec end);
|
namespace alpr
|
||||||
|
|
||||||
#ifdef WINDOWS
|
|
||||||
|
|
||||||
// Windows timing code
|
|
||||||
LARGE_INTEGER getFILETIMEoffset()
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
timespec diff(timespec start, timespec end);
|
||||||
|
|
||||||
|
#ifdef WINDOWS
|
||||||
|
|
||||||
|
// Windows timing code
|
||||||
|
LARGE_INTEGER getFILETIMEoffset()
|
||||||
|
{
|
||||||
SYSTEMTIME s;
|
SYSTEMTIME s;
|
||||||
FILETIME f;
|
FILETIME f;
|
||||||
LARGE_INTEGER t;
|
LARGE_INTEGER t;
|
||||||
@@ -23,10 +26,10 @@ LARGE_INTEGER getFILETIMEoffset()
|
|||||||
t.QuadPart <<= 32;
|
t.QuadPart <<= 32;
|
||||||
t.QuadPart |= f.dwLowDateTime;
|
t.QuadPart |= f.dwLowDateTime;
|
||||||
return (t);
|
return (t);
|
||||||
}
|
}
|
||||||
|
|
||||||
int clock_gettime(int X, timespec *tv)
|
int clock_gettime(int X, timespec *tv)
|
||||||
{
|
{
|
||||||
LARGE_INTEGER t;
|
LARGE_INTEGER t;
|
||||||
FILETIME f;
|
FILETIME f;
|
||||||
double microseconds;
|
double microseconds;
|
||||||
@@ -66,23 +69,23 @@ int clock_gettime(int X, timespec *tv)
|
|||||||
tv->tv_sec = t.QuadPart / 1000000;
|
tv->tv_sec = t.QuadPart / 1000000;
|
||||||
tv->tv_usec = t.QuadPart % 1000000;
|
tv->tv_usec = t.QuadPart % 1000000;
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getTime(timespec* time)
|
void getTime(timespec* time)
|
||||||
{
|
{
|
||||||
clock_gettime(0, time);
|
clock_gettime(0, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
double diffclock(timespec time1,timespec time2)
|
double diffclock(timespec time1,timespec time2)
|
||||||
{
|
{
|
||||||
timespec delta = diff(time1,time2);
|
timespec delta = diff(time1,time2);
|
||||||
double milliseconds = (delta.tv_sec * 1000) + (((double) delta.tv_usec) / 1000.0);
|
double milliseconds = (delta.tv_sec * 1000) + (((double) delta.tv_usec) / 1000.0);
|
||||||
|
|
||||||
return milliseconds;
|
return milliseconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
timespec diff(timespec start, timespec end)
|
timespec diff(timespec start, timespec end)
|
||||||
{
|
{
|
||||||
timespec temp;
|
timespec temp;
|
||||||
if ((end.tv_usec-start.tv_usec)<0)
|
if ((end.tv_usec-start.tv_usec)<0)
|
||||||
{
|
{
|
||||||
@@ -95,19 +98,19 @@ timespec diff(timespec start, timespec end)
|
|||||||
temp.tv_usec = end.tv_usec-start.tv_usec;
|
temp.tv_usec = end.tv_usec-start.tv_usec;
|
||||||
}
|
}
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
long getEpochTime()
|
long getEpochTime()
|
||||||
{
|
{
|
||||||
return std::time(0) * 1000;
|
return std::time(0) * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
void getTime(timespec* time)
|
void getTime(timespec* time)
|
||||||
{
|
{
|
||||||
#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time
|
#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time
|
||||||
clock_serv_t cclock;
|
clock_serv_t cclock;
|
||||||
mach_timespec_t mts;
|
mach_timespec_t mts;
|
||||||
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
|
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
|
||||||
@@ -115,21 +118,21 @@ void getTime(timespec* time)
|
|||||||
mach_port_deallocate(mach_task_self(), cclock);
|
mach_port_deallocate(mach_task_self(), cclock);
|
||||||
time->tv_sec = mts.tv_sec;
|
time->tv_sec = mts.tv_sec;
|
||||||
time->tv_nsec = mts.tv_nsec;
|
time->tv_nsec = mts.tv_nsec;
|
||||||
#else
|
#else
|
||||||
clock_gettime(CLOCK_MONOTONIC, time);
|
clock_gettime(CLOCK_MONOTONIC, time);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
double diffclock(timespec time1,timespec time2)
|
double diffclock(timespec time1,timespec time2)
|
||||||
{
|
{
|
||||||
timespec delta = diff(time1,time2);
|
timespec delta = diff(time1,time2);
|
||||||
double milliseconds = (delta.tv_sec * 1000) + (((double) delta.tv_nsec) / 1000000.0);
|
double milliseconds = (delta.tv_sec * 1000) + (((double) delta.tv_nsec) / 1000000.0);
|
||||||
|
|
||||||
return milliseconds;
|
return milliseconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
timespec diff(timespec start, timespec end)
|
timespec diff(timespec start, timespec end)
|
||||||
{
|
{
|
||||||
timespec temp;
|
timespec temp;
|
||||||
if ((end.tv_nsec-start.tv_nsec)<0)
|
if ((end.tv_nsec-start.tv_nsec)<0)
|
||||||
{
|
{
|
||||||
@@ -142,18 +145,20 @@ timespec diff(timespec start, timespec end)
|
|||||||
temp.tv_nsec = end.tv_nsec-start.tv_nsec;
|
temp.tv_nsec = end.tv_nsec-start.tv_nsec;
|
||||||
}
|
}
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
long getEpochTime()
|
long getEpochTime()
|
||||||
{
|
{
|
||||||
struct timeval tp;
|
struct timeval tp;
|
||||||
gettimeofday(&tp, NULL);
|
gettimeofday(&tp, NULL);
|
||||||
long ms = tp.tv_sec * 1000 + tp.tv_usec / 1000;
|
long ms = tp.tv_sec * 1000 + tp.tv_usec / 1000;
|
||||||
|
|
||||||
return ms;
|
return ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -23,9 +23,14 @@
|
|||||||
#define timespec timeval
|
#define timespec timeval
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void getTime(timespec* time);
|
namespace alpr
|
||||||
double diffclock(timespec time1,timespec time2);
|
{
|
||||||
|
|
||||||
long getEpochTime();
|
void getTime(timespec* time);
|
||||||
|
double diffclock(timespec time1,timespec time2);
|
||||||
|
|
||||||
|
long getEpochTime();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -25,10 +25,13 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
bool sort_text_line(TextLine i, TextLine j) { return (i.topLine.p1.y < j.topLine.p1.y); }
|
namespace alpr
|
||||||
|
|
||||||
CharacterAnalysis::CharacterAnalysis(PipelineData* pipeline_data)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
bool sort_text_line(TextLine i, TextLine j) { return (i.topLine.p1.y < j.topLine.p1.y); }
|
||||||
|
|
||||||
|
CharacterAnalysis::CharacterAnalysis(PipelineData* pipeline_data)
|
||||||
|
{
|
||||||
this->pipeline_data = pipeline_data;
|
this->pipeline_data = pipeline_data;
|
||||||
this->config = pipeline_data->config;
|
this->config = pipeline_data->config;
|
||||||
|
|
||||||
@@ -38,15 +41,15 @@ CharacterAnalysis::CharacterAnalysis(PipelineData* pipeline_data)
|
|||||||
cout << "Starting CharacterAnalysis identification" << endl;
|
cout << "Starting CharacterAnalysis identification" << endl;
|
||||||
|
|
||||||
this->analyze();
|
this->analyze();
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterAnalysis::~CharacterAnalysis()
|
CharacterAnalysis::~CharacterAnalysis()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterAnalysis::analyze()
|
void CharacterAnalysis::analyze()
|
||||||
{
|
{
|
||||||
pipeline_data->clearThresholds();
|
pipeline_data->clearThresholds();
|
||||||
pipeline_data->thresholds = produceThresholds(pipeline_data->crop_gray, config);
|
pipeline_data->thresholds = produceThresholds(pipeline_data->crop_gray, config);
|
||||||
|
|
||||||
@@ -234,13 +237,13 @@ void CharacterAnalysis::analyze()
|
|||||||
tempDash.push_back(bestVal);
|
tempDash.push_back(bestVal);
|
||||||
displayImage(config, "Character Region Step 1 Thresholds", drawImageDashboard(tempDash, bestVal.type(), 3));
|
displayImage(config, "Character Region Step 1 Thresholds", drawImageDashboard(tempDash, bestVal.type(), 3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Mat CharacterAnalysis::getCharacterMask()
|
Mat CharacterAnalysis::getCharacterMask()
|
||||||
{
|
{
|
||||||
Mat charMask = Mat::zeros(bestThreshold.size(), CV_8U);
|
Mat charMask = Mat::zeros(bestThreshold.size(), CV_8U);
|
||||||
|
|
||||||
for (uint i = 0; i < bestContours.size(); i++)
|
for (uint i = 0; i < bestContours.size(); i++)
|
||||||
@@ -260,11 +263,11 @@ Mat CharacterAnalysis::getCharacterMask()
|
|||||||
}
|
}
|
||||||
|
|
||||||
return charMask;
|
return charMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CharacterAnalysis::filter(Mat img, TextContours& textContours)
|
void CharacterAnalysis::filter(Mat img, TextContours& textContours)
|
||||||
{
|
{
|
||||||
static int STARTING_MIN_HEIGHT = round (((float) img.rows) * config->charAnalysisMinPercent);
|
static int STARTING_MIN_HEIGHT = round (((float) img.rows) * config->charAnalysisMinPercent);
|
||||||
static int STARTING_MAX_HEIGHT = round (((float) img.rows) * (config->charAnalysisMinPercent + config->charAnalysisHeightRange));
|
static int STARTING_MAX_HEIGHT = round (((float) img.rows) * (config->charAnalysisMinPercent + config->charAnalysisHeightRange));
|
||||||
static int HEIGHT_STEP = round (((float) img.rows) * config->charAnalysisHeightStepSize);
|
static int HEIGHT_STEP = round (((float) img.rows) * config->charAnalysisHeightStepSize);
|
||||||
@@ -303,11 +306,11 @@ void CharacterAnalysis::filter(Mat img, TextContours& textContours)
|
|||||||
}
|
}
|
||||||
|
|
||||||
textContours.setIndices(bestIndices);
|
textContours.setIndices(bestIndices);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Goes through the contours for the plate and picks out possible char segments based on min/max height
|
// Goes through the contours for the plate and picks out possible char segments based on min/max height
|
||||||
void CharacterAnalysis::filterByBoxSize(TextContours& textContours, int minHeightPx, int maxHeightPx)
|
void CharacterAnalysis::filterByBoxSize(TextContours& textContours, int minHeightPx, int maxHeightPx)
|
||||||
{
|
{
|
||||||
float idealAspect=config->charWidthMM / config->charHeightMM;
|
float idealAspect=config->charWidthMM / config->charHeightMM;
|
||||||
float aspecttolerance=0.25;
|
float aspecttolerance=0.25;
|
||||||
|
|
||||||
@@ -336,10 +339,10 @@ void CharacterAnalysis::filterByBoxSize(TextContours& textContours, int minHeigh
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterAnalysis::filterContourHoles(TextContours& textContours)
|
void CharacterAnalysis::filterContourHoles(TextContours& textContours)
|
||||||
{
|
{
|
||||||
|
|
||||||
for (uint i = 0; i < textContours.size(); i++)
|
for (uint i = 0; i < textContours.size(); i++)
|
||||||
{
|
{
|
||||||
@@ -364,12 +367,12 @@ void CharacterAnalysis::filterContourHoles(TextContours& textContours)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Goes through the contours for the plate and picks out possible char segments based on min/max height
|
// Goes through the contours for the plate and picks out possible char segments based on min/max height
|
||||||
// returns a vector of indices corresponding to valid contours
|
// returns a vector of indices corresponding to valid contours
|
||||||
void CharacterAnalysis::filterByParentContour( TextContours& textContours)
|
void CharacterAnalysis::filterByParentContour( TextContours& textContours)
|
||||||
{
|
{
|
||||||
|
|
||||||
vector<int> parentIDs;
|
vector<int> parentIDs;
|
||||||
vector<int> votes;
|
vector<int> votes;
|
||||||
@@ -433,10 +436,10 @@ void CharacterAnalysis::filterByParentContour( TextContours& textContours)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterAnalysis::filterBetweenLines(Mat img, TextContours& textContours, vector<TextLine> textLines )
|
void CharacterAnalysis::filterBetweenLines(Mat img, TextContours& textContours, vector<TextLine> textLines )
|
||||||
{
|
{
|
||||||
static float MIN_AREA_PERCENT_WITHIN_LINES = 0.88;
|
static float MIN_AREA_PERCENT_WITHIN_LINES = 0.88;
|
||||||
static float MAX_DISTANCE_PERCENT_FROM_LINES = 0.15;
|
static float MAX_DISTANCE_PERCENT_FROM_LINES = 0.15;
|
||||||
|
|
||||||
@@ -512,10 +515,10 @@ void CharacterAnalysis::filterBetweenLines(Mat img, TextContours& textContours,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterAnalysis::filterByOuterMask(TextContours& textContours)
|
void CharacterAnalysis::filterByOuterMask(TextContours& textContours)
|
||||||
{
|
{
|
||||||
float MINIMUM_PERCENT_LEFT_AFTER_MASK = 0.1;
|
float MINIMUM_PERCENT_LEFT_AFTER_MASK = 0.1;
|
||||||
float MINIMUM_PERCENT_OF_CHARS_INSIDE_PLATE_MASK = 0.6;
|
float MINIMUM_PERCENT_OF_CHARS_INSIDE_PLATE_MASK = 0.6;
|
||||||
|
|
||||||
@@ -570,10 +573,10 @@ void CharacterAnalysis::filterByOuterMask(TextContours& textContours)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterAnalysis::isPlateInverted()
|
bool CharacterAnalysis::isPlateInverted()
|
||||||
{
|
{
|
||||||
Mat charMask = getCharacterMask();
|
Mat charMask = getCharacterMask();
|
||||||
|
|
||||||
|
|
||||||
@@ -586,10 +589,10 @@ bool CharacterAnalysis::isPlateInverted()
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterAnalysis::verifySize(Mat r, float minHeightPx, float maxHeightPx)
|
bool CharacterAnalysis::verifySize(Mat r, float minHeightPx, float maxHeightPx)
|
||||||
{
|
{
|
||||||
//Char sizes 45x90
|
//Char sizes 45x90
|
||||||
float aspect=config->charWidthMM / config->charHeightMM;
|
float aspect=config->charWidthMM / config->charHeightMM;
|
||||||
float charAspect= (float)r.cols/(float)r.rows;
|
float charAspect= (float)r.cols/(float)r.rows;
|
||||||
@@ -612,10 +615,10 @@ bool CharacterAnalysis::verifySize(Mat r, float minHeightPx, float maxHeightPx)
|
|||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<Point> CharacterAnalysis::getCharArea(LineSegment topLine, LineSegment bottomLine)
|
vector<Point> CharacterAnalysis::getCharArea(LineSegment topLine, LineSegment bottomLine)
|
||||||
{
|
{
|
||||||
const int MAX = 100000;
|
const int MAX = 100000;
|
||||||
const int MIN= -1;
|
const int MIN= -1;
|
||||||
|
|
||||||
@@ -650,4 +653,6 @@ vector<Point> CharacterAnalysis::getCharArea(LineSegment topLine, LineSegment bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
return charArea;
|
return charArea;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -29,9 +29,12 @@
|
|||||||
#include "platemask.h"
|
#include "platemask.h"
|
||||||
#include "linefinder.h"
|
#include "linefinder.h"
|
||||||
|
|
||||||
class CharacterAnalysis
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class CharacterAnalysis
|
||||||
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CharacterAnalysis(PipelineData* pipeline_data);
|
CharacterAnalysis(PipelineData* pipeline_data);
|
||||||
virtual ~CharacterAnalysis();
|
virtual ~CharacterAnalysis();
|
||||||
@@ -69,6 +72,8 @@ class CharacterAnalysis
|
|||||||
bool verifySize(cv::Mat r, float minHeightPx, float maxHeightPx);
|
bool verifySize(cv::Mat r, float minHeightPx, float maxHeightPx);
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // OPENALPR_CHARACTERANALYSIS_H
|
#endif // OPENALPR_CHARACTERANALYSIS_H
|
||||||
|
@@ -26,15 +26,18 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
LineFinder::LineFinder(PipelineData* pipeline_data) {
|
namespace alpr
|
||||||
this->pipeline_data = pipeline_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
LineFinder::~LineFinder() {
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<vector<Point> > LineFinder::findLines(Mat image, const TextContours contours)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
LineFinder::LineFinder(PipelineData* pipeline_data) {
|
||||||
|
this->pipeline_data = pipeline_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
LineFinder::~LineFinder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<vector<Point> > LineFinder::findLines(Mat image, const TextContours contours)
|
||||||
|
{
|
||||||
const float MIN_AREA_TO_IGNORE = 0.65;
|
const float MIN_AREA_TO_IGNORE = 0.65;
|
||||||
|
|
||||||
vector<vector<Point> > linesFound;
|
vector<vector<Point> > linesFound;
|
||||||
@@ -83,12 +86,12 @@ vector<vector<Point> > LineFinder::findLines(Mat image, const TextContours conto
|
|||||||
|
|
||||||
|
|
||||||
return linesFound;
|
return linesFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Returns a polygon "stripe" across the width of the character region. The lines are voted and the polygon starts at 0 and extends to image width
|
// Returns a polygon "stripe" across the width of the character region. The lines are voted and the polygon starts at 0 and extends to image width
|
||||||
vector<Point> LineFinder::getBestLine(const TextContours contours, vector<CharPointInfo> charPoints)
|
vector<Point> LineFinder::getBestLine(const TextContours contours, vector<CharPointInfo> charPoints)
|
||||||
{
|
{
|
||||||
vector<Point> bestStripe;
|
vector<Point> bestStripe;
|
||||||
|
|
||||||
// Find the best fit line segment that is parallel with the most char segments
|
// Find the best fit line segment that is parallel with the most char segments
|
||||||
@@ -132,12 +135,12 @@ vector<Point> LineFinder::getBestLine(const TextContours contours, vector<CharPo
|
|||||||
|
|
||||||
|
|
||||||
// Only allow lines that have a sane angle
|
// Only allow lines that have a sane angle
|
||||||
// if (abs(top.angle) <= pipeline_data->config->maxPlateAngleDegrees &&
|
// if (abs(top.angle) <= pipeline_data->config->maxPlateAngleDegrees &&
|
||||||
// abs(bottom.angle) <= pipeline_data->config->maxPlateAngleDegrees)
|
// abs(bottom.angle) <= pipeline_data->config->maxPlateAngleDegrees)
|
||||||
// {
|
// {
|
||||||
// topLines.push_back(top);
|
// topLines.push_back(top);
|
||||||
// bottomLines.push_back(bottom);
|
// bottomLines.push_back(bottom);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
LineSegment parallelBot = top.getParallelLine(medianCharHeight * -1);
|
LineSegment parallelBot = top.getParallelLine(medianCharHeight * -1);
|
||||||
LineSegment parallelTop = bottom.getParallelLine(medianCharHeight);
|
LineSegment parallelTop = bottom.getParallelLine(medianCharHeight);
|
||||||
@@ -230,9 +233,9 @@ vector<Point> LineFinder::getBestLine(const TextContours contours, vector<CharPo
|
|||||||
|
|
||||||
|
|
||||||
return bestStripe;
|
return bestStripe;
|
||||||
}
|
}
|
||||||
|
|
||||||
CharPointInfo::CharPointInfo(vector<Point> contour, int index) {
|
CharPointInfo::CharPointInfo(vector<Point> contour, int index) {
|
||||||
|
|
||||||
|
|
||||||
this->contourIndex = index;
|
this->contourIndex = index;
|
||||||
@@ -250,4 +253,6 @@ CharPointInfo::CharPointInfo(vector<Point> contour, int index) {
|
|||||||
|
|
||||||
this->bottom = Point(x,y);
|
this->bottom = Point(x,y);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -27,9 +27,12 @@
|
|||||||
#include "textline.h"
|
#include "textline.h"
|
||||||
#include "pipeline_data.h"
|
#include "pipeline_data.h"
|
||||||
|
|
||||||
class CharPointInfo
|
namespace alpr
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
|
class CharPointInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
CharPointInfo(std::vector<cv::Point> contour, int index);
|
CharPointInfo(std::vector<cv::Point> contour, int index);
|
||||||
|
|
||||||
cv::Rect boundingBox;
|
cv::Rect boundingBox;
|
||||||
@@ -37,19 +40,21 @@ public:
|
|||||||
cv::Point bottom;
|
cv::Point bottom;
|
||||||
int contourIndex;
|
int contourIndex;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class LineFinder {
|
class LineFinder {
|
||||||
public:
|
public:
|
||||||
LineFinder(PipelineData* pipeline_data);
|
LineFinder(PipelineData* pipeline_data);
|
||||||
virtual ~LineFinder();
|
virtual ~LineFinder();
|
||||||
|
|
||||||
std::vector<std::vector<cv::Point> > findLines(cv::Mat image, const TextContours contours);
|
std::vector<std::vector<cv::Point> > findLines(cv::Mat image, const TextContours contours);
|
||||||
private:
|
private:
|
||||||
PipelineData* pipeline_data;
|
PipelineData* pipeline_data;
|
||||||
|
|
||||||
std::vector<cv::Point> getBestLine(const TextContours contours, std::vector<CharPointInfo> charPoints);
|
std::vector<cv::Point> getBestLine(const TextContours contours, std::vector<CharPointInfo> charPoints);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* OPENALPR_LINEFINDER_H */
|
#endif /* OPENALPR_LINEFINDER_H */
|
||||||
|
|
||||||
|
@@ -22,22 +22,25 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
PlateMask::PlateMask(PipelineData* pipeline_data) {
|
namespace alpr
|
||||||
|
{
|
||||||
|
|
||||||
|
PlateMask::PlateMask(PipelineData* pipeline_data) {
|
||||||
this->pipeline_data = pipeline_data;
|
this->pipeline_data = pipeline_data;
|
||||||
this->hasPlateMask = false;
|
this->hasPlateMask = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PlateMask::~PlateMask() {
|
PlateMask::~PlateMask() {
|
||||||
}
|
}
|
||||||
|
|
||||||
cv::Mat PlateMask::getMask() {
|
cv::Mat PlateMask::getMask() {
|
||||||
return this->plateMask;
|
return this->plateMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlateMask::findOuterBoxMask( vector<TextContours > contours )
|
void PlateMask::findOuterBoxMask( vector<TextContours > contours )
|
||||||
{
|
{
|
||||||
double min_parent_area = pipeline_data->config->templateHeightPx * pipeline_data->config->templateWidthPx * 0.10; // Needs to be at least 10% of the plate area to be considered.
|
double min_parent_area = pipeline_data->config->templateHeightPx * pipeline_data->config->templateWidthPx * 0.10; // Needs to be at least 10% of the plate area to be considered.
|
||||||
|
|
||||||
int winningIndex = -1;
|
int winningIndex = -1;
|
||||||
@@ -196,4 +199,6 @@ void PlateMask::findOuterBoxMask( vector<TextContours > contours )
|
|||||||
bitwise_not(fullMask, fullMask);
|
bitwise_not(fullMask, fullMask);
|
||||||
|
|
||||||
this->plateMask = fullMask;
|
this->plateMask = fullMask;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -24,8 +24,11 @@
|
|||||||
#include "pipeline_data.h"
|
#include "pipeline_data.h"
|
||||||
#include "textcontours.h"
|
#include "textcontours.h"
|
||||||
|
|
||||||
class PlateMask {
|
namespace alpr
|
||||||
public:
|
{
|
||||||
|
|
||||||
|
class PlateMask {
|
||||||
|
public:
|
||||||
PlateMask(PipelineData* pipeline_data);
|
PlateMask(PipelineData* pipeline_data);
|
||||||
virtual ~PlateMask();
|
virtual ~PlateMask();
|
||||||
|
|
||||||
@@ -35,13 +38,14 @@ public:
|
|||||||
|
|
||||||
void findOuterBoxMask(std::vector<TextContours > contours);
|
void findOuterBoxMask(std::vector<TextContours > contours);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
PipelineData* pipeline_data;
|
PipelineData* pipeline_data;
|
||||||
cv::Mat plateMask;
|
cv::Mat plateMask;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
#endif /* OPENALPR_PLATEMASK_H */
|
#endif /* OPENALPR_PLATEMASK_H */
|
||||||
|
|
||||||
|
@@ -22,23 +22,25 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
|
namespace alpr
|
||||||
|
{
|
||||||
|
|
||||||
TextContours::TextContours() {
|
TextContours::TextContours() {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TextContours::TextContours(cv::Mat threshold) {
|
TextContours::TextContours(cv::Mat threshold) {
|
||||||
|
|
||||||
load(threshold);
|
load(threshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TextContours::~TextContours() {
|
TextContours::~TextContours() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextContours::load(cv::Mat threshold) {
|
void TextContours::load(cv::Mat threshold) {
|
||||||
|
|
||||||
Mat tempThreshold(threshold.size(), CV_8U);
|
Mat tempThreshold(threshold.size(), CV_8U);
|
||||||
threshold.copyTo(tempThreshold);
|
threshold.copyTo(tempThreshold);
|
||||||
@@ -53,17 +55,17 @@ void TextContours::load(cv::Mat threshold) {
|
|||||||
|
|
||||||
this->width = threshold.cols;
|
this->width = threshold.cols;
|
||||||
this->height = threshold.rows;
|
this->height = threshold.rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint TextContours::size() {
|
uint TextContours::size() {
|
||||||
return contours.size();
|
return contours.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int TextContours::getGoodIndicesCount()
|
int TextContours::getGoodIndicesCount()
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (uint i = 0; i < goodIndices.size(); i++)
|
for (uint i = 0; i < goodIndices.size(); i++)
|
||||||
{
|
{
|
||||||
@@ -72,11 +74,11 @@ int TextContours::getGoodIndicesCount()
|
|||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<bool> TextContours::getIndicesCopy()
|
std::vector<bool> TextContours::getIndicesCopy()
|
||||||
{
|
{
|
||||||
vector<bool> copyArray;
|
vector<bool> copyArray;
|
||||||
for (uint i = 0; i < goodIndices.size(); i++)
|
for (uint i = 0; i < goodIndices.size(); i++)
|
||||||
{
|
{
|
||||||
@@ -85,10 +87,10 @@ std::vector<bool> TextContours::getIndicesCopy()
|
|||||||
}
|
}
|
||||||
|
|
||||||
return copyArray;
|
return copyArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextContours::setIndices(std::vector<bool> newIndices)
|
void TextContours::setIndices(std::vector<bool> newIndices)
|
||||||
{
|
{
|
||||||
if (newIndices.size() == goodIndices.size())
|
if (newIndices.size() == goodIndices.size())
|
||||||
{
|
{
|
||||||
for (uint i = 0; i < newIndices.size(); i++)
|
for (uint i = 0; i < newIndices.size(); i++)
|
||||||
@@ -98,16 +100,16 @@ void TextContours::setIndices(std::vector<bool> newIndices)
|
|||||||
{
|
{
|
||||||
assert("Invalid set operation on indices");
|
assert("Invalid set operation on indices");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat TextContours::drawDebugImage() {
|
Mat TextContours::drawDebugImage() {
|
||||||
|
|
||||||
Mat img_contours = Mat::zeros(Size(width, height), CV_8U);
|
Mat img_contours = Mat::zeros(Size(width, height), CV_8U);
|
||||||
|
|
||||||
return drawDebugImage(img_contours);
|
return drawDebugImage(img_contours);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat TextContours::drawDebugImage(Mat baseImage) {
|
Mat TextContours::drawDebugImage(Mat baseImage) {
|
||||||
Mat img_contours(baseImage.size(), CV_8U);
|
Mat img_contours(baseImage.size(), CV_8U);
|
||||||
baseImage.copyTo(img_contours);
|
baseImage.copyTo(img_contours);
|
||||||
|
|
||||||
@@ -132,7 +134,7 @@ Mat TextContours::drawDebugImage(Mat baseImage) {
|
|||||||
|
|
||||||
|
|
||||||
return img_contours;
|
return img_contours;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -23,8 +23,11 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include "opencv2/imgproc/imgproc.hpp"
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
|
|
||||||
class TextContours {
|
namespace alpr
|
||||||
public:
|
{
|
||||||
|
|
||||||
|
class TextContours {
|
||||||
|
public:
|
||||||
TextContours();
|
TextContours();
|
||||||
TextContours(cv::Mat threshold);
|
TextContours(cv::Mat threshold);
|
||||||
virtual ~TextContours();
|
virtual ~TextContours();
|
||||||
@@ -47,10 +50,12 @@ public:
|
|||||||
cv::Mat drawDebugImage();
|
cv::Mat drawDebugImage();
|
||||||
cv::Mat drawDebugImage(cv::Mat baseImage);
|
cv::Mat drawDebugImage(cv::Mat baseImage);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* TEXTCONTOURS_H */
|
#endif /* TEXTCONTOURS_H */
|
||||||
|
|
||||||
|
@@ -24,7 +24,10 @@
|
|||||||
|
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
TextLine::TextLine(std::vector<cv::Point2f> textArea, std::vector<cv::Point2f> linePolygon) {
|
namespace alpr
|
||||||
|
{
|
||||||
|
|
||||||
|
TextLine::TextLine(std::vector<cv::Point2f> textArea, std::vector<cv::Point2f> linePolygon) {
|
||||||
std::vector<Point> textAreaInts, linePolygonInts;
|
std::vector<Point> textAreaInts, linePolygonInts;
|
||||||
|
|
||||||
for (uint i = 0; i < textArea.size(); i++)
|
for (uint i = 0; i < textArea.size(); i++)
|
||||||
@@ -33,19 +36,19 @@ TextLine::TextLine(std::vector<cv::Point2f> textArea, std::vector<cv::Point2f> l
|
|||||||
linePolygonInts.push_back(Point(round(linePolygon[i].x), round(linePolygon[i].y)));
|
linePolygonInts.push_back(Point(round(linePolygon[i].x), round(linePolygon[i].y)));
|
||||||
|
|
||||||
initialize(textAreaInts, linePolygonInts);
|
initialize(textAreaInts, linePolygonInts);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextLine::TextLine(std::vector<cv::Point> textArea, std::vector<cv::Point> linePolygon) {
|
TextLine::TextLine(std::vector<cv::Point> textArea, std::vector<cv::Point> linePolygon) {
|
||||||
initialize(textArea, linePolygon);
|
initialize(textArea, linePolygon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TextLine::~TextLine() {
|
TextLine::~TextLine() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void TextLine::initialize(std::vector<cv::Point> textArea, std::vector<cv::Point> linePolygon) {
|
void TextLine::initialize(std::vector<cv::Point> textArea, std::vector<cv::Point> linePolygon) {
|
||||||
if (textArea.size() > 0)
|
if (textArea.size() > 0)
|
||||||
{
|
{
|
||||||
if (this->textArea.size() > 0)
|
if (this->textArea.size() > 0)
|
||||||
@@ -79,10 +82,10 @@ void TextLine::initialize(std::vector<cv::Point> textArea, std::vector<cv::Point
|
|||||||
this->angle = (topLine.angle + bottomLine.angle) / 2;
|
this->angle = (topLine.angle + bottomLine.angle) / 2;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
cv::Mat TextLine::drawDebugImage(cv::Mat baseImage) {
|
cv::Mat TextLine::drawDebugImage(cv::Mat baseImage) {
|
||||||
cv::Mat debugImage(baseImage.size(), baseImage.type());
|
cv::Mat debugImage(baseImage.size(), baseImage.type());
|
||||||
|
|
||||||
baseImage.copyTo(debugImage);
|
baseImage.copyTo(debugImage);
|
||||||
@@ -104,4 +107,6 @@ cv::Mat TextLine::drawDebugImage(cv::Mat baseImage) {
|
|||||||
|
|
||||||
|
|
||||||
return debugImage;
|
return debugImage;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -24,8 +24,11 @@
|
|||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
#include "opencv2/imgproc/imgproc.hpp"
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
|
|
||||||
class TextLine {
|
namespace alpr
|
||||||
public:
|
{
|
||||||
|
|
||||||
|
class TextLine {
|
||||||
|
public:
|
||||||
TextLine(std::vector<cv::Point> textArea, std::vector<cv::Point> linePolygon);
|
TextLine(std::vector<cv::Point> textArea, std::vector<cv::Point> linePolygon);
|
||||||
TextLine(std::vector<cv::Point2f> textArea, std::vector<cv::Point2f> linePolygon);
|
TextLine(std::vector<cv::Point2f> textArea, std::vector<cv::Point2f> linePolygon);
|
||||||
virtual ~TextLine();
|
virtual ~TextLine();
|
||||||
@@ -45,10 +48,12 @@ public:
|
|||||||
float angle;
|
float angle;
|
||||||
|
|
||||||
cv::Mat drawDebugImage(cv::Mat baseImage);
|
cv::Mat drawDebugImage(cv::Mat baseImage);
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void initialize(std::vector<cv::Point> textArea, std::vector<cv::Point> linePolygon);
|
void initialize(std::vector<cv::Point> textArea, std::vector<cv::Point> linePolygon);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* OPENALPR_TEXTLINE_H */
|
#endif /* OPENALPR_TEXTLINE_H */
|
||||||
|
|
||||||
|
@@ -22,30 +22,33 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
Transformation::Transformation(Mat bigImage, Mat smallImage, Rect regionInBigImage) {
|
namespace alpr
|
||||||
|
{
|
||||||
|
|
||||||
|
Transformation::Transformation(Mat bigImage, Mat smallImage, Rect regionInBigImage) {
|
||||||
this->bigImage = bigImage;
|
this->bigImage = bigImage;
|
||||||
this->smallImage = smallImage;
|
this->smallImage = smallImage;
|
||||||
this->regionInBigImage = regionInBigImage;
|
this->regionInBigImage = regionInBigImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Transformation::~Transformation() {
|
Transformation::~Transformation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-maps the coordinates from the smallImage to the coordinate space of the bigImage.
|
// Re-maps the coordinates from the smallImage to the coordinate space of the bigImage.
|
||||||
vector<Point2f> Transformation::transformSmallPointsToBigImage(vector<Point> points)
|
vector<Point2f> Transformation::transformSmallPointsToBigImage(vector<Point> points)
|
||||||
{
|
{
|
||||||
vector<Point2f> floatPoints;
|
vector<Point2f> floatPoints;
|
||||||
for (unsigned int i = 0; i < points.size(); i++)
|
for (unsigned int i = 0; i < points.size(); i++)
|
||||||
floatPoints.push_back(points[i]);
|
floatPoints.push_back(points[i]);
|
||||||
|
|
||||||
return transformSmallPointsToBigImage(floatPoints);
|
return transformSmallPointsToBigImage(floatPoints);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-maps the coordinates from the smallImage to the coordinate space of the bigImage.
|
// Re-maps the coordinates from the smallImage to the coordinate space of the bigImage.
|
||||||
vector<Point2f> Transformation::transformSmallPointsToBigImage(vector<Point2f> points)
|
vector<Point2f> Transformation::transformSmallPointsToBigImage(vector<Point2f> points)
|
||||||
{
|
{
|
||||||
vector<Point2f> bigPoints;
|
vector<Point2f> bigPoints;
|
||||||
for (uint i = 0; i < points.size(); i++)
|
for (uint i = 0; i < points.size(); i++)
|
||||||
{
|
{
|
||||||
@@ -59,11 +62,11 @@ vector<Point2f> Transformation::transformSmallPointsToBigImage(vector<Point2f> p
|
|||||||
}
|
}
|
||||||
|
|
||||||
return bigPoints;
|
return bigPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Mat Transformation::getTransformationMatrix(vector<Point2f> corners, Size outputImageSize)
|
Mat Transformation::getTransformationMatrix(vector<Point2f> corners, Size outputImageSize)
|
||||||
{
|
{
|
||||||
// Corners of the destination image
|
// Corners of the destination image
|
||||||
vector<Point2f> quad_pts;
|
vector<Point2f> quad_pts;
|
||||||
quad_pts.push_back(Point2f(0, 0));
|
quad_pts.push_back(Point2f(0, 0));
|
||||||
@@ -72,20 +75,20 @@ Mat Transformation::getTransformationMatrix(vector<Point2f> corners, Size output
|
|||||||
quad_pts.push_back(Point2f(0, outputImageSize.height));
|
quad_pts.push_back(Point2f(0, outputImageSize.height));
|
||||||
|
|
||||||
return getTransformationMatrix(corners, quad_pts);
|
return getTransformationMatrix(corners, quad_pts);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat Transformation::getTransformationMatrix(vector<Point2f> corners, vector<Point2f> outputCorners)
|
Mat Transformation::getTransformationMatrix(vector<Point2f> corners, vector<Point2f> outputCorners)
|
||||||
{
|
{
|
||||||
|
|
||||||
// Get transformation matrix
|
// Get transformation matrix
|
||||||
Mat transmtx = getPerspectiveTransform(corners, outputCorners);
|
Mat transmtx = getPerspectiveTransform(corners, outputCorners);
|
||||||
|
|
||||||
return transmtx;
|
return transmtx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Mat Transformation::crop(Size outputImageSize, Mat transformationMatrix)
|
Mat Transformation::crop(Size outputImageSize, Mat transformationMatrix)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
Mat deskewed(outputImageSize, this->bigImage.type());
|
Mat deskewed(outputImageSize, this->bigImage.type());
|
||||||
@@ -97,27 +100,27 @@ Mat Transformation::crop(Size outputImageSize, Mat transformationMatrix)
|
|||||||
|
|
||||||
|
|
||||||
return deskewed;
|
return deskewed;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<Point2f> Transformation::remapSmallPointstoCrop(vector<Point> smallPoints, cv::Mat transformationMatrix)
|
vector<Point2f> Transformation::remapSmallPointstoCrop(vector<Point> smallPoints, cv::Mat transformationMatrix)
|
||||||
{
|
{
|
||||||
vector<Point2f> floatPoints;
|
vector<Point2f> floatPoints;
|
||||||
for (unsigned int i = 0; i < smallPoints.size(); i++)
|
for (unsigned int i = 0; i < smallPoints.size(); i++)
|
||||||
floatPoints.push_back(smallPoints[i]);
|
floatPoints.push_back(smallPoints[i]);
|
||||||
|
|
||||||
return remapSmallPointstoCrop(floatPoints, transformationMatrix);
|
return remapSmallPointstoCrop(floatPoints, transformationMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<Point2f> Transformation::remapSmallPointstoCrop(vector<Point2f> smallPoints, cv::Mat transformationMatrix)
|
vector<Point2f> Transformation::remapSmallPointstoCrop(vector<Point2f> smallPoints, cv::Mat transformationMatrix)
|
||||||
{
|
{
|
||||||
vector<Point2f> remappedPoints;
|
vector<Point2f> remappedPoints;
|
||||||
perspectiveTransform(smallPoints, remappedPoints, transformationMatrix);
|
perspectiveTransform(smallPoints, remappedPoints, transformationMatrix);
|
||||||
|
|
||||||
return remappedPoints;
|
return remappedPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
Size Transformation::getCropSize(vector<Point2f> areaCorners, Size targetSize)
|
Size Transformation::getCropSize(vector<Point2f> areaCorners, Size targetSize)
|
||||||
{
|
{
|
||||||
// Figure out the approximate width/height of the license plate region, so we can maintain the aspect ratio.
|
// Figure out the approximate width/height of the license plate region, so we can maintain the aspect ratio.
|
||||||
LineSegment leftEdge(round(areaCorners[3].x), round(areaCorners[3].y), round(areaCorners[0].x), round(areaCorners[0].y));
|
LineSegment leftEdge(round(areaCorners[3].x), round(areaCorners[3].y), round(areaCorners[0].x), round(areaCorners[0].y));
|
||||||
LineSegment rightEdge(round(areaCorners[2].x), round(areaCorners[2].y), round(areaCorners[1].x), round(areaCorners[1].y));
|
LineSegment rightEdge(round(areaCorners[2].x), round(areaCorners[2].y), round(areaCorners[1].x), round(areaCorners[1].y));
|
||||||
@@ -136,4 +139,6 @@ Size Transformation::getCropSize(vector<Point2f> areaCorners, Size targetSize)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Size(width, height);
|
return Size(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -23,8 +23,11 @@
|
|||||||
#include "opencv2/imgproc/imgproc.hpp"
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
|
|
||||||
class Transformation {
|
namespace alpr
|
||||||
public:
|
{
|
||||||
|
|
||||||
|
class Transformation {
|
||||||
|
public:
|
||||||
Transformation(cv::Mat bigImage, cv::Mat smallImage, cv::Rect regionInBigImage);
|
Transformation(cv::Mat bigImage, cv::Mat smallImage, cv::Rect regionInBigImage);
|
||||||
virtual ~Transformation();
|
virtual ~Transformation();
|
||||||
|
|
||||||
@@ -40,12 +43,14 @@ public:
|
|||||||
|
|
||||||
cv::Size getCropSize(std::vector<cv::Point2f> areaCorners, cv::Size targetSize);
|
cv::Size getCropSize(std::vector<cv::Point2f> areaCorners, cv::Size targetSize);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
cv::Mat bigImage;
|
cv::Mat bigImage;
|
||||||
cv::Mat smallImage;
|
cv::Mat smallImage;
|
||||||
cv::Rect regionInBigImage;
|
cv::Rect regionInBigImage;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* OPENALPR_TRANSFORMATION_H */
|
#endif /* OPENALPR_TRANSFORMATION_H */
|
||||||
|
|
||||||
|
@@ -24,8 +24,11 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
Rect expandRect(Rect original, int expandXPixels, int expandYPixels, int maxX, int maxY)
|
namespace alpr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
Rect expandRect(Rect original, int expandXPixels, int expandYPixels, int maxX, int maxY)
|
||||||
|
{
|
||||||
Rect expandedRegion = Rect(original);
|
Rect expandedRegion = Rect(original);
|
||||||
|
|
||||||
float halfX = round((float) expandXPixels / 2.0);
|
float halfX = round((float) expandXPixels / 2.0);
|
||||||
@@ -45,10 +48,10 @@ Rect expandRect(Rect original, int expandXPixels, int expandYPixels, int maxX, i
|
|||||||
expandedRegion.height = maxY - expandedRegion.y;
|
expandedRegion.height = maxY - expandedRegion.y;
|
||||||
|
|
||||||
return expandedRegion;
|
return expandedRegion;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat drawImageDashboard(vector<Mat> images, int imageType, uint numColumns)
|
Mat drawImageDashboard(vector<Mat> images, int imageType, uint numColumns)
|
||||||
{
|
{
|
||||||
uint numRows = ceil((float) images.size() / (float) numColumns);
|
uint numRows = ceil((float) images.size() / (float) numColumns);
|
||||||
|
|
||||||
Mat dashboard(Size(images[0].cols * numColumns, images[0].rows * numRows), imageType);
|
Mat dashboard(Size(images[0].cols * numColumns, images[0].rows * numRows), imageType);
|
||||||
@@ -65,10 +68,10 @@ Mat drawImageDashboard(vector<Mat> images, int imageType, uint numColumns)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return dashboard;
|
return dashboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat addLabel(Mat input, string label)
|
Mat addLabel(Mat input, string label)
|
||||||
{
|
{
|
||||||
const int border_size = 1;
|
const int border_size = 1;
|
||||||
const Scalar border_color(0,0,255);
|
const Scalar border_color(0,0,255);
|
||||||
const int extraHeight = 20;
|
const int extraHeight = 20;
|
||||||
@@ -89,10 +92,10 @@ Mat addLabel(Mat input, string label)
|
|||||||
rectangle(newImage, Point(0,0), Point(newImage.cols - 1, newImage.rows -1), border_color, border_size);
|
rectangle(newImage, Point(0,0), Point(newImage.cols - 1, newImage.rows -1), border_color, border_size);
|
||||||
|
|
||||||
return newImage;
|
return newImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawAndWait(cv::Mat* frame)
|
void drawAndWait(cv::Mat* frame)
|
||||||
{
|
{
|
||||||
cv::imshow("Temp Window", *frame);
|
cv::imshow("Temp Window", *frame);
|
||||||
|
|
||||||
while (cv::waitKey(50) == -1)
|
while (cv::waitKey(50) == -1)
|
||||||
@@ -101,19 +104,19 @@ void drawAndWait(cv::Mat* frame)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cv::destroyWindow("Temp Window");
|
cv::destroyWindow("Temp Window");
|
||||||
}
|
}
|
||||||
|
|
||||||
void displayImage(Config* config, string windowName, cv::Mat frame)
|
void displayImage(Config* config, string windowName, cv::Mat frame)
|
||||||
{
|
{
|
||||||
if (config->debugShowImages)
|
if (config->debugShowImages)
|
||||||
{
|
{
|
||||||
imshow(windowName, frame);
|
imshow(windowName, frame);
|
||||||
cv::waitKey(5);
|
cv::waitKey(5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<Mat> produceThresholds(const Mat img_gray, Config* config)
|
vector<Mat> produceThresholds(const Mat img_gray, Config* config)
|
||||||
{
|
{
|
||||||
const int THRESHOLD_COUNT = 3;
|
const int THRESHOLD_COUNT = 3;
|
||||||
//Mat img_equalized = equalizeBrightness(img_gray);
|
//Mat img_equalized = equalizeBrightness(img_gray);
|
||||||
|
|
||||||
@@ -163,10 +166,10 @@ vector<Mat> produceThresholds(const Mat img_gray, Config* config)
|
|||||||
|
|
||||||
return thresholds;
|
return thresholds;
|
||||||
//threshold(img_equalized, img_threshold, 100, 255, THRESH_BINARY);
|
//threshold(img_equalized, img_threshold, 100, 255, THRESH_BINARY);
|
||||||
}
|
}
|
||||||
|
|
||||||
double median(int array[], int arraySize)
|
double median(int array[], int arraySize)
|
||||||
{
|
{
|
||||||
if (arraySize == 0)
|
if (arraySize == 0)
|
||||||
{
|
{
|
||||||
//std::cerr << "Median calculation requested on empty array" << endl;
|
//std::cerr << "Median calculation requested on empty array" << endl;
|
||||||
@@ -175,10 +178,10 @@ double median(int array[], int arraySize)
|
|||||||
|
|
||||||
std::sort(&array[0], &array[arraySize]);
|
std::sort(&array[0], &array[arraySize]);
|
||||||
return arraySize % 2 ? array[arraySize / 2] : (array[arraySize / 2 - 1] + array[arraySize / 2]) / 2;
|
return arraySize % 2 ? array[arraySize / 2] : (array[arraySize / 2 - 1] + array[arraySize / 2]) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat equalizeBrightness(Mat img)
|
Mat equalizeBrightness(Mat img)
|
||||||
{
|
{
|
||||||
// Divide the image by its morphologically closed counterpart
|
// Divide the image by its morphologically closed counterpart
|
||||||
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(19,19));
|
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(19,19));
|
||||||
Mat closed;
|
Mat closed;
|
||||||
@@ -190,18 +193,18 @@ Mat equalizeBrightness(Mat img)
|
|||||||
img.convertTo(img, CV_8U); // convert back to unsigned int
|
img.convertTo(img, CV_8U); // convert back to unsigned int
|
||||||
|
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawRotatedRect(Mat* img, RotatedRect rect, Scalar color, int thickness)
|
void drawRotatedRect(Mat* img, RotatedRect rect, Scalar color, int thickness)
|
||||||
{
|
{
|
||||||
Point2f rect_points[4];
|
Point2f rect_points[4];
|
||||||
rect.points( rect_points );
|
rect.points( rect_points );
|
||||||
for( int j = 0; j < 4; j++ )
|
for( int j = 0; j < 4; j++ )
|
||||||
line( *img, rect_points[j], rect_points[(j+1)%4], color, thickness, 8 );
|
line( *img, rect_points[j], rect_points[(j+1)%4], color, thickness, 8 );
|
||||||
}
|
}
|
||||||
|
|
||||||
void fillMask(Mat img, const Mat mask, Scalar color)
|
void fillMask(Mat img, const Mat mask, Scalar color)
|
||||||
{
|
{
|
||||||
for (int row = 0; row < img.rows; row++)
|
for (int row = 0; row < img.rows; row++)
|
||||||
{
|
{
|
||||||
for (int col = 0; col < img.cols; col++)
|
for (int col = 0; col < img.cols; col++)
|
||||||
@@ -218,10 +221,10 @@ void fillMask(Mat img, const Mat mask, Scalar color)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawX(Mat img, Rect rect, Scalar color, int thickness)
|
void drawX(Mat img, Rect rect, Scalar color, int thickness)
|
||||||
{
|
{
|
||||||
Point tl(rect.x, rect.y);
|
Point tl(rect.x, rect.y);
|
||||||
Point tr(rect.x + rect.width, rect.y);
|
Point tr(rect.x + rect.width, rect.y);
|
||||||
Point bl(rect.x, rect.y + rect.height);
|
Point bl(rect.x, rect.y + rect.height);
|
||||||
@@ -229,26 +232,26 @@ void drawX(Mat img, Rect rect, Scalar color, int thickness)
|
|||||||
|
|
||||||
line(img, tl, br, color, thickness);
|
line(img, tl, br, color, thickness);
|
||||||
line(img, bl, tr, color, thickness);
|
line(img, bl, tr, color, thickness);
|
||||||
}
|
}
|
||||||
|
|
||||||
double distanceBetweenPoints(Point p1, Point p2)
|
double distanceBetweenPoints(Point p1, Point p2)
|
||||||
{
|
{
|
||||||
float asquared = (p2.x - p1.x)*(p2.x - p1.x);
|
float asquared = (p2.x - p1.x)*(p2.x - p1.x);
|
||||||
float bsquared = (p2.y - p1.y)*(p2.y - p1.y);
|
float bsquared = (p2.y - p1.y)*(p2.y - p1.y);
|
||||||
|
|
||||||
return sqrt(asquared + bsquared);
|
return sqrt(asquared + bsquared);
|
||||||
}
|
}
|
||||||
|
|
||||||
float angleBetweenPoints(Point p1, Point p2)
|
float angleBetweenPoints(Point p1, Point p2)
|
||||||
{
|
{
|
||||||
int deltaY = p2.y - p1.y;
|
int deltaY = p2.y - p1.y;
|
||||||
int deltaX = p2.x - p1.x;
|
int deltaX = p2.x - p1.x;
|
||||||
|
|
||||||
return atan2((float) deltaY, (float) deltaX) * (180 / CV_PI);
|
return atan2((float) deltaY, (float) deltaX) * (180 / CV_PI);
|
||||||
}
|
}
|
||||||
|
|
||||||
Size getSizeMaintainingAspect(Mat inputImg, int maxWidth, int maxHeight)
|
Size getSizeMaintainingAspect(Mat inputImg, int maxWidth, int maxHeight)
|
||||||
{
|
{
|
||||||
float aspect = ((float) inputImg.cols) / ((float) inputImg.rows);
|
float aspect = ((float) inputImg.cols) / ((float) inputImg.rows);
|
||||||
|
|
||||||
if (maxWidth / aspect > maxHeight)
|
if (maxWidth / aspect > maxHeight)
|
||||||
@@ -259,25 +262,25 @@ Size getSizeMaintainingAspect(Mat inputImg, int maxWidth, int maxHeight)
|
|||||||
{
|
{
|
||||||
return Size(maxWidth, maxWidth / aspect);
|
return Size(maxWidth, maxWidth / aspect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LineSegment::LineSegment()
|
LineSegment::LineSegment()
|
||||||
{
|
{
|
||||||
init(0, 0, 0, 0);
|
init(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
LineSegment::LineSegment(Point p1, Point p2)
|
LineSegment::LineSegment(Point p1, Point p2)
|
||||||
{
|
{
|
||||||
init(p1.x, p1.y, p2.x, p2.y);
|
init(p1.x, p1.y, p2.x, p2.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
LineSegment::LineSegment(int x1, int y1, int x2, int y2)
|
LineSegment::LineSegment(int x1, int y1, int x2, int y2)
|
||||||
{
|
{
|
||||||
init(x1, y1, x2, y2);
|
init(x1, y1, x2, y2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LineSegment::init(int x1, int y1, int x2, int y2)
|
void LineSegment::init(int x1, int y1, int x2, int y2)
|
||||||
{
|
{
|
||||||
this->p1 = Point(x1, y1);
|
this->p1 = Point(x1, y1);
|
||||||
this->p2 = Point(x2, y2);
|
this->p2 = Point(x2, y2);
|
||||||
|
|
||||||
@@ -289,20 +292,20 @@ void LineSegment::init(int x1, int y1, int x2, int y2)
|
|||||||
this->length = distanceBetweenPoints(p1, p2);
|
this->length = distanceBetweenPoints(p1, p2);
|
||||||
|
|
||||||
this->angle = angleBetweenPoints(p1, p2);
|
this->angle = angleBetweenPoints(p1, p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LineSegment::isPointBelowLine( Point tp )
|
bool LineSegment::isPointBelowLine( Point tp )
|
||||||
{
|
{
|
||||||
return ((p2.x - p1.x)*(tp.y - p1.y) - (p2.y - p1.y)*(tp.x - p1.x)) > 0;
|
return ((p2.x - p1.x)*(tp.y - p1.y) - (p2.y - p1.y)*(tp.x - p1.x)) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
float LineSegment::getPointAt(float x)
|
float LineSegment::getPointAt(float x)
|
||||||
{
|
{
|
||||||
return slope * (x - p2.x) + p2.y;
|
return slope * (x - p2.x) + p2.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
Point LineSegment::closestPointOnSegmentTo(Point p)
|
Point LineSegment::closestPointOnSegmentTo(Point p)
|
||||||
{
|
{
|
||||||
float top = (p.x - p1.x) * (p2.x - p1.x) + (p.y - p1.y)*(p2.y - p1.y);
|
float top = (p.x - p1.x) * (p2.x - p1.x) + (p.y - p1.y)*(p2.y - p1.y);
|
||||||
|
|
||||||
float bottom = distanceBetweenPoints(p2, p1);
|
float bottom = distanceBetweenPoints(p2, p1);
|
||||||
@@ -314,10 +317,10 @@ Point LineSegment::closestPointOnSegmentTo(Point p)
|
|||||||
float y = p1.y + u * (p2.y - p1.y);
|
float y = p1.y + u * (p2.y - p1.y);
|
||||||
|
|
||||||
return Point(x, y);
|
return Point(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
Point LineSegment::intersection(LineSegment line)
|
Point LineSegment::intersection(LineSegment line)
|
||||||
{
|
{
|
||||||
float c1, c2;
|
float c1, c2;
|
||||||
float intersection_X = -1, intersection_Y= -1;
|
float intersection_X = -1, intersection_Y= -1;
|
||||||
|
|
||||||
@@ -346,10 +349,10 @@ Point LineSegment::intersection(LineSegment line)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Point(intersection_X, intersection_Y);
|
return Point(intersection_X, intersection_Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
Point LineSegment::midpoint()
|
Point LineSegment::midpoint()
|
||||||
{
|
{
|
||||||
// Handle the case where the line is vertical
|
// Handle the case where the line is vertical
|
||||||
if (p1.x == p2.x)
|
if (p1.x == p2.x)
|
||||||
{
|
{
|
||||||
@@ -362,10 +365,10 @@ Point LineSegment::midpoint()
|
|||||||
int midY = getPointAt(midX);
|
int midY = getPointAt(midX);
|
||||||
|
|
||||||
return Point(midX, midY);
|
return Point(midX, midY);
|
||||||
}
|
}
|
||||||
|
|
||||||
LineSegment LineSegment::getParallelLine(float distance)
|
LineSegment LineSegment::getParallelLine(float distance)
|
||||||
{
|
{
|
||||||
float diff_x = p2.x - p1.x;
|
float diff_x = p2.x - p1.x;
|
||||||
float diff_y = p2.y - p1.y;
|
float diff_y = p2.y - p1.y;
|
||||||
float angle = atan2( diff_x, diff_y);
|
float angle = atan2( diff_x, diff_y);
|
||||||
@@ -379,12 +382,12 @@ LineSegment LineSegment::getParallelLine(float distance)
|
|||||||
p2.x + offsetX, p2.y + offsetY);
|
p2.x + offsetX, p2.y + offsetY);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given a contour and a mask, this function determines what percentage of the contour (area)
|
// Given a contour and a mask, this function determines what percentage of the contour (area)
|
||||||
// is inside the masked area.
|
// is inside the masked area.
|
||||||
float getContourAreaPercentInsideMask(cv::Mat mask, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, int contourIndex)
|
float getContourAreaPercentInsideMask(cv::Mat mask, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, int contourIndex)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
Mat innerArea = Mat::zeros(mask.size(), CV_8U);
|
Mat innerArea = Mat::zeros(mask.size(), CV_8U);
|
||||||
@@ -410,27 +413,29 @@ float getContourAreaPercentInsideMask(cv::Mat mask, std::vector<std::vector<cv::
|
|||||||
|
|
||||||
return ((float) endingPixels) / ((float) startingPixels);
|
return ((float) endingPixels) / ((float) startingPixels);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string toString(int value)
|
std::string toString(int value)
|
||||||
{
|
{
|
||||||
stringstream ss;
|
stringstream ss;
|
||||||
ss << value;
|
ss << value;
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
std::string toString(uint value)
|
std::string toString(uint value)
|
||||||
{
|
{
|
||||||
return toString((int) value);
|
return toString((int) value);
|
||||||
}
|
}
|
||||||
std::string toString(float value)
|
std::string toString(float value)
|
||||||
{
|
{
|
||||||
stringstream ss;
|
stringstream ss;
|
||||||
ss << value;
|
ss << value;
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
std::string toString(double value)
|
std::string toString(double value)
|
||||||
{
|
{
|
||||||
stringstream ss;
|
stringstream ss;
|
||||||
ss << value;
|
ss << value;
|
||||||
return ss.str();
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -33,18 +33,11 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
/*
|
namespace alpr
|
||||||
struct LineSegment
|
|
||||||
{
|
{
|
||||||
float x1;
|
|
||||||
float y1;
|
|
||||||
float x2;
|
|
||||||
float y2;
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
class LineSegment
|
class LineSegment
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cv::Point p1, p2;
|
cv::Point p1, p2;
|
||||||
@@ -78,40 +71,42 @@ class LineSegment
|
|||||||
return ss.str() ;
|
return ss.str() ;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
double median(int array[], int arraySize);
|
double median(int array[], int arraySize);
|
||||||
|
|
||||||
std::vector<cv::Mat> produceThresholds(const cv::Mat img_gray, Config* config);
|
std::vector<cv::Mat> produceThresholds(const cv::Mat img_gray, Config* config);
|
||||||
|
|
||||||
cv::Mat drawImageDashboard(std::vector<cv::Mat> images, int imageType, uint numColumns);
|
cv::Mat drawImageDashboard(std::vector<cv::Mat> images, int imageType, uint numColumns);
|
||||||
|
|
||||||
void displayImage(Config* config, std::string windowName, cv::Mat frame);
|
void displayImage(Config* config, std::string windowName, cv::Mat frame);
|
||||||
void drawAndWait(cv::Mat* frame);
|
void drawAndWait(cv::Mat* frame);
|
||||||
|
|
||||||
double distanceBetweenPoints(cv::Point p1, cv::Point p2);
|
double distanceBetweenPoints(cv::Point p1, cv::Point p2);
|
||||||
|
|
||||||
void drawRotatedRect(cv::Mat* img, cv::RotatedRect rect, cv::Scalar color, int thickness);
|
void drawRotatedRect(cv::Mat* img, cv::RotatedRect rect, cv::Scalar color, int thickness);
|
||||||
|
|
||||||
void drawX(cv::Mat img, cv::Rect rect, cv::Scalar color, int thickness);
|
void drawX(cv::Mat img, cv::Rect rect, cv::Scalar color, int thickness);
|
||||||
void fillMask(cv::Mat img, const cv::Mat mask, cv::Scalar color);
|
void fillMask(cv::Mat img, const cv::Mat mask, cv::Scalar color);
|
||||||
|
|
||||||
float angleBetweenPoints(cv::Point p1, cv::Point p2);
|
float angleBetweenPoints(cv::Point p1, cv::Point p2);
|
||||||
|
|
||||||
cv::Size getSizeMaintainingAspect(cv::Mat inputImg, int maxWidth, int maxHeight);
|
cv::Size getSizeMaintainingAspect(cv::Mat inputImg, int maxWidth, int maxHeight);
|
||||||
|
|
||||||
float getContourAreaPercentInsideMask(cv::Mat mask, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, int contourIndex);
|
float getContourAreaPercentInsideMask(cv::Mat mask, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, int contourIndex);
|
||||||
|
|
||||||
cv::Mat equalizeBrightness(cv::Mat img);
|
cv::Mat equalizeBrightness(cv::Mat img);
|
||||||
|
|
||||||
cv::Rect expandRect(cv::Rect original, int expandXPixels, int expandYPixels, int maxX, int maxY);
|
cv::Rect expandRect(cv::Rect original, int expandXPixels, int expandYPixels, int maxX, int maxY);
|
||||||
|
|
||||||
cv::Mat addLabel(cv::Mat input, std::string label);
|
cv::Mat addLabel(cv::Mat input, std::string label);
|
||||||
|
|
||||||
|
|
||||||
std::string toString(int value);
|
std::string toString(int value);
|
||||||
std::string toString(uint value);
|
std::string toString(uint value);
|
||||||
std::string toString(float value);
|
std::string toString(float value);
|
||||||
std::string toString(double value);
|
std::string toString(double value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // OPENALPR_UTILITY_H
|
#endif // OPENALPR_UTILITY_H
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace alpr;
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE( "JSON Serialization/Deserialization", "[json]" ) {
|
TEST_CASE( "JSON Serialization/Deserialization", "[json]" ) {
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
using namespace alpr;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "videobuffer.h"
|
#include "videobuffer.h"
|
||||||
|
|
||||||
|
using namespace alpr;
|
||||||
|
|
||||||
void imageCollectionThread(void* arg);
|
void imageCollectionThread(void* arg);
|
||||||
void getALPRImages(cv::VideoCapture cap, VideoDispatcher* dispatcher);
|
void getALPRImages(cv::VideoCapture cap, VideoDispatcher* dispatcher);
|
||||||
|
Reference in New Issue
Block a user