mirror of
https://github.com/kerberos-io/openalpr-base.git
synced 2025-10-06 15:26:53 +08:00
Merge branch 'pipeline'
Conflicts: src/openalpr/CMakeLists.txt
This commit is contained in:
@@ -104,6 +104,8 @@ int main( int argc, const char** argv )
|
|||||||
plateCoords.y = 0;
|
plateCoords.y = 0;
|
||||||
plateCoords.width = frame.cols;
|
plateCoords.width = frame.cols;
|
||||||
plateCoords.height = frame.rows;
|
plateCoords.height = frame.rows;
|
||||||
|
|
||||||
|
PipelineData pipeline_data(frame, plateCoords, config);
|
||||||
|
|
||||||
char statecode[3];
|
char statecode[3];
|
||||||
statecode[0] = files[i][0];
|
statecode[0] = files[i][0];
|
||||||
@@ -111,7 +113,7 @@ int main( int argc, const char** argv )
|
|||||||
statecode[2] = '\0';
|
statecode[2] = '\0';
|
||||||
string statecodestr(statecode);
|
string statecodestr(statecode);
|
||||||
|
|
||||||
CharacterRegion charRegion(frame, config);
|
CharacterRegion charRegion(&pipeline_data);
|
||||||
|
|
||||||
if (abs(charRegion.getTopLine().angle) > 4)
|
if (abs(charRegion.getTopLine().angle) > 4)
|
||||||
{
|
{
|
||||||
@@ -124,10 +126,11 @@ int main( int argc, const char** argv )
|
|||||||
warpAffine( frame, rotated, rot_mat, frame.size() );
|
warpAffine( frame, rotated, rot_mat, frame.size() );
|
||||||
|
|
||||||
rotated.copyTo(frame);
|
rotated.copyTo(frame);
|
||||||
|
pipeline_data.crop_gray = frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterSegmenter charSegmenter(frame, charRegion.thresholdsInverted(), config);
|
CharacterSegmenter charSegmenter(&pipeline_data);
|
||||||
ocr->performOCR(charSegmenter.getThresholds(), charSegmenter.characters);
|
ocr->performOCR(&pipeline_data);
|
||||||
ocr->postProcessor->analyze(statecode, 25);
|
ocr->postProcessor->analyze(statecode, 25);
|
||||||
|
|
||||||
cout << files[i] << "," << statecode << "," << ocr->postProcessor->bestChars << endl;
|
cout << files[i] << "," << statecode << "," << ocr->postProcessor->bestChars << endl;
|
||||||
@@ -211,27 +214,30 @@ int main( int argc, const char** argv )
|
|||||||
|
|
||||||
for (int z = 0; z < regions.size(); z++)
|
for (int z = 0; z < regions.size(); z++)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
PipelineData pipeline_data(frame, regions[z].rect, &config);
|
||||||
|
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
char temp[5];
|
|
||||||
stateIdentifier.recognize(frame, regions[z].rect, temp);
|
stateIdentifier.recognize(&pipeline_data);
|
||||||
getTime(&endTime);
|
getTime(&endTime);
|
||||||
double stateidTime = diffclock(startTime, endTime);
|
double stateidTime = diffclock(startTime, endTime);
|
||||||
cout << "\tRegion " << z << ": State ID time: " << stateidTime << "ms." << endl;
|
cout << "\tRegion " << z << ": State ID time: " << stateidTime << "ms." << endl;
|
||||||
stateIdTimes.push_back(stateidTime);
|
stateIdTimes.push_back(stateidTime);
|
||||||
|
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
LicensePlateCandidate lp(frame, regions[z].rect, &config);
|
LicensePlateCandidate lp(&pipeline_data);
|
||||||
lp.recognize();
|
lp.recognize();
|
||||||
getTime(&endTime);
|
getTime(&endTime);
|
||||||
double analysisTime = diffclock(startTime, endTime);
|
double analysisTime = diffclock(startTime, endTime);
|
||||||
cout << "\tRegion " << z << ": Analysis time: " << analysisTime << "ms." << endl;
|
cout << "\tRegion " << z << ": Analysis time: " << analysisTime << "ms." << endl;
|
||||||
|
|
||||||
if (lp.confidence > 10)
|
if (pipeline_data.plate_area_confidence > 10)
|
||||||
{
|
{
|
||||||
lpAnalysisPositiveTimes.push_back(analysisTime);
|
lpAnalysisPositiveTimes.push_back(analysisTime);
|
||||||
|
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
ocr.performOCR(lp.charSegmenter->getThresholds(), lp.charSegmenter->characters);
|
ocr.performOCR(&pipeline_data);
|
||||||
getTime(&endTime);
|
getTime(&endTime);
|
||||||
double ocrTime = diffclock(startTime, endTime);
|
double ocrTime = diffclock(startTime, endTime);
|
||||||
cout << "\tRegion " << z << ": OCR time: " << ocrTime << "ms." << endl;
|
cout << "\tRegion " << z << ": OCR time: " << ocrTime << "ms." << endl;
|
||||||
|
@@ -124,13 +124,14 @@ int main( int argc, const char** argv )
|
|||||||
|
|
||||||
imshow ("Original", frame);
|
imshow ("Original", frame);
|
||||||
|
|
||||||
|
PipelineData pipeline_data(frame, Rect(0, 0, frame.cols, frame.rows), &config);
|
||||||
char statecode[3];
|
char statecode[3];
|
||||||
statecode[0] = files[i][0];
|
statecode[0] = files[i][0];
|
||||||
statecode[1] = files[i][1];
|
statecode[1] = files[i][1];
|
||||||
statecode[2] = '\0';
|
statecode[2] = '\0';
|
||||||
string statecodestr(statecode);
|
string statecodestr(statecode);
|
||||||
|
|
||||||
CharacterRegion regionizer(frame, &config);
|
CharacterRegion regionizer(&pipeline_data);
|
||||||
|
|
||||||
if (abs(regionizer.getTopLine().angle) > 4)
|
if (abs(regionizer.getTopLine().angle) > 4)
|
||||||
{
|
{
|
||||||
@@ -143,28 +144,29 @@ int main( int argc, const char** argv )
|
|||||||
warpAffine( frame, rotated, rot_mat, frame.size() );
|
warpAffine( frame, rotated, rot_mat, frame.size() );
|
||||||
|
|
||||||
rotated.copyTo(frame);
|
rotated.copyTo(frame);
|
||||||
|
pipeline_data.crop_gray = rotated;
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterSegmenter charSegmenter(frame, regionizer.thresholdsInverted(), &config);
|
CharacterSegmenter charSegmenter(&pipeline_data);
|
||||||
|
|
||||||
//ocr.cleanCharRegions(charSegmenter.thresholds, charSegmenter.characters);
|
//ocr.cleanCharRegions(charSegmenter.thresholds, charSegmenter.characters);
|
||||||
|
|
||||||
ocr.performOCR(charSegmenter.getThresholds(), charSegmenter.characters);
|
ocr.performOCR(&pipeline_data);
|
||||||
ocr.postProcessor->analyze(statecodestr, 25);
|
ocr.postProcessor->analyze(statecodestr, 25);
|
||||||
cout << "OCR results: " << ocr.postProcessor->bestChars << endl;
|
cout << "OCR results: " << ocr.postProcessor->bestChars << endl;
|
||||||
|
|
||||||
vector<bool> selectedBoxes(charSegmenter.getThresholds().size());
|
vector<bool> selectedBoxes(pipeline_data.thresholds.size());
|
||||||
for (int z = 0; z < charSegmenter.getThresholds().size(); z++)
|
for (int z = 0; z < pipeline_data.thresholds.size(); z++)
|
||||||
selectedBoxes[z] = false;
|
selectedBoxes[z] = false;
|
||||||
|
|
||||||
int curDashboardSelection = 0;
|
int curDashboardSelection = 0;
|
||||||
|
|
||||||
vector<char> humanInputs(charSegmenter.characters.size());
|
vector<char> humanInputs(pipeline_data.charRegions.size());
|
||||||
|
|
||||||
for (int z = 0; z < charSegmenter.characters.size(); z++)
|
for (int z = 0; z < pipeline_data.charRegions.size(); z++)
|
||||||
humanInputs[z] = ' ';
|
humanInputs[z] = ' ';
|
||||||
|
|
||||||
showDashboard(charSegmenter.getThresholds(), selectedBoxes, 0);
|
showDashboard(pipeline_data.thresholds, selectedBoxes, 0);
|
||||||
|
|
||||||
char waitkey = (char) waitKey(50);
|
char waitkey = (char) waitKey(50);
|
||||||
|
|
||||||
@@ -174,50 +176,50 @@ int main( int argc, const char** argv )
|
|||||||
{
|
{
|
||||||
if (curDashboardSelection > 0)
|
if (curDashboardSelection > 0)
|
||||||
curDashboardSelection--;
|
curDashboardSelection--;
|
||||||
showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection);
|
showDashboard(pipeline_data.thresholds, selectedBoxes, curDashboardSelection);
|
||||||
}
|
}
|
||||||
else if (waitkey == RIGHT_ARROW_KEY) // right arrow key
|
else if (waitkey == RIGHT_ARROW_KEY) // right arrow key
|
||||||
{
|
{
|
||||||
if (curDashboardSelection < charSegmenter.getThresholds().size() - 1)
|
if (curDashboardSelection < pipeline_data.thresholds.size() - 1)
|
||||||
curDashboardSelection++;
|
curDashboardSelection++;
|
||||||
showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection);
|
showDashboard(pipeline_data.thresholds, selectedBoxes, curDashboardSelection);
|
||||||
}
|
}
|
||||||
else if (waitkey == DOWN_ARROW_KEY)
|
else if (waitkey == DOWN_ARROW_KEY)
|
||||||
{
|
{
|
||||||
if (curDashboardSelection + DASHBOARD_COLUMNS <= charSegmenter.getThresholds().size() - 1)
|
if (curDashboardSelection + DASHBOARD_COLUMNS <= pipeline_data.thresholds.size() - 1)
|
||||||
curDashboardSelection += DASHBOARD_COLUMNS;
|
curDashboardSelection += DASHBOARD_COLUMNS;
|
||||||
showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection);
|
showDashboard(pipeline_data.thresholds, selectedBoxes, curDashboardSelection);
|
||||||
}
|
}
|
||||||
else if (waitkey == UP_ARROW_KEY)
|
else if (waitkey == UP_ARROW_KEY)
|
||||||
{
|
{
|
||||||
if (curDashboardSelection - DASHBOARD_COLUMNS >= 0)
|
if (curDashboardSelection - DASHBOARD_COLUMNS >= 0)
|
||||||
curDashboardSelection -= DASHBOARD_COLUMNS;
|
curDashboardSelection -= DASHBOARD_COLUMNS;
|
||||||
showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection);
|
showDashboard(pipeline_data.thresholds, selectedBoxes, curDashboardSelection);
|
||||||
}
|
}
|
||||||
else if (waitkey == ENTER_KEY)
|
else if (waitkey == ENTER_KEY)
|
||||||
{
|
{
|
||||||
vector<char> tempdata = showCharSelection(charSegmenter.getThresholds()[curDashboardSelection], charSegmenter.characters, statecodestr);
|
vector<char> tempdata = showCharSelection(pipeline_data.thresholds[curDashboardSelection], pipeline_data.charRegions, statecodestr);
|
||||||
for (int c = 0; c < charSegmenter.characters.size(); c++)
|
for (int c = 0; c < pipeline_data.charRegions.size(); c++)
|
||||||
humanInputs[c] = tempdata[c];
|
humanInputs[c] = tempdata[c];
|
||||||
}
|
}
|
||||||
else if (waitkey == SPACE_KEY)
|
else if (waitkey == SPACE_KEY)
|
||||||
{
|
{
|
||||||
selectedBoxes[curDashboardSelection] = !selectedBoxes[curDashboardSelection];
|
selectedBoxes[curDashboardSelection] = !selectedBoxes[curDashboardSelection];
|
||||||
showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection);
|
showDashboard(pipeline_data.thresholds, selectedBoxes, curDashboardSelection);
|
||||||
}
|
}
|
||||||
else if (waitkey == 's' || waitkey == 'S' || waitkey == 'W')
|
else if (waitkey == 's' || waitkey == 'S' || waitkey == 'W')
|
||||||
{
|
{
|
||||||
if (waitkey == 'W')
|
if (waitkey == 'W')
|
||||||
{
|
{
|
||||||
selectedBoxes[curDashboardSelection] = true;
|
selectedBoxes[curDashboardSelection] = true;
|
||||||
showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection);
|
showDashboard(pipeline_data.thresholds, selectedBoxes, curDashboardSelection);
|
||||||
const std::string& ocr_str = ocr.postProcessor->bestChars;
|
const std::string& ocr_str = ocr.postProcessor->bestChars;
|
||||||
humanInputs.assign(ocr_str.begin(), ocr_str.end());
|
humanInputs.assign(ocr_str.begin(), ocr_str.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool somethingSelected = false;
|
bool somethingSelected = false;
|
||||||
bool chardataTagged = false;
|
bool chardataTagged = false;
|
||||||
for (int c = 0; c < charSegmenter.getThresholds().size(); c++)
|
for (int c = 0; c < pipeline_data.thresholds.size(); c++)
|
||||||
{
|
{
|
||||||
if (selectedBoxes[c])
|
if (selectedBoxes[c])
|
||||||
{
|
{
|
||||||
@@ -225,7 +227,7 @@ int main( int argc, const char** argv )
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int c = 0; c < charSegmenter.characters.size(); c++)
|
for (int c = 0; c < pipeline_data.charRegions.size(); c++)
|
||||||
{
|
{
|
||||||
if (humanInputs[c] != ' ')
|
if (humanInputs[c] != ' ')
|
||||||
{
|
{
|
||||||
@@ -236,18 +238,18 @@ int main( int argc, const char** argv )
|
|||||||
// Save
|
// Save
|
||||||
if (somethingSelected && chardataTagged)
|
if (somethingSelected && chardataTagged)
|
||||||
{
|
{
|
||||||
for (int c = 0; c < charSegmenter.characters.size(); c++)
|
for (int c = 0; c < pipeline_data.charRegions.size(); c++)
|
||||||
{
|
{
|
||||||
if (humanInputs[c] == ' ')
|
if (humanInputs[c] == ' ')
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (int t = 0; t < charSegmenter.getThresholds().size(); t++)
|
for (int t = 0; t < pipeline_data.thresholds.size(); t++)
|
||||||
{
|
{
|
||||||
if (selectedBoxes[t] == false)
|
if (selectedBoxes[t] == false)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
stringstream filename;
|
stringstream filename;
|
||||||
Mat cropped = charSegmenter.getThresholds()[t](charSegmenter.characters[c]);
|
Mat cropped = pipeline_data.thresholds[t](pipeline_data.charRegions[c]);
|
||||||
filename << outDir << "/" << humanInputs[c] << "-" << t << "-" << files[i];
|
filename << outDir << "/" << humanInputs[c] << "-" << t << "-" << files[i];
|
||||||
imwrite(filename.str(), cropped);
|
imwrite(filename.str(), cropped);
|
||||||
cout << "Writing char image: " << filename.str() << endl;
|
cout << "Writing char image: " << filename.str() << endl;
|
||||||
|
@@ -79,31 +79,26 @@ int main( int argc, const char** argv )
|
|||||||
cout << fullpath << endl;
|
cout << fullpath << endl;
|
||||||
frame = imread( fullpath.c_str() );
|
frame = imread( fullpath.c_str() );
|
||||||
|
|
||||||
char code[4];
|
PipelineData pipeline_data(frame, Rect(0, 0, frame.cols, frame.rows), &config);
|
||||||
int confidence = identifier.recognize(frame, code);
|
identifier.recognize(&pipeline_data);
|
||||||
|
|
||||||
if (confidence <= 20)
|
if (pipeline_data.region_confidence <= 20)
|
||||||
{
|
{
|
||||||
code[0] = 'z';
|
pipeline_data.region_code = 'zz';
|
||||||
code[1] = 'z';
|
pipeline_data.region_confidence = 100;
|
||||||
confidence = 100;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
//imshow("Plate", frame);
|
{
|
||||||
if (confidence > 20)
|
cout << pipeline_data.region_confidence << " : " << pipeline_data.region_code;
|
||||||
{
|
|
||||||
cout << confidence << " : " << code;
|
|
||||||
|
|
||||||
ostringstream convert; // stream used for the conversion
|
ostringstream convert; // stream used for the conversion
|
||||||
convert << i; // insert the textual representation of 'Number' in the characters in the stream
|
convert << i; // insert the textual representation of 'Number' in the characters in the stream
|
||||||
|
|
||||||
string copyCommand = "cp \"" + fullpath + "\" " + outDir + code + convert.str() + ".png";
|
string copyCommand = "cp \"" + fullpath + "\" " + outDir + pipeline_data.region_code + convert.str() + ".png";
|
||||||
system( copyCommand.c_str() );
|
system( copyCommand.c_str() );
|
||||||
waitKey(50);
|
waitKey(50);
|
||||||
//while ((char) waitKey(50) != 'c') { }
|
//while ((char) waitKey(50) != 'c') { }
|
||||||
}
|
}
|
||||||
else
|
|
||||||
waitKey(50);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,7 @@ set(lpr_source_files
|
|||||||
platecorners.cpp
|
platecorners.cpp
|
||||||
colorfilter.cpp
|
colorfilter.cpp
|
||||||
characteranalysis.cpp
|
characteranalysis.cpp
|
||||||
|
pipeline_data.cpp
|
||||||
trex.c
|
trex.c
|
||||||
cjson.c
|
cjson.c
|
||||||
)
|
)
|
||||||
|
@@ -186,17 +186,17 @@ void plateAnalysisThread(void* arg)
|
|||||||
if (dispatcher->config->debugGeneral)
|
if (dispatcher->config->debugGeneral)
|
||||||
cout << "Thread: " << tthread::this_thread::get_id() << " loop " << ++loop_count << endl;
|
cout << "Thread: " << tthread::this_thread::get_id() << " loop " << ++loop_count << endl;
|
||||||
|
|
||||||
Mat img = dispatcher->getImageCopy();
|
PipelineData pipeline_data(dispatcher->getImageCopy(), plateRegion.rect, dispatcher->config);
|
||||||
|
|
||||||
timespec platestarttime;
|
timespec platestarttime;
|
||||||
getTime(&platestarttime);
|
getTime(&platestarttime);
|
||||||
|
|
||||||
LicensePlateCandidate lp(img, plateRegion.rect, dispatcher->config);
|
LicensePlateCandidate lp(&pipeline_data);
|
||||||
|
|
||||||
lp.recognize();
|
lp.recognize();
|
||||||
|
|
||||||
|
|
||||||
if (lp.confidence <= 10)
|
if (pipeline_data.plate_area_confidence <= 10)
|
||||||
{
|
{
|
||||||
// Not a valid plate
|
// Not a valid plate
|
||||||
// Check if this plate has any children, if so, send them back up to the dispatcher for processing
|
// Check if this plate has any children, if so, send them back up to the dispatcher for processing
|
||||||
@@ -213,14 +213,14 @@ void plateAnalysisThread(void* arg)
|
|||||||
|
|
||||||
for (int pointidx = 0; pointidx < 4; pointidx++)
|
for (int pointidx = 0; pointidx < 4; pointidx++)
|
||||||
{
|
{
|
||||||
plateResult.plate_points[pointidx].x = (int) lp.plateCorners[pointidx].x;
|
plateResult.plate_points[pointidx].x = (int) pipeline_data.plate_corners[pointidx].x;
|
||||||
plateResult.plate_points[pointidx].y = (int) lp.plateCorners[pointidx].y;
|
plateResult.plate_points[pointidx].y = (int) pipeline_data.plate_corners[pointidx].y;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dispatcher->detectRegion)
|
if (dispatcher->detectRegion)
|
||||||
{
|
{
|
||||||
char statecode[4];
|
char statecode[4];
|
||||||
plateResult.regionConfidence = dispatcher->stateIdentifier->recognize(img, plateRegion.rect, statecode);
|
plateResult.regionConfidence = dispatcher->stateIdentifier->recognize(&pipeline_data);
|
||||||
if (plateResult.regionConfidence > 0)
|
if (plateResult.regionConfidence > 0)
|
||||||
{
|
{
|
||||||
plateResult.region = statecode;
|
plateResult.region = statecode;
|
||||||
@@ -230,7 +230,7 @@ void plateAnalysisThread(void* arg)
|
|||||||
|
|
||||||
// Tesseract OCR does not appear to be threadsafe
|
// Tesseract OCR does not appear to be threadsafe
|
||||||
dispatcher->ocrMutex.lock();
|
dispatcher->ocrMutex.lock();
|
||||||
dispatcher->ocr->performOCR(lp.charSegmenter->getThresholds(), lp.charSegmenter->characters);
|
dispatcher->ocr->performOCR(&pipeline_data);
|
||||||
dispatcher->ocr->postProcessor->analyze(plateResult.region, dispatcher->topN);
|
dispatcher->ocr->postProcessor->analyze(plateResult.region, dispatcher->topN);
|
||||||
const vector<PPResult> ppResults = dispatcher->ocr->postProcessor->getResults();
|
const vector<PPResult> ppResults = dispatcher->ocr->postProcessor->getResults();
|
||||||
dispatcher->ocrMutex.unlock();
|
dispatcher->ocrMutex.unlock();
|
||||||
|
@@ -37,6 +37,8 @@
|
|||||||
|
|
||||||
#include "cjson.h"
|
#include "cjson.h"
|
||||||
|
|
||||||
|
#include "pipeline_data.h"
|
||||||
|
|
||||||
#include <opencv2/core/core.hpp>
|
#include <opencv2/core/core.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
@@ -22,61 +22,40 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
CharacterAnalysis::CharacterAnalysis(Mat img, Config* config)
|
CharacterAnalysis::CharacterAnalysis(PipelineData* pipeline_data)
|
||||||
{
|
{
|
||||||
this->config = config;
|
this->pipeline_data = pipeline_data;
|
||||||
|
this->config = pipeline_data->config;
|
||||||
|
|
||||||
this->hasPlateMask = false;
|
this->hasPlateMask = false;
|
||||||
|
|
||||||
if (this->config->debugCharAnalysis)
|
if (this->config->debugCharAnalysis)
|
||||||
cout << "Starting CharacterAnalysis identification" << endl;
|
cout << "Starting CharacterAnalysis identification" << endl;
|
||||||
|
|
||||||
if (img.type() != CV_8U)
|
|
||||||
cvtColor( img, this->img_gray, CV_BGR2GRAY );
|
|
||||||
else
|
|
||||||
{
|
|
||||||
img_gray = Mat(img.size(), img.type());
|
|
||||||
img.copyTo(img_gray);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterAnalysis::~CharacterAnalysis()
|
CharacterAnalysis::~CharacterAnalysis()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < thresholds.size(); i++)
|
|
||||||
{
|
|
||||||
thresholds[i].release();
|
|
||||||
}
|
|
||||||
thresholds.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterAnalysis::analyze()
|
void CharacterAnalysis::analyze()
|
||||||
{
|
{
|
||||||
thresholds = produceThresholds(img_gray, config);
|
pipeline_data->clearThresholds();
|
||||||
|
pipeline_data->thresholds = produceThresholds(pipeline_data->crop_gray, config);
|
||||||
|
|
||||||
/*
|
|
||||||
// Morph Close the gray image to make it easier to detect blobs
|
|
||||||
int morph_elem = 1;
|
|
||||||
int morph_size = 1;
|
|
||||||
Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
|
|
||||||
|
|
||||||
for (int i = 0; i < thresholds.size(); i++)
|
|
||||||
{
|
|
||||||
//morphologyEx( mask, mask, MORPH_CLOSE, element );
|
|
||||||
morphologyEx( thresholds[i], thresholds[i], MORPH_OPEN, element );
|
|
||||||
//dilate( thresholds[i], thresholds[i], element );
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
timespec startTime;
|
timespec startTime;
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
|
|
||||||
for (int i = 0; i < thresholds.size(); i++)
|
for (int i = 0; i < pipeline_data->thresholds.size(); i++)
|
||||||
{
|
{
|
||||||
vector<vector<Point> > contours;
|
vector<vector<Point> > contours;
|
||||||
vector<Vec4i> hierarchy;
|
vector<Vec4i> hierarchy;
|
||||||
|
|
||||||
Mat tempThreshold(thresholds[i].size(), CV_8U);
|
Mat tempThreshold(pipeline_data->thresholds[i].size(), CV_8U);
|
||||||
thresholds[i].copyTo(tempThreshold);
|
pipeline_data->thresholds[i].copyTo(tempThreshold);
|
||||||
findContours(tempThreshold,
|
findContours(tempThreshold,
|
||||||
contours, // a vector of contours
|
contours, // a vector of contours
|
||||||
hierarchy,
|
hierarchy,
|
||||||
@@ -97,9 +76,9 @@ void CharacterAnalysis::analyze()
|
|||||||
|
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
|
|
||||||
for (int i = 0; i < thresholds.size(); i++)
|
for (int i = 0; i < pipeline_data->thresholds.size(); i++)
|
||||||
{
|
{
|
||||||
vector<bool> goodIndices = this->filter(thresholds[i], allContours[i], allHierarchy[i]);
|
vector<bool> goodIndices = this->filter(pipeline_data->thresholds[i], allContours[i], allHierarchy[i]);
|
||||||
charSegments.push_back(goodIndices);
|
charSegments.push_back(goodIndices);
|
||||||
|
|
||||||
if (config->debugCharAnalysis)
|
if (config->debugCharAnalysis)
|
||||||
@@ -118,7 +97,7 @@ void CharacterAnalysis::analyze()
|
|||||||
if (hasPlateMask)
|
if (hasPlateMask)
|
||||||
{
|
{
|
||||||
// Filter out bad contours now that we have an outer box mask...
|
// Filter out bad contours now that we have an outer box mask...
|
||||||
for (int i = 0; i < thresholds.size(); i++)
|
for (int i = 0; i < pipeline_data->thresholds.size(); i++)
|
||||||
{
|
{
|
||||||
charSegments[i] = filterByOuterMask(allContours[i], allHierarchy[i], charSegments[i]);
|
charSegments[i] = filterByOuterMask(allContours[i], allHierarchy[i], charSegments[i]);
|
||||||
}
|
}
|
||||||
@@ -126,7 +105,7 @@ void CharacterAnalysis::analyze()
|
|||||||
|
|
||||||
int bestFitScore = -1;
|
int bestFitScore = -1;
|
||||||
int bestFitIndex = -1;
|
int bestFitIndex = -1;
|
||||||
for (int i = 0; i < thresholds.size(); i++)
|
for (int i = 0; i < pipeline_data->thresholds.size(); i++)
|
||||||
{
|
{
|
||||||
//vector<bool> goodIndices = this->filter(thresholds[i], allContours[i], allHierarchy[i]);
|
//vector<bool> goodIndices = this->filter(thresholds[i], allContours[i], allHierarchy[i]);
|
||||||
//charSegments.push_back(goodIndices);
|
//charSegments.push_back(goodIndices);
|
||||||
@@ -138,7 +117,7 @@ void CharacterAnalysis::analyze()
|
|||||||
bestFitScore = segmentCount;
|
bestFitScore = segmentCount;
|
||||||
bestFitIndex = i;
|
bestFitIndex = i;
|
||||||
bestCharSegments = charSegments[i];
|
bestCharSegments = charSegments[i];
|
||||||
bestThreshold = thresholds[i];
|
bestThreshold = pipeline_data->thresholds[i];
|
||||||
bestContours = allContours[i];
|
bestContours = allContours[i];
|
||||||
bestHierarchy = allHierarchy[i];
|
bestHierarchy = allHierarchy[i];
|
||||||
bestCharSegmentsCount = segmentCount;
|
bestCharSegmentsCount = segmentCount;
|
||||||
@@ -181,7 +160,7 @@ void CharacterAnalysis::analyze()
|
|||||||
|
|
||||||
//charsegments = this->getPossibleCharRegions(img_threshold, allContours, allHierarchy, STARTING_MIN_HEIGHT + (bestFitIndex * HEIGHT_STEP), STARTING_MAX_HEIGHT + (bestFitIndex * HEIGHT_STEP));
|
//charsegments = this->getPossibleCharRegions(img_threshold, allContours, allHierarchy, STARTING_MIN_HEIGHT + (bestFitIndex * HEIGHT_STEP), STARTING_MAX_HEIGHT + (bestFitIndex * HEIGHT_STEP));
|
||||||
|
|
||||||
this->linePolygon = getBestVotedLines(img_gray, bestContours, bestCharSegments);
|
this->linePolygon = getBestVotedLines(pipeline_data->crop_gray, bestContours, bestCharSegments);
|
||||||
|
|
||||||
if (this->linePolygon.size() > 0)
|
if (this->linePolygon.size() > 0)
|
||||||
{
|
{
|
||||||
@@ -290,7 +269,7 @@ Mat CharacterAnalysis::findOuterBoxMask()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat mask = Mat::zeros(thresholds[winningIndex].size(), CV_8U);
|
Mat mask = Mat::zeros(pipeline_data->thresholds[winningIndex].size(), CV_8U);
|
||||||
|
|
||||||
// get rid of the outline by drawing a 1 pixel width black line
|
// get rid of the outline by drawing a 1 pixel width black line
|
||||||
drawContours(mask, allContours[winningIndex],
|
drawContours(mask, allContours[winningIndex],
|
||||||
@@ -334,7 +313,7 @@ Mat CharacterAnalysis::findOuterBoxMask()
|
|||||||
|
|
||||||
if (biggestContourIndex != -1)
|
if (biggestContourIndex != -1)
|
||||||
{
|
{
|
||||||
mask = Mat::zeros(thresholds[winningIndex].size(), CV_8U);
|
mask = Mat::zeros(pipeline_data->thresholds[winningIndex].size(), CV_8U);
|
||||||
|
|
||||||
vector<Point> smoothedMaskPoints;
|
vector<Point> smoothedMaskPoints;
|
||||||
approxPolyDP(contoursSecondRound[biggestContourIndex], smoothedMaskPoints, 2, true);
|
approxPolyDP(contoursSecondRound[biggestContourIndex], smoothedMaskPoints, 2, true);
|
||||||
@@ -355,12 +334,12 @@ Mat CharacterAnalysis::findOuterBoxMask()
|
|||||||
if (this->config->debugCharAnalysis)
|
if (this->config->debugCharAnalysis)
|
||||||
{
|
{
|
||||||
vector<Mat> debugImgs;
|
vector<Mat> debugImgs;
|
||||||
Mat debugImgMasked = Mat::zeros(thresholds[winningIndex].size(), CV_8U);
|
Mat debugImgMasked = Mat::zeros(pipeline_data->thresholds[winningIndex].size(), CV_8U);
|
||||||
|
|
||||||
thresholds[winningIndex].copyTo(debugImgMasked, mask);
|
pipeline_data->thresholds[winningIndex].copyTo(debugImgMasked, mask);
|
||||||
|
|
||||||
debugImgs.push_back(mask);
|
debugImgs.push_back(mask);
|
||||||
debugImgs.push_back(thresholds[winningIndex]);
|
debugImgs.push_back(pipeline_data->thresholds[winningIndex]);
|
||||||
debugImgs.push_back(debugImgMasked);
|
debugImgs.push_back(debugImgMasked);
|
||||||
|
|
||||||
Mat dashboard = drawImageDashboard(debugImgs, CV_8U, 1);
|
Mat dashboard = drawImageDashboard(debugImgs, CV_8U, 1);
|
||||||
@@ -372,7 +351,7 @@ Mat CharacterAnalysis::findOuterBoxMask()
|
|||||||
}
|
}
|
||||||
|
|
||||||
hasPlateMask = false;
|
hasPlateMask = false;
|
||||||
Mat fullMask = Mat::zeros(thresholds[0].size(), CV_8U);
|
Mat fullMask = Mat::zeros(pipeline_data->thresholds[0].size(), CV_8U);
|
||||||
bitwise_not(fullMask, fullMask);
|
bitwise_not(fullMask, fullMask);
|
||||||
return fullMask;
|
return fullMask;
|
||||||
}
|
}
|
||||||
|
@@ -24,13 +24,13 @@
|
|||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "pipeline_data.h"
|
||||||
|
|
||||||
class CharacterAnalysis
|
class CharacterAnalysis
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CharacterAnalysis(cv::Mat img, Config* config);
|
CharacterAnalysis(PipelineData* pipeline_data);
|
||||||
virtual ~CharacterAnalysis();
|
virtual ~CharacterAnalysis();
|
||||||
|
|
||||||
bool hasPlateMask;
|
bool hasPlateMask;
|
||||||
@@ -54,7 +54,6 @@ class CharacterAnalysis
|
|||||||
|
|
||||||
bool thresholdsInverted;
|
bool thresholdsInverted;
|
||||||
|
|
||||||
std::vector<cv::Mat> thresholds;
|
|
||||||
std::vector<std::vector<std::vector<cv::Point> > > allContours;
|
std::vector<std::vector<std::vector<cv::Point> > > allContours;
|
||||||
std::vector<std::vector<cv::Vec4i> > allHierarchy;
|
std::vector<std::vector<cv::Vec4i> > allHierarchy;
|
||||||
std::vector<std::vector<bool> > charSegments;
|
std::vector<std::vector<bool> > charSegments;
|
||||||
@@ -64,10 +63,9 @@ class CharacterAnalysis
|
|||||||
cv::Mat getCharacterMask();
|
cv::Mat getCharacterMask();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
PipelineData* pipeline_data;
|
||||||
Config* config;
|
Config* config;
|
||||||
|
|
||||||
cv::Mat img_gray;
|
|
||||||
|
|
||||||
cv::Mat findOuterBoxMask( );
|
cv::Mat findOuterBoxMask( );
|
||||||
|
|
||||||
bool isPlateInverted();
|
bool isPlateInverted();
|
||||||
|
@@ -22,9 +22,9 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
CharacterRegion::CharacterRegion(Mat img, Config* config)
|
CharacterRegion::CharacterRegion(PipelineData* pipeline_data)
|
||||||
{
|
{
|
||||||
this->config = config;
|
this->config = pipeline_data->config;
|
||||||
this->debug = config->debugCharRegions;
|
this->debug = config->debugCharRegions;
|
||||||
|
|
||||||
this->confidence = 0;
|
this->confidence = 0;
|
||||||
@@ -35,16 +35,18 @@ CharacterRegion::CharacterRegion(Mat img, Config* config)
|
|||||||
timespec startTime;
|
timespec startTime;
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
|
|
||||||
charAnalysis = new CharacterAnalysis(img, config);
|
charAnalysis = new CharacterAnalysis(pipeline_data);
|
||||||
charAnalysis->analyze();
|
charAnalysis->analyze();
|
||||||
|
pipeline_data->plate_inverted = charAnalysis->thresholdsInverted;
|
||||||
|
pipeline_data->plate_mask = charAnalysis->plateMask;
|
||||||
|
|
||||||
if (this->debug && charAnalysis->linePolygon.size() > 0)
|
if (this->debug && charAnalysis->linePolygon.size() > 0)
|
||||||
{
|
{
|
||||||
vector<Mat> tempDash;
|
vector<Mat> tempDash;
|
||||||
for (int z = 0; z < charAnalysis->thresholds.size(); z++)
|
for (int z = 0; z < pipeline_data->thresholds.size(); z++)
|
||||||
{
|
{
|
||||||
Mat tmp(charAnalysis->thresholds[z].size(), charAnalysis->thresholds[z].type());
|
Mat tmp(pipeline_data->thresholds[z].size(), pipeline_data->thresholds[z].type());
|
||||||
charAnalysis->thresholds[z].copyTo(tmp);
|
pipeline_data->thresholds[z].copyTo(tmp);
|
||||||
cvtColor(tmp, tmp, CV_GRAY2BGR);
|
cvtColor(tmp, tmp, CV_GRAY2BGR);
|
||||||
|
|
||||||
tempDash.push_back(tmp);
|
tempDash.push_back(tmp);
|
||||||
@@ -100,10 +102,6 @@ CharacterRegion::~CharacterRegion()
|
|||||||
delete(charAnalysis);
|
delete(charAnalysis);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat CharacterRegion::getPlateMask()
|
|
||||||
{
|
|
||||||
return charAnalysis->plateMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
LineSegment CharacterRegion::getTopLine()
|
LineSegment CharacterRegion::getTopLine()
|
||||||
{
|
{
|
||||||
@@ -140,7 +138,3 @@ LineSegment CharacterRegion::getCharBoxRight()
|
|||||||
return charAnalysis->charBoxRight;
|
return charAnalysis->charBoxRight;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterRegion::thresholdsInverted()
|
|
||||||
{
|
|
||||||
return charAnalysis->thresholdsInverted;
|
|
||||||
}
|
|
||||||
|
@@ -25,23 +25,20 @@
|
|||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
#include "characteranalysis.h"
|
#include "characteranalysis.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "pipeline_data.h"
|
||||||
|
|
||||||
class CharacterRegion
|
class CharacterRegion
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CharacterRegion(cv::Mat img, Config* config);
|
CharacterRegion(PipelineData* pipeline_data);
|
||||||
virtual ~CharacterRegion();
|
virtual ~CharacterRegion();
|
||||||
|
|
||||||
CharacterAnalysis *charAnalysis;
|
|
||||||
|
|
||||||
int confidence;
|
int confidence;
|
||||||
cv::Mat getPlateMask();
|
|
||||||
|
|
||||||
LineSegment getTopLine();
|
LineSegment getTopLine();
|
||||||
LineSegment getBottomLine();
|
LineSegment getBottomLine();
|
||||||
//vector<Point> getLinePolygon();
|
|
||||||
std::vector<cv::Point> getCharArea();
|
std::vector<cv::Point> getCharArea();
|
||||||
|
|
||||||
LineSegment getCharBoxTop();
|
LineSegment getCharBoxTop();
|
||||||
@@ -49,12 +46,12 @@ class CharacterRegion
|
|||||||
LineSegment getCharBoxLeft();
|
LineSegment getCharBoxLeft();
|
||||||
LineSegment getCharBoxRight();
|
LineSegment getCharBoxRight();
|
||||||
|
|
||||||
bool thresholdsInverted();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Config* config;
|
Config* config;
|
||||||
bool debug;
|
bool debug;
|
||||||
|
|
||||||
|
CharacterAnalysis *charAnalysis;
|
||||||
cv::Mat findOuterBoxMask(std::vector<cv::Mat> thresholds, std::vector<std::vector<std::vector<cv::Point> > > allContours, std::vector<std::vector<cv::Vec4i> > allHierarchy);
|
cv::Mat findOuterBoxMask(std::vector<cv::Mat> thresholds, std::vector<std::vector<std::vector<cv::Point> > > allContours, std::vector<std::vector<cv::Vec4i> > allHierarchy);
|
||||||
|
|
||||||
std::vector<bool> filter(cv::Mat img, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy);
|
std::vector<bool> filter(cv::Mat img, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy);
|
||||||
|
@@ -22,12 +22,11 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
LicensePlateCandidate::LicensePlateCandidate(Mat frame, Rect regionOfInterest, Config* config)
|
LicensePlateCandidate::LicensePlateCandidate(PipelineData* pipeline_data)
|
||||||
{
|
{
|
||||||
this->config = config;
|
this->pipeline_data = pipeline_data;
|
||||||
|
this->config = pipeline_data->config;
|
||||||
|
|
||||||
this->frame = frame;
|
|
||||||
this->plateRegion = regionOfInterest;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LicensePlateCandidate::~LicensePlateCandidate()
|
LicensePlateCandidate::~LicensePlateCandidate()
|
||||||
@@ -40,45 +39,41 @@ void LicensePlateCandidate::recognize()
|
|||||||
{
|
{
|
||||||
charSegmenter = NULL;
|
charSegmenter = NULL;
|
||||||
|
|
||||||
this->confidence = 0;
|
pipeline_data->plate_area_confidence = 0;
|
||||||
|
|
||||||
int expandX = round(this->plateRegion.width * 0.20);
|
int expandX = round(this->pipeline_data->regionOfInterest.width * 0.20);
|
||||||
int expandY = round(this->plateRegion.height * 0.15);
|
int expandY = round(this->pipeline_data->regionOfInterest.height * 0.15);
|
||||||
// expand box by 15% in all directions
|
// expand box by 15% in all directions
|
||||||
Rect expandedRegion = expandRect( this->plateRegion, expandX, expandY, frame.cols, frame.rows) ;
|
Rect expandedRegion = expandRect( this->pipeline_data->regionOfInterest, expandX, expandY, this->pipeline_data->grayImg.cols, this->pipeline_data->grayImg.rows) ;
|
||||||
|
|
||||||
Mat plate_bgr = Mat(frame, expandedRegion);
|
pipeline_data->crop_gray = Mat(this->pipeline_data->grayImg, expandedRegion);
|
||||||
resize(plate_bgr, plate_bgr, Size(config->templateWidthPx, config->templateHeightPx));
|
resize(pipeline_data->crop_gray, pipeline_data->crop_gray, Size(config->templateWidthPx, config->templateHeightPx));
|
||||||
|
|
||||||
Mat plate_gray;
|
|
||||||
cvtColor(plate_bgr, plate_gray, CV_BGR2GRAY);
|
|
||||||
|
|
||||||
|
|
||||||
CharacterRegion charRegion(plate_bgr, config);
|
CharacterRegion charRegion(pipeline_data);
|
||||||
|
|
||||||
if (charRegion.confidence > 10)
|
if (charRegion.confidence > 10)
|
||||||
{
|
{
|
||||||
PlateLines plateLines(config);
|
PlateLines plateLines(config);
|
||||||
//Mat boogedy = charRegion.getPlateMask();
|
|
||||||
|
|
||||||
plateLines.processImage(charRegion.getPlateMask(), &charRegion, 1.10);
|
plateLines.processImage(pipeline_data->plate_mask, &charRegion, 1.10);
|
||||||
plateLines.processImage(plate_gray, &charRegion, 0.9);
|
plateLines.processImage(pipeline_data->crop_gray, &charRegion, 0.9);
|
||||||
|
|
||||||
PlateCorners cornerFinder(plate_bgr, &plateLines, &charRegion, config);
|
PlateCorners cornerFinder(pipeline_data->crop_gray, &plateLines, &charRegion, config);
|
||||||
vector<Point> smallPlateCorners = cornerFinder.findPlateCorners();
|
vector<Point> smallPlateCorners = cornerFinder.findPlateCorners();
|
||||||
|
|
||||||
if (cornerFinder.confidence > 0)
|
if (cornerFinder.confidence > 0)
|
||||||
{
|
{
|
||||||
this->plateCorners = transformPointsToOriginalImage(frame, plate_bgr, expandedRegion, smallPlateCorners);
|
pipeline_data->plate_corners = transformPointsToOriginalImage(this->pipeline_data->grayImg, pipeline_data->crop_gray, expandedRegion, smallPlateCorners);
|
||||||
|
|
||||||
this->deskewed = deSkewPlate(frame, this->plateCorners);
|
pipeline_data->crop_gray = deSkewPlate(this->pipeline_data->grayImg, pipeline_data->plate_corners);
|
||||||
|
|
||||||
charSegmenter = new CharacterSegmenter(deskewed, charRegion.thresholdsInverted(), config);
|
charSegmenter = new CharacterSegmenter(pipeline_data);
|
||||||
|
|
||||||
//this->recognizedText = ocr->recognizedText;
|
//this->recognizedText = ocr->recognizedText;
|
||||||
//strcpy(this->recognizedText, ocr.recognizedText);
|
//strcpy(this->recognizedText, ocr.recognizedText);
|
||||||
|
|
||||||
this->confidence = 100;
|
pipeline_data->plate_area_confidence = 100;
|
||||||
}
|
}
|
||||||
charRegion.confidence = 0;
|
charRegion.confidence = 0;
|
||||||
}
|
}
|
||||||
@@ -122,7 +117,7 @@ Mat LicensePlateCandidate::deSkewPlate(Mat inputImage, vector<Point2f> corners)
|
|||||||
width = round(((float) height) * aspect);
|
width = round(((float) height) * aspect);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat deskewed(height, width, frame.type());
|
Mat deskewed(height, width, this->pipeline_data->grayImg.type());
|
||||||
|
|
||||||
// Corners of the destination image
|
// Corners of the destination image
|
||||||
vector<Point2f> quad_pts;
|
vector<Point2f> quad_pts;
|
||||||
|
@@ -34,7 +34,7 @@
|
|||||||
#include "segmentation/charactersegmenter.h"
|
#include "segmentation/charactersegmenter.h"
|
||||||
#include "platecorners.h"
|
#include "platecorners.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "pipeline_data.h"
|
||||||
|
|
||||||
//vector<Rect> getCharacterRegions(Mat frame, vector<Rect> regionsOfInterest);
|
//vector<Rect> getCharacterRegions(Mat frame, vector<Rect> regionsOfInterest);
|
||||||
//vector<RotatedRect> getCharSegmentsBetweenLines(Mat img, vector<vector<Point> > contours, LineSegment top, LineSegment bottom);
|
//vector<RotatedRect> getCharSegmentsBetweenLines(Mat img, vector<vector<Point> > contours, LineSegment top, LineSegment bottom);
|
||||||
@@ -43,24 +43,18 @@ class LicensePlateCandidate
|
|||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LicensePlateCandidate(cv::Mat frame, cv::Rect regionOfInterest, Config* config);
|
LicensePlateCandidate(PipelineData* pipeline_data);
|
||||||
virtual ~LicensePlateCandidate();
|
virtual ~LicensePlateCandidate();
|
||||||
|
|
||||||
float confidence; // 0-100
|
|
||||||
//vector<Point> points; // top-left, top-right, bottom-right, bottom-left
|
|
||||||
std::vector<cv::Point2f> plateCorners;
|
|
||||||
|
|
||||||
void recognize();
|
void recognize();
|
||||||
|
|
||||||
cv::Mat deskewed;
|
|
||||||
CharacterSegmenter* charSegmenter;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
PipelineData* pipeline_data;
|
||||||
Config* config;
|
Config* config;
|
||||||
|
|
||||||
cv::Mat frame;
|
CharacterSegmenter* charSegmenter;
|
||||||
cv::Rect plateRegion;
|
|
||||||
|
|
||||||
cv::Mat filterByCharacterHue(std::vector<std::vector<cv::Point> > charRegionContours);
|
cv::Mat filterByCharacterHue(std::vector<std::vector<cv::Point> > charRegionContours);
|
||||||
std::vector<cv::Point> findPlateCorners(cv::Mat inputImage, PlateLines plateLines, CharacterRegion charRegion); // top-left, top-right, bottom-right, bottom-left
|
std::vector<cv::Point> findPlateCorners(cv::Mat inputImage, PlateLines plateLines, CharacterRegion charRegion); // top-left, top-right, bottom-right, bottom-left
|
||||||
|
@@ -52,7 +52,7 @@ OCR::~OCR()
|
|||||||
delete tesseract;
|
delete tesseract;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OCR::performOCR(vector<Mat> thresholds, vector<Rect> charRegions)
|
void OCR::performOCR(PipelineData* pipeline_data)
|
||||||
{
|
{
|
||||||
timespec startTime;
|
timespec startTime;
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
@@ -60,18 +60,20 @@ void OCR::performOCR(vector<Mat> thresholds, vector<Rect> charRegions)
|
|||||||
postProcessor->clear();
|
postProcessor->clear();
|
||||||
|
|
||||||
// Don't waste time on OCR processing if it is impossible to get sufficient characters
|
// Don't waste time on OCR processing if it is impossible to get sufficient characters
|
||||||
if (charRegions.size() < config->postProcessMinCharacters)
|
if (pipeline_data->charRegions.size() < config->postProcessMinCharacters)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (int i = 0; i < thresholds.size(); i++)
|
for (int i = 0; i < pipeline_data->thresholds.size(); i++)
|
||||||
{
|
{
|
||||||
// Make it black text on white background
|
// Make it black text on white background
|
||||||
bitwise_not(thresholds[i], thresholds[i]);
|
bitwise_not(pipeline_data->thresholds[i], pipeline_data->thresholds[i]);
|
||||||
tesseract->SetImage((uchar*) thresholds[i].data, thresholds[i].size().width, thresholds[i].size().height, thresholds[i].channels(), thresholds[i].step1());
|
tesseract->SetImage((uchar*) pipeline_data->thresholds[i].data,
|
||||||
|
pipeline_data->thresholds[i].size().width, pipeline_data->thresholds[i].size().height,
|
||||||
|
pipeline_data->thresholds[i].channels(), pipeline_data->thresholds[i].step1());
|
||||||
|
|
||||||
for (int j = 0; j < charRegions.size(); j++)
|
for (int j = 0; j < pipeline_data->charRegions.size(); j++)
|
||||||
{
|
{
|
||||||
Rect expandedRegion = expandRect( charRegions[j], 2, 2, thresholds[i].cols, thresholds[i].rows) ;
|
Rect expandedRegion = expandRect( pipeline_data->charRegions[j], 2, 2, pipeline_data->thresholds[i].cols, pipeline_data->thresholds[i].rows) ;
|
||||||
|
|
||||||
tesseract->SetRectangle(expandedRegion.x, expandedRegion.y, expandedRegion.width, expandedRegion.height);
|
tesseract->SetRectangle(expandedRegion.x, expandedRegion.y, expandedRegion.width, expandedRegion.height);
|
||||||
tesseract->Recognize(NULL);
|
tesseract->Recognize(NULL);
|
||||||
|
@@ -26,6 +26,7 @@
|
|||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
#include "postprocess.h"
|
#include "postprocess.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "pipeline_data.h"
|
||||||
|
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
#include "opencv2/imgproc/imgproc.hpp"
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
@@ -39,7 +40,7 @@ class OCR
|
|||||||
OCR(Config* config);
|
OCR(Config* config);
|
||||||
virtual ~OCR();
|
virtual ~OCR();
|
||||||
|
|
||||||
void performOCR(std::vector<cv::Mat> thresholds, std::vector<cv::Rect> charRegions);
|
void performOCR(PipelineData* pipeline_data);
|
||||||
|
|
||||||
PostProcess* postProcessor;
|
PostProcess* postProcessor;
|
||||||
//string recognizedText;
|
//string recognizedText;
|
||||||
|
29
src/openalpr/pipeline_data.cpp
Normal file
29
src/openalpr/pipeline_data.cpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#include "pipeline_data.h"
|
||||||
|
|
||||||
|
using namespace cv;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
PipelineData::PipelineData(Mat colorImage, Rect regionOfInterest, Config* config)
|
||||||
|
{
|
||||||
|
this->colorImg = colorImage;
|
||||||
|
cvtColor(this->colorImg, this->grayImg, CV_BGR2GRAY);
|
||||||
|
|
||||||
|
this->regionOfInterest = regionOfInterest;
|
||||||
|
this->config = config;
|
||||||
|
|
||||||
|
plate_inverted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PipelineData::~PipelineData()
|
||||||
|
{
|
||||||
|
clearThresholds();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PipelineData::clearThresholds()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < thresholds.size(); i++)
|
||||||
|
{
|
||||||
|
thresholds[i].release();
|
||||||
|
}
|
||||||
|
thresholds.clear();
|
||||||
|
}
|
51
src/openalpr/pipeline_data.h
Normal file
51
src/openalpr/pipeline_data.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
|
||||||
|
#ifndef OPENALPR_PIPELINEDATA_H
|
||||||
|
#define OPENALPR_PIPELINEDATA_H
|
||||||
|
|
||||||
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
|
#include "utility.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
class PipelineData
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
PipelineData(cv::Mat colorImage, cv::Rect regionOfInterest, Config* config);
|
||||||
|
virtual ~PipelineData();
|
||||||
|
|
||||||
|
void clearThresholds();
|
||||||
|
|
||||||
|
// Inputs
|
||||||
|
Config* config;
|
||||||
|
|
||||||
|
cv::Mat colorImg;
|
||||||
|
cv::Mat grayImg;
|
||||||
|
cv::Rect regionOfInterest;
|
||||||
|
|
||||||
|
cv::Mat crop_gray;
|
||||||
|
cv::Mat plate_mask;
|
||||||
|
std::vector<cv::Mat> thresholds;
|
||||||
|
|
||||||
|
std::vector<cv::Point2f> plate_corners;
|
||||||
|
|
||||||
|
|
||||||
|
// Outputs
|
||||||
|
bool plate_inverted;
|
||||||
|
|
||||||
|
std::string region_code;
|
||||||
|
float region_confidence;
|
||||||
|
|
||||||
|
|
||||||
|
float plate_area_confidence;
|
||||||
|
|
||||||
|
std::vector<cv::Rect> charRegions;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// OCR
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // OPENALPR_PIPELINEDATA_H
|
@@ -40,10 +40,6 @@ PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, CharacterRegi
|
|||||||
Point bottomPoint = charRegion->getBottomLine().closestPointOnSegmentTo(topPoint);
|
Point bottomPoint = charRegion->getBottomLine().closestPointOnSegmentTo(topPoint);
|
||||||
this->charHeight = distanceBetweenPoints(topPoint, bottomPoint);
|
this->charHeight = distanceBetweenPoints(topPoint, bottomPoint);
|
||||||
|
|
||||||
//this->charHeight = distanceBetweenPoints(charRegion->getCharArea()[0], charRegion->getCharArea()[3]);
|
|
||||||
//this->charHeight = this->charHeight - 2; // Adjust since this height is a box around our char.
|
|
||||||
// Adjust the char height for the difference in size...
|
|
||||||
//this->charHeight = ((float) inputImage.size().height / (float) TEMPLATE_PLATE_HEIGHT) * this->charHeight;
|
|
||||||
|
|
||||||
this->charAngle = angleBetweenPoints(charRegion->getCharArea()[0], charRegion->getCharArea()[1]);
|
this->charAngle = angleBetweenPoints(charRegion->getCharArea()[0], charRegion->getCharArea()[1]);
|
||||||
}
|
}
|
||||||
|
@@ -67,7 +67,7 @@ void PlateLines::processImage(Mat inputImage, CharacterRegion* charRegion, float
|
|||||||
|
|
||||||
// Create a mask that is dilated based on the detected characters
|
// Create a mask that is dilated based on the detected characters
|
||||||
vector<vector<Point> > polygons;
|
vector<vector<Point> > polygons;
|
||||||
polygons.push_back(charRegion->charAnalysis->charArea);
|
polygons.push_back(charRegion->getCharArea());
|
||||||
|
|
||||||
Mat mask = Mat::zeros(inputImage.size(), CV_8U);
|
Mat mask = Mat::zeros(inputImage.size(), CV_8U);
|
||||||
fillPoly(mask, polygons, Scalar(255,255,255));
|
fillPoly(mask, polygons, Scalar(255,255,255));
|
||||||
|
@@ -22,9 +22,10 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* config)
|
CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data)
|
||||||
{
|
{
|
||||||
this->config = config;
|
this->pipeline_data = pipeline_data;
|
||||||
|
this->config = pipeline_data->config;
|
||||||
|
|
||||||
this->confidence = 0;
|
this->confidence = 0;
|
||||||
|
|
||||||
@@ -36,20 +37,18 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
|
|||||||
timespec startTime;
|
timespec startTime;
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
|
|
||||||
Mat img_gray(img.size(), CV_8U);
|
|
||||||
cvtColor( img, img_gray, CV_BGR2GRAY );
|
|
||||||
|
|
||||||
medianBlur(img_gray, img_gray, 3);
|
medianBlur(pipeline_data->crop_gray, pipeline_data->crop_gray, 3);
|
||||||
|
|
||||||
if (invertedColors)
|
if (pipeline_data->plate_inverted)
|
||||||
bitwise_not(img_gray, img_gray);
|
bitwise_not(pipeline_data->crop_gray, pipeline_data->crop_gray);
|
||||||
|
|
||||||
charAnalysis = new CharacterAnalysis(img_gray, config);
|
charAnalysis = new CharacterAnalysis(pipeline_data);
|
||||||
charAnalysis->analyze();
|
charAnalysis->analyze();
|
||||||
|
|
||||||
if (this->config->debugCharSegmenter)
|
if (this->config->debugCharSegmenter)
|
||||||
{
|
{
|
||||||
displayImage(config, "CharacterSegmenter Thresholds", drawImageDashboard(charAnalysis->thresholds, CV_8U, 3));
|
displayImage(config, "CharacterSegmenter Thresholds", drawImageDashboard(pipeline_data->thresholds, CV_8U, 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->config->debugCharSegmenter && charAnalysis->linePolygon.size() > 0)
|
if (this->config->debugCharSegmenter && charAnalysis->linePolygon.size() > 0)
|
||||||
@@ -107,7 +106,7 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
|
|||||||
float avgCharWidth = median(charWidths.data(), charWidths.size());
|
float avgCharWidth = median(charWidths.data(), charWidths.size());
|
||||||
float avgCharHeight = median(charHeights.data(), charHeights.size());
|
float avgCharHeight = median(charHeights.data(), charHeights.size());
|
||||||
|
|
||||||
removeSmallContours(charAnalysis->thresholds, charAnalysis->allContours, avgCharWidth, avgCharHeight);
|
removeSmallContours(pipeline_data->thresholds, charAnalysis->allContours, avgCharWidth, avgCharHeight);
|
||||||
|
|
||||||
// Do the histogram analysis to figure out char regions
|
// Do the histogram analysis to figure out char regions
|
||||||
|
|
||||||
@@ -119,11 +118,11 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
|
|||||||
vector<Rect> allBoxes;
|
vector<Rect> allBoxes;
|
||||||
for (int i = 0; i < charAnalysis->allContours.size(); i++)
|
for (int i = 0; i < charAnalysis->allContours.size(); i++)
|
||||||
{
|
{
|
||||||
Mat histogramMask = Mat::zeros(charAnalysis->thresholds[i].size(), CV_8U);
|
Mat histogramMask = Mat::zeros(pipeline_data->thresholds[i].size(), CV_8U);
|
||||||
|
|
||||||
fillConvexPoly(histogramMask, charAnalysis->linePolygon.data(), charAnalysis->linePolygon.size(), Scalar(255,255,255));
|
fillConvexPoly(histogramMask, charAnalysis->linePolygon.data(), charAnalysis->linePolygon.size(), Scalar(255,255,255));
|
||||||
|
|
||||||
VerticalHistogram vertHistogram(charAnalysis->thresholds[i], histogramMask);
|
VerticalHistogram vertHistogram(pipeline_data->thresholds[i], histogramMask);
|
||||||
|
|
||||||
if (this->config->debugCharSegmenter)
|
if (this->config->debugCharSegmenter)
|
||||||
{
|
{
|
||||||
@@ -173,16 +172,16 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
|
|||||||
}
|
}
|
||||||
|
|
||||||
//ColorFilter colorFilter(img, charAnalysis->getCharacterMask());
|
//ColorFilter colorFilter(img, charAnalysis->getCharacterMask());
|
||||||
vector<Rect> candidateBoxes = getBestCharBoxes(charAnalysis->thresholds[0], allBoxes, medianCharWidth);
|
vector<Rect> candidateBoxes = getBestCharBoxes(pipeline_data->thresholds[0], allBoxes, medianCharWidth);
|
||||||
|
|
||||||
if (this->config->debugCharSegmenter)
|
if (this->config->debugCharSegmenter)
|
||||||
{
|
{
|
||||||
// Setup the dashboard images to show the cleaning filters
|
// Setup the dashboard images to show the cleaning filters
|
||||||
for (int i = 0; i < charAnalysis->thresholds.size(); i++)
|
for (int i = 0; i < pipeline_data->thresholds.size(); i++)
|
||||||
{
|
{
|
||||||
Mat cleanImg = Mat::zeros(charAnalysis->thresholds[i].size(), charAnalysis->thresholds[i].type());
|
Mat cleanImg = Mat::zeros(pipeline_data->thresholds[i].size(), pipeline_data->thresholds[i].type());
|
||||||
Mat boxMask = getCharBoxMask(charAnalysis->thresholds[i], candidateBoxes);
|
Mat boxMask = getCharBoxMask(pipeline_data->thresholds[i], candidateBoxes);
|
||||||
charAnalysis->thresholds[i].copyTo(cleanImg);
|
pipeline_data->thresholds[i].copyTo(cleanImg);
|
||||||
bitwise_and(cleanImg, boxMask, cleanImg);
|
bitwise_and(cleanImg, boxMask, cleanImg);
|
||||||
cvtColor(cleanImg, cleanImg, CV_GRAY2BGR);
|
cvtColor(cleanImg, cleanImg, CV_GRAY2BGR);
|
||||||
|
|
||||||
@@ -194,19 +193,19 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
|
|||||||
|
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
|
|
||||||
filterEdgeBoxes(charAnalysis->thresholds, candidateBoxes, medianCharWidth, avgCharHeight);
|
filterEdgeBoxes(pipeline_data->thresholds, candidateBoxes, medianCharWidth, avgCharHeight);
|
||||||
|
|
||||||
candidateBoxes = filterMostlyEmptyBoxes(charAnalysis->thresholds, candidateBoxes);
|
candidateBoxes = filterMostlyEmptyBoxes(pipeline_data->thresholds, candidateBoxes);
|
||||||
|
|
||||||
candidateBoxes = combineCloseBoxes(candidateBoxes, medianCharWidth);
|
candidateBoxes = combineCloseBoxes(candidateBoxes, medianCharWidth);
|
||||||
|
|
||||||
cleanCharRegions(charAnalysis->thresholds, candidateBoxes);
|
cleanCharRegions(pipeline_data->thresholds, candidateBoxes);
|
||||||
cleanMostlyFullBoxes(charAnalysis->thresholds, candidateBoxes);
|
cleanMostlyFullBoxes(pipeline_data->thresholds, candidateBoxes);
|
||||||
|
|
||||||
//cleanBasedOnColor(thresholds, colorFilter.colorMask, candidateBoxes);
|
//cleanBasedOnColor(thresholds, colorFilter.colorMask, candidateBoxes);
|
||||||
|
|
||||||
candidateBoxes = filterMostlyEmptyBoxes(charAnalysis->thresholds, candidateBoxes);
|
candidateBoxes = filterMostlyEmptyBoxes(pipeline_data->thresholds, candidateBoxes);
|
||||||
this->characters = candidateBoxes;
|
pipeline_data->charRegions = candidateBoxes;
|
||||||
|
|
||||||
if (config->debugTiming)
|
if (config->debugTiming)
|
||||||
{
|
{
|
||||||
@@ -217,7 +216,7 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
|
|||||||
|
|
||||||
if (this->config->debugCharSegmenter)
|
if (this->config->debugCharSegmenter)
|
||||||
{
|
{
|
||||||
Mat imgDash = drawImageDashboard(charAnalysis->thresholds, CV_8U, 3);
|
Mat imgDash = drawImageDashboard(pipeline_data->thresholds, CV_8U, 3);
|
||||||
displayImage(config, "Segmentation after cleaning", imgDash);
|
displayImage(config, "Segmentation after cleaning", imgDash);
|
||||||
|
|
||||||
Mat generalDash = drawImageDashboard(this->imgDbgGeneral, this->imgDbgGeneral[0].type(), 2);
|
Mat generalDash = drawImageDashboard(this->imgDbgGeneral, this->imgDbgGeneral[0].type(), 2);
|
||||||
@@ -491,7 +490,7 @@ vector<Rect> CharacterSegmenter::combineCloseBoxes( vector<Rect> charBoxes, floa
|
|||||||
newCharBoxes.push_back(bigRect);
|
newCharBoxes.push_back(bigRect);
|
||||||
if (this->config->debugCharSegmenter)
|
if (this->config->debugCharSegmenter)
|
||||||
{
|
{
|
||||||
for (int z = 0; z < charAnalysis->thresholds.size(); z++)
|
for (int z = 0; z < pipeline_data->thresholds.size(); z++)
|
||||||
{
|
{
|
||||||
Point center(bigRect.x + bigRect.width / 2, bigRect.y + bigRect.height / 2);
|
Point center(bigRect.x + bigRect.width / 2, bigRect.y + bigRect.height / 2);
|
||||||
RotatedRect rrect(center, Size2f(bigRect.width, bigRect.height + (bigRect.height / 2)), 0);
|
RotatedRect rrect(center, Size2f(bigRect.width, bigRect.height + (bigRect.height / 2)), 0);
|
||||||
@@ -1141,7 +1140,4 @@ Mat CharacterSegmenter::getCharBoxMask(Mat img_threshold, vector<Rect> charBoxes
|
|||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<Mat> CharacterSegmenter::getThresholds()
|
|
||||||
{
|
|
||||||
return charAnalysis->thresholds;
|
|
||||||
}
|
|
||||||
|
@@ -44,17 +44,16 @@ class CharacterSegmenter
|
|||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CharacterSegmenter(cv::Mat img, bool invertedColors, Config* config);
|
CharacterSegmenter(PipelineData* pipeline_data);
|
||||||
virtual ~CharacterSegmenter();
|
virtual ~CharacterSegmenter();
|
||||||
|
|
||||||
std::vector<cv::Rect> characters;
|
|
||||||
int confidence;
|
int confidence;
|
||||||
|
|
||||||
std::vector<cv::Mat> getThresholds();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Config* config;
|
Config* config;
|
||||||
|
PipelineData* pipeline_data;
|
||||||
|
|
||||||
CharacterAnalysis* charAnalysis;
|
CharacterAnalysis* charAnalysis;
|
||||||
|
|
||||||
LineSegment top;
|
LineSegment top;
|
||||||
|
50
src/openalpr/segmentation/segment.cpp
Normal file
50
src/openalpr/segmentation/segment.cpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 New Designs Unlimited, LLC
|
||||||
|
* Opensource Automated License Plate Recognition [http://www.openalpr.com]
|
||||||
|
*
|
||||||
|
* This file is part of OpenAlpr.
|
||||||
|
*
|
||||||
|
* OpenAlpr is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License
|
||||||
|
* version 3 as published by the Free Software Foundation
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "segment.h"
|
||||||
|
|
||||||
|
Segment::Segment(cv::Rect newSegment)
|
||||||
|
{
|
||||||
|
this->segment = newSegment;
|
||||||
|
}
|
||||||
|
|
||||||
|
Segment::~Segment()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Segment::matches(cv::Rect newSegment)
|
||||||
|
{
|
||||||
|
// Compare the two segments with a given leniency
|
||||||
|
const float WIDTH_LENIENCY_MIN = 0.25;
|
||||||
|
const float WIDTH_LENIENCY_MAX = 0.20;
|
||||||
|
|
||||||
|
float left_min = segment.x - (((float)segment.width) * WIDTH_LENIENCY_MIN);
|
||||||
|
float left_max = segment.x + (((float)segment.width) * WIDTH_LENIENCY_MAX);
|
||||||
|
float right_min = (segment.x + segment.width) - (((float)segment.width) * WIDTH_LENIENCY_MIN);
|
||||||
|
float right_max = (segment.x + segment.width) + (((float)segment.width) * WIDTH_LENIENCY_MAX);
|
||||||
|
|
||||||
|
int newSegRight = newSegment.x + newSegment.width;
|
||||||
|
if (newSegment.x >= left_min && newSegment.x <= left_max &&
|
||||||
|
newSegRight >= right_min && newSegRight <= right_max)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
41
src/openalpr/segmentation/segment.h
Normal file
41
src/openalpr/segmentation/segment.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 New Designs Unlimited, LLC
|
||||||
|
* Opensource Automated License Plate Recognition [http://www.openalpr.com]
|
||||||
|
*
|
||||||
|
* This file is part of OpenAlpr.
|
||||||
|
*
|
||||||
|
* OpenAlpr is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License
|
||||||
|
* version 3 as published by the Free Software Foundation
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OPENALPR_SEGMENT_H
|
||||||
|
#define OPENALPR_SEGMENT_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
|
|
||||||
|
class Segment
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
Segment(cv::Rect newSegment);
|
||||||
|
virtual ~Segment();
|
||||||
|
|
||||||
|
cv::Rect segment;
|
||||||
|
|
||||||
|
bool matches(cv::Rect newSegment);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // OPENALPR_SEGMENTATIONGROUP_H
|
49
src/openalpr/segmentation/segmentationgroup.cpp
Normal file
49
src/openalpr/segmentation/segmentationgroup.cpp
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 New Designs Unlimited, LLC
|
||||||
|
* Opensource Automated License Plate Recognition [http://www.openalpr.com]
|
||||||
|
*
|
||||||
|
* This file is part of OpenAlpr.
|
||||||
|
*
|
||||||
|
* OpenAlpr is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License
|
||||||
|
* version 3 as published by the Free Software Foundation
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "segmentationgroup.h"
|
||||||
|
|
||||||
|
SegmentationGroup::SegmentationGroup()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SegmentationGroup::~SegmentationGroup()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SegmentationGroup::add(int segmentID)
|
||||||
|
{
|
||||||
|
this->segmentIDs.push_back(segmentID);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SegmentationGroup::equals(SegmentationGroup otherGroup)
|
||||||
|
{
|
||||||
|
if (segmentIDs.size() != otherGroup.segmentIDs.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < segmentIDs.size(); i++)
|
||||||
|
{
|
||||||
|
if (otherGroup.segmentIDs[i] != segmentIDs[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
50
src/openalpr/segmentation/segmentationgroup.h
Normal file
50
src/openalpr/segmentation/segmentationgroup.h
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 New Designs Unlimited, LLC
|
||||||
|
* Opensource Automated License Plate Recognition [http://www.openalpr.com]
|
||||||
|
*
|
||||||
|
* This file is part of OpenAlpr.
|
||||||
|
*
|
||||||
|
* OpenAlpr is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License
|
||||||
|
* version 3 as published by the Free Software Foundation
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OPENALPR_SEGMENTATIONGROUP_H
|
||||||
|
#define OPENALPR_SEGMENTATIONGROUP_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
|
|
||||||
|
#include "segment.h"
|
||||||
|
|
||||||
|
|
||||||
|
class SegmentationGroup
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
SegmentationGroup();
|
||||||
|
virtual ~SegmentationGroup();
|
||||||
|
|
||||||
|
void add(int segmentID);
|
||||||
|
|
||||||
|
std::vector<int> segmentIDs;
|
||||||
|
|
||||||
|
bool equals(SegmentationGroup otherGroup);
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
float strength; // Debuggin purposes -- how many threshold segmentations match this one perfectly
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // OPENALPR_SEGMENTATIONGROUP_H
|
@@ -43,26 +43,18 @@ StateIdentifier::~StateIdentifier()
|
|||||||
delete featureMatcher;
|
delete featureMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
int StateIdentifier::recognize(Mat img, Rect frame, char* stateCode)
|
|
||||||
{
|
|
||||||
Mat croppedImage = Mat(img, frame);
|
|
||||||
|
|
||||||
return this->recognize(croppedImage, stateCode);
|
// Attempts to recognize the plate. Returns a confidence level. Updates the region code and confidence
|
||||||
}
|
// If region is found, returns true.
|
||||||
// Attempts to recognize the plate. Returns a confidence level. Updates teh "stateCode" variable
|
bool StateIdentifier::recognize(PipelineData* pipeline_data)
|
||||||
// with the value of the country/state
|
|
||||||
int StateIdentifier::recognize(Mat img, char* stateCode)
|
|
||||||
{
|
{
|
||||||
timespec startTime;
|
timespec startTime;
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
|
|
||||||
cvtColor(img, img, CV_BGR2GRAY);
|
Mat plateImg = Mat(pipeline_data->grayImg, pipeline_data->regionOfInterest);
|
||||||
|
|
||||||
resize(img, img, getSizeMaintainingAspect(img, config->stateIdImageWidthPx, config->stateIdimageHeightPx));
|
resize(plateImg, plateImg, getSizeMaintainingAspect(plateImg, config->stateIdImageWidthPx, config->stateIdimageHeightPx));
|
||||||
|
|
||||||
Mat plateImg(img.size(), img.type());
|
|
||||||
//plateImg = equalizeBrightness(img);
|
|
||||||
img.copyTo(plateImg);
|
|
||||||
|
|
||||||
Mat debugImg(plateImg.size(), plateImg.type());
|
Mat debugImg(plateImg.size(), plateImg.type());
|
||||||
plateImg.copyTo(debugImg);
|
plateImg.copyTo(debugImg);
|
||||||
@@ -87,7 +79,11 @@ int StateIdentifier::recognize(Mat img, char* stateCode)
|
|||||||
if (result.haswinner == false)
|
if (result.haswinner == false)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
strcpy(stateCode, result.winner.c_str());
|
pipeline_data->region_code = result.winner;
|
||||||
|
pipeline_data->region_confidence = result.confidence;
|
||||||
return result.confidence;
|
|
||||||
|
if (result.confidence >= 10)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -25,6 +25,7 @@
|
|||||||
#include "featurematcher.h"
|
#include "featurematcher.h"
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "pipeline_data.h"
|
||||||
|
|
||||||
class StateIdentifier
|
class StateIdentifier
|
||||||
{
|
{
|
||||||
@@ -33,8 +34,7 @@ class StateIdentifier
|
|||||||
StateIdentifier(Config* config);
|
StateIdentifier(Config* config);
|
||||||
virtual ~StateIdentifier();
|
virtual ~StateIdentifier();
|
||||||
|
|
||||||
int recognize(cv::Mat img, cv::Rect frame, char* stateCode);
|
bool recognize(PipelineData* pipeline_data);
|
||||||
int recognize(cv::Mat img, char* stateCode);
|
|
||||||
|
|
||||||
//int confidence;
|
//int confidence;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user