mirror of
https://github.com/kerberos-io/openalpr-base.git
synced 2025-10-06 06:46:53 +08:00
Removed (non-working) multithreading implementation. Going to reimplement it differently.
This commit is contained in:
@@ -31,8 +31,6 @@ detection_strictness = 3
|
|||||||
max_detection_input_width = 1280
|
max_detection_input_width = 1280
|
||||||
max_detection_input_height = 720
|
max_detection_input_height = 720
|
||||||
|
|
||||||
multithreading_cores = 1
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
max_plate_angle_degrees = 15
|
max_plate_angle_degrees = 15
|
||||||
|
@@ -102,34 +102,107 @@ AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img, std::vector<cv::Rect
|
|||||||
// Find all the candidate regions
|
// Find all the candidate regions
|
||||||
response.plateRegions = plateDetector->detect(img, regionsOfInterest);
|
response.plateRegions = plateDetector->detect(img, regionsOfInterest);
|
||||||
|
|
||||||
// Get the number of threads specified and make sure the value is sane (cannot be greater than CPU cores or less than 1)
|
queue<PlateRegion> plateQueue;
|
||||||
uint numThreads = config->multithreading_cores;
|
for (uint i = 0; i < response.plateRegions.size(); i++)
|
||||||
if (numThreads > tthread::thread::hardware_concurrency())
|
plateQueue.push(response.plateRegions[i]);
|
||||||
numThreads = tthread::thread::hardware_concurrency();
|
|
||||||
if (numThreads <= 0)
|
|
||||||
numThreads = 1;
|
|
||||||
|
|
||||||
|
|
||||||
PlateDispatcher dispatcher(response.plateRegions, &img,
|
|
||||||
config, stateIdentifier, ocr,
|
|
||||||
topN, detectRegion, defaultRegion);
|
|
||||||
|
|
||||||
// Spawn n threads to process all of the candidate regions and recognize
|
|
||||||
list<tthread::thread*> threads;
|
|
||||||
for (uint i = 0; i < numThreads; i++)
|
|
||||||
{
|
|
||||||
tthread::thread * t = new tthread::thread(plateAnalysisThread, (void *) &dispatcher);
|
|
||||||
threads.push_back(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for all threads to finish
|
while(!plateQueue.empty())
|
||||||
for(list<tthread::thread *>::iterator i = threads.begin(); i != threads.end(); ++ i)
|
|
||||||
{
|
{
|
||||||
tthread::thread* t = *i;
|
PlateRegion plateRegion = plateQueue.front();
|
||||||
t->join();
|
plateQueue.pop();
|
||||||
delete t;
|
|
||||||
|
PipelineData pipeline_data(img, plateRegion.rect, config);
|
||||||
|
|
||||||
|
timespec platestarttime;
|
||||||
|
getTime(&platestarttime);
|
||||||
|
|
||||||
|
LicensePlateCandidate lp(&pipeline_data);
|
||||||
|
|
||||||
|
lp.recognize();
|
||||||
|
|
||||||
|
bool plateDetected = false;
|
||||||
|
if (pipeline_data.plate_area_confidence > 10)
|
||||||
|
{
|
||||||
|
AlprResult plateResult;
|
||||||
|
plateResult.region = defaultRegion;
|
||||||
|
plateResult.regionConfidence = 0;
|
||||||
|
|
||||||
|
for (int pointidx = 0; pointidx < 4; pointidx++)
|
||||||
|
{
|
||||||
|
plateResult.plate_points[pointidx].x = (int) pipeline_data.plate_corners[pointidx].x;
|
||||||
|
plateResult.plate_points[pointidx].y = (int) pipeline_data.plate_corners[pointidx].y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (detectRegion)
|
||||||
|
{
|
||||||
|
char statecode[4];
|
||||||
|
plateResult.regionConfidence = stateIdentifier->recognize(&pipeline_data);
|
||||||
|
if (plateResult.regionConfidence > 0)
|
||||||
|
{
|
||||||
|
plateResult.region = statecode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ocr->performOCR(&pipeline_data);
|
||||||
|
ocr->postProcessor->analyze(plateResult.region, topN);
|
||||||
|
const vector<PPResult> ppResults = ocr->postProcessor->getResults();
|
||||||
|
|
||||||
|
|
||||||
|
int bestPlateIndex = 0;
|
||||||
|
|
||||||
|
for (uint pp = 0; pp < ppResults.size(); pp++)
|
||||||
|
{
|
||||||
|
if (pp >= topN)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Set our "best plate" match to either the first entry, or the first entry with a postprocessor template match
|
||||||
|
if (bestPlateIndex == 0 && ppResults[pp].matchesTemplate)
|
||||||
|
bestPlateIndex = pp;
|
||||||
|
|
||||||
|
if (ppResults[pp].letters.size() >= config->postProcessMinCharacters &&
|
||||||
|
ppResults[pp].letters.size() <= config->postProcessMaxCharacters)
|
||||||
|
{
|
||||||
|
AlprPlate aplate;
|
||||||
|
aplate.characters = ppResults[pp].letters;
|
||||||
|
aplate.overall_confidence = ppResults[pp].totalscore;
|
||||||
|
aplate.matches_template = ppResults[pp].matchesTemplate;
|
||||||
|
plateResult.topNPlates.push_back(aplate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
plateResult.result_count = plateResult.topNPlates.size();
|
||||||
|
|
||||||
|
if (plateResult.topNPlates.size() > 0)
|
||||||
|
plateResult.bestPlate = plateResult.topNPlates[bestPlateIndex];
|
||||||
|
|
||||||
|
timespec plateEndTime;
|
||||||
|
getTime(&plateEndTime);
|
||||||
|
plateResult.processing_time_ms = diffclock(platestarttime, plateEndTime);
|
||||||
|
|
||||||
|
if (plateResult.result_count > 0)
|
||||||
|
{
|
||||||
|
plateDetected = true;
|
||||||
|
response.results.push_back(plateResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!plateDetected)
|
||||||
|
{
|
||||||
|
// Not a valid plate
|
||||||
|
// Check if this plate has any children, if so, send them back up for processing
|
||||||
|
for (uint childidx = 0; childidx < plateRegion.children.size(); childidx++)
|
||||||
|
{
|
||||||
|
plateQueue.push(plateRegion.children[childidx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (config->debugTiming)
|
if (config->debugTiming)
|
||||||
{
|
{
|
||||||
timespec endTime;
|
timespec endTime;
|
||||||
@@ -144,11 +217,11 @@ AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img, std::vector<cv::Rect
|
|||||||
rectangle(img, response.plateRegions[i].rect, Scalar(0, 0, 255), 2);
|
rectangle(img, response.plateRegions[i].rect, Scalar(0, 0, 255), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint i = 0; i < dispatcher.getRecognitionResults().size(); i++)
|
for (uint i = 0; i < response.results.size(); i++)
|
||||||
{
|
{
|
||||||
for (int z = 0; z < 4; z++)
|
for (int z = 0; z < 4; z++)
|
||||||
{
|
{
|
||||||
AlprCoordinate* coords = dispatcher.getRecognitionResults()[i].plate_points;
|
AlprCoordinate* coords = response.results[i].plate_points;
|
||||||
Point p1(coords[z].x, coords[z].y);
|
Point p1(coords[z].x, coords[z].y);
|
||||||
Point p2(coords[(z + 1) % 4].x, coords[(z + 1) % 4].y);
|
Point p2(coords[(z + 1) % 4].x, coords[(z + 1) % 4].y);
|
||||||
line(img, p1, p2, Scalar(255,0,255), 2);
|
line(img, p1, p2, Scalar(255,0,255), 2);
|
||||||
@@ -164,8 +237,6 @@ AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img, std::vector<cv::Rect
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
response.results = dispatcher.getRecognitionResults();
|
|
||||||
|
|
||||||
if (config->debugPauseOnFrame)
|
if (config->debugPauseOnFrame)
|
||||||
{
|
{
|
||||||
// Pause indefinitely until they press a key
|
// Pause indefinitely until they press a key
|
||||||
@@ -197,126 +268,7 @@ std::vector<AlprResult> AlprImpl::recognize(cv::Mat img, std::vector<cv::Rect> r
|
|||||||
AlprFullDetails fullDetails = recognizeFullDetails(img, regionsOfInterest);
|
AlprFullDetails fullDetails = recognizeFullDetails(img, regionsOfInterest);
|
||||||
return fullDetails.results;
|
return fullDetails.results;
|
||||||
}
|
}
|
||||||
void plateAnalysisThread(void* arg)
|
|
||||||
{
|
|
||||||
PlateDispatcher* dispatcher = (PlateDispatcher*) arg;
|
|
||||||
|
|
||||||
if (dispatcher->config->debugGeneral)
|
|
||||||
cout << "Thread: " << tthread::this_thread::get_id() << " Initialized" << endl;
|
|
||||||
|
|
||||||
int loop_count = 0;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
PlateRegion plateRegion;
|
|
||||||
if (dispatcher->nextPlate(&plateRegion) == false)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (dispatcher->config->debugGeneral)
|
|
||||||
cout << "Thread: " << tthread::this_thread::get_id() << " loop " << ++loop_count << endl;
|
|
||||||
|
|
||||||
PipelineData pipeline_data(dispatcher->getImageCopy(), plateRegion.rect, dispatcher->config);
|
|
||||||
|
|
||||||
timespec platestarttime;
|
|
||||||
getTime(&platestarttime);
|
|
||||||
|
|
||||||
LicensePlateCandidate lp(&pipeline_data);
|
|
||||||
|
|
||||||
lp.recognize();
|
|
||||||
|
|
||||||
|
|
||||||
if (pipeline_data.plate_area_confidence <= 10)
|
|
||||||
{
|
|
||||||
// Not a valid plate
|
|
||||||
// Check if this plate has any children, if so, send them back up to the dispatcher for processing
|
|
||||||
for (uint childidx = 0; childidx < plateRegion.children.size(); childidx++)
|
|
||||||
{
|
|
||||||
dispatcher->appendPlate(plateRegion.children[childidx]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AlprResult plateResult;
|
|
||||||
plateResult.region = dispatcher->defaultRegion;
|
|
||||||
plateResult.regionConfidence = 0;
|
|
||||||
|
|
||||||
for (int pointidx = 0; pointidx < 4; pointidx++)
|
|
||||||
{
|
|
||||||
plateResult.plate_points[pointidx].x = (int) pipeline_data.plate_corners[pointidx].x;
|
|
||||||
plateResult.plate_points[pointidx].y = (int) pipeline_data.plate_corners[pointidx].y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dispatcher->detectRegion)
|
|
||||||
{
|
|
||||||
char statecode[4];
|
|
||||||
plateResult.regionConfidence = dispatcher->stateIdentifier->recognize(&pipeline_data);
|
|
||||||
if (plateResult.regionConfidence > 0)
|
|
||||||
{
|
|
||||||
plateResult.region = statecode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Tesseract OCR does not appear to be threadsafe
|
|
||||||
dispatcher->ocrMutex.lock();
|
|
||||||
dispatcher->ocr->performOCR(&pipeline_data);
|
|
||||||
dispatcher->ocr->postProcessor->analyze(plateResult.region, dispatcher->topN);
|
|
||||||
const vector<PPResult> ppResults = dispatcher->ocr->postProcessor->getResults();
|
|
||||||
dispatcher->ocrMutex.unlock();
|
|
||||||
|
|
||||||
int bestPlateIndex = 0;
|
|
||||||
|
|
||||||
for (uint pp = 0; pp < ppResults.size(); pp++)
|
|
||||||
{
|
|
||||||
if (pp >= dispatcher->topN)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Set our "best plate" match to either the first entry, or the first entry with a postprocessor template match
|
|
||||||
if (bestPlateIndex == 0 && ppResults[pp].matchesTemplate)
|
|
||||||
bestPlateIndex = pp;
|
|
||||||
|
|
||||||
if (ppResults[pp].letters.size() >= dispatcher->config->postProcessMinCharacters &&
|
|
||||||
ppResults[pp].letters.size() <= dispatcher->config->postProcessMaxCharacters)
|
|
||||||
{
|
|
||||||
AlprPlate aplate;
|
|
||||||
aplate.characters = ppResults[pp].letters;
|
|
||||||
aplate.overall_confidence = ppResults[pp].totalscore;
|
|
||||||
aplate.matches_template = ppResults[pp].matchesTemplate;
|
|
||||||
plateResult.topNPlates.push_back(aplate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
plateResult.result_count = plateResult.topNPlates.size();
|
|
||||||
|
|
||||||
if (plateResult.topNPlates.size() > 0)
|
|
||||||
plateResult.bestPlate = plateResult.topNPlates[bestPlateIndex];
|
|
||||||
|
|
||||||
timespec plateEndTime;
|
|
||||||
getTime(&plateEndTime);
|
|
||||||
plateResult.processing_time_ms = diffclock(platestarttime, plateEndTime);
|
|
||||||
|
|
||||||
if (plateResult.result_count > 0)
|
|
||||||
{
|
|
||||||
// Synchronized section
|
|
||||||
dispatcher->addResult(plateResult);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (dispatcher->config->debugTiming)
|
|
||||||
{
|
|
||||||
timespec plateEndTime;
|
|
||||||
getTime(&plateEndTime);
|
|
||||||
cout << "Thread: " << tthread::this_thread::get_id() << " Finished loop " << loop_count << " in " << diffclock(platestarttime, plateEndTime) << "ms." << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dispatcher->config->debugGeneral)
|
|
||||||
cout << "Thread: " << tthread::this_thread::get_id() << " Complete" << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<cv::Rect> AlprImpl::convertRects(std::vector<AlprRegionOfInterest> regionsOfInterest)
|
std::vector<cv::Rect> AlprImpl::convertRects(std::vector<AlprRegionOfInterest> regionsOfInterest)
|
||||||
{
|
{
|
||||||
|
@@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
#include "alpr.h"
|
#include "alpr.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@@ -43,8 +45,6 @@
|
|||||||
|
|
||||||
#include <opencv2/core/core.hpp>
|
#include <opencv2/core/core.hpp>
|
||||||
|
|
||||||
|
|
||||||
#include "support/tinythread.h"
|
|
||||||
#include "support/platform.h"
|
#include "support/platform.h"
|
||||||
|
|
||||||
#define DEFAULT_TOPN 25
|
#define DEFAULT_TOPN 25
|
||||||
@@ -101,86 +101,6 @@ class AlprImpl
|
|||||||
cJSON* createJsonObj(const AlprResult* result);
|
cJSON* createJsonObj(const AlprResult* result);
|
||||||
};
|
};
|
||||||
|
|
||||||
class PlateDispatcher
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PlateDispatcher(std::vector<PlateRegion> plateRegions, cv::Mat* image,
|
|
||||||
Config* config,
|
|
||||||
StateIdentifier* stateIdentifier,
|
|
||||||
OCR* ocr,
|
|
||||||
int topN, bool detectRegion, std::string defaultRegion)
|
|
||||||
{
|
|
||||||
this->plateRegions = plateRegions;
|
|
||||||
this->frame = image;
|
|
||||||
|
|
||||||
this->config = config;
|
|
||||||
this->stateIdentifier = stateIdentifier;
|
|
||||||
this->ocr = ocr;
|
|
||||||
this->topN = topN;
|
|
||||||
this->detectRegion = detectRegion;
|
|
||||||
this->defaultRegion = defaultRegion;
|
|
||||||
}
|
|
||||||
|
|
||||||
cv::Mat getImageCopy()
|
|
||||||
{
|
|
||||||
tthread::lock_guard<tthread::mutex> guard(mMutex);
|
|
||||||
|
|
||||||
cv::Mat img(this->frame->size(), this->frame->type());
|
|
||||||
this->frame->copyTo(img);
|
|
||||||
|
|
||||||
return img;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool nextPlate(PlateRegion* plateRegion)
|
|
||||||
{
|
|
||||||
tthread::lock_guard<tthread::mutex> guard(mMutex);
|
|
||||||
|
|
||||||
if (plateRegions.size() == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
*plateRegion = plateRegions[plateRegions.size() - 1];
|
|
||||||
plateRegions.pop_back();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void appendPlate(PlateRegion plate)
|
|
||||||
{
|
|
||||||
tthread::lock_guard<tthread::mutex> guard(mMutex);
|
|
||||||
|
|
||||||
plateRegions.push_back(plate);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addResult(AlprResult recognitionResult)
|
|
||||||
{
|
|
||||||
tthread::lock_guard<tthread::mutex> guard(mMutex);
|
|
||||||
recognitionResults.push_back(recognitionResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<AlprResult> getRecognitionResults()
|
|
||||||
{
|
|
||||||
return recognitionResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
StateIdentifier* stateIdentifier;
|
|
||||||
OCR* ocr;
|
|
||||||
Config* config;
|
|
||||||
|
|
||||||
uint topN;
|
|
||||||
bool detectRegion;
|
|
||||||
std::string defaultRegion;
|
|
||||||
|
|
||||||
tthread::mutex ocrMutex;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
tthread::mutex mMutex;
|
|
||||||
|
|
||||||
cv::Mat* frame;
|
|
||||||
std::vector<PlateRegion> plateRegions;
|
|
||||||
std::vector<AlprResult> recognitionResults;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // OPENALPR_ALPRIMPL_H
|
#endif // OPENALPR_ALPRIMPL_H
|
@@ -127,8 +127,6 @@ 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");
|
||||||
|
|
||||||
multithreading_cores = getInt("common", "multithreading_cores", 1);
|
|
||||||
|
|
||||||
detection_iteration_increase = getFloat("common", "detection_iteration_increase", 1.1);
|
detection_iteration_increase = getFloat("common", "detection_iteration_increase", 1.1);
|
||||||
detectionStrictness = getInt("common", "detection_strictness", 3);
|
detectionStrictness = getInt("common", "detection_strictness", 3);
|
||||||
maxPlateWidthPercent = getFloat("common", "max_plate_width_percent", 100);
|
maxPlateWidthPercent = getFloat("common", "max_plate_width_percent", 100);
|
||||||
|
@@ -45,8 +45,6 @@ class Config
|
|||||||
|
|
||||||
std::string country;
|
std::string country;
|
||||||
|
|
||||||
int multithreading_cores;
|
|
||||||
|
|
||||||
float detection_iteration_increase;
|
float detection_iteration_increase;
|
||||||
int detectionStrictness;
|
int detectionStrictness;
|
||||||
float maxPlateWidthPercent;
|
float maxPlateWidthPercent;
|
||||||
|
Reference in New Issue
Block a user