mirror of
https://github.com/kerberos-io/openalpr-base.git
synced 2025-10-06 02:06:57 +08:00
Added per-line values for character height.
Helps support multiline plates with different heights on each line
This commit is contained in:
@@ -33,7 +33,7 @@ namespace alpr
|
|||||||
float getFloat(CSimpleIniA* ini, std::string section, std::string key, float defaultValue);
|
float getFloat(CSimpleIniA* ini, std::string section, std::string key, float defaultValue);
|
||||||
std::string getString(CSimpleIniA* ini, std::string section, std::string key, std::string defaultValue);
|
std::string getString(CSimpleIniA* ini, std::string section, std::string key, std::string defaultValue);
|
||||||
bool getBoolean(CSimpleIniA* ini, std::string section, std::string key, bool defaultValue);
|
bool getBoolean(CSimpleIniA* ini, std::string section, std::string key, bool defaultValue);
|
||||||
|
std::vector<float> getAllFloats(CSimpleIniA* ini, string section, string key);
|
||||||
|
|
||||||
Config::Config(const std::string country, const std::string config_file, const std::string runtime_dir)
|
Config::Config(const std::string country, const std::string config_file, const std::string runtime_dir)
|
||||||
{
|
{
|
||||||
@@ -214,6 +214,7 @@ namespace alpr
|
|||||||
void Config::loadCountryValues(string configFile, string country)
|
void Config::loadCountryValues(string configFile, string country)
|
||||||
{
|
{
|
||||||
CSimpleIniA iniObj;
|
CSimpleIniA iniObj;
|
||||||
|
iniObj.SetMultiKey(true);
|
||||||
iniObj.LoadFile(configFile.c_str());
|
iniObj.LoadFile(configFile.c_str());
|
||||||
CSimpleIniA* ini = &iniObj;
|
CSimpleIniA* ini = &iniObj;
|
||||||
|
|
||||||
@@ -243,10 +244,23 @@ namespace alpr
|
|||||||
plateWidthMM = getFloat(ini, "", "plate_width_mm", 100);
|
plateWidthMM = getFloat(ini, "", "plate_width_mm", 100);
|
||||||
plateHeightMM = getFloat(ini, "", "plate_height_mm", 100);
|
plateHeightMM = getFloat(ini, "", "plate_height_mm", 100);
|
||||||
|
|
||||||
charHeightMM = getFloat(ini, "", "char_height_mm", 100);
|
charHeightMM = getAllFloats(ini, "", "char_height_mm");
|
||||||
charWidthMM = getFloat(ini, "", "char_width_mm", 100);
|
charWidthMM = getAllFloats(ini, "", "char_width_mm");
|
||||||
|
|
||||||
|
// Compute the average char height/widths
|
||||||
|
avgCharHeightMM = 0;
|
||||||
|
avgCharWidthMM = 0;
|
||||||
|
for (unsigned int i = 0; i < charHeightMM.size(); i++)
|
||||||
|
{
|
||||||
|
avgCharHeightMM += charHeightMM[i];
|
||||||
|
avgCharWidthMM += charWidthMM[i];
|
||||||
|
}
|
||||||
|
avgCharHeightMM /= charHeightMM.size();
|
||||||
|
avgCharWidthMM /= charHeightMM.size();
|
||||||
|
|
||||||
charWhitespaceTopMM = getFloat(ini, "", "char_whitespace_top_mm", 100);
|
charWhitespaceTopMM = getFloat(ini, "", "char_whitespace_top_mm", 100);
|
||||||
charWhitespaceBotMM = getFloat(ini, "", "char_whitespace_bot_mm", 100);
|
charWhitespaceBotMM = getFloat(ini, "", "char_whitespace_bot_mm", 100);
|
||||||
|
charWhitespaceBetweenLinesMM = getFloat(ini, "", "char_whitespace_between_lines_mm", 5);
|
||||||
|
|
||||||
templateWidthPx = getInt(ini, "", "template_max_width_px", 100);
|
templateWidthPx = getInt(ini, "", "template_max_width_px", 100);
|
||||||
templateHeightPx = getInt(ini, "", "template_max_height_px", 100);
|
templateHeightPx = getInt(ini, "", "template_max_height_px", 100);
|
||||||
@@ -358,6 +372,27 @@ namespace alpr
|
|||||||
float val = atof(pszValue);
|
float val = atof(pszValue);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<float> getAllFloats(CSimpleIniA* ini, string section, string key)
|
||||||
|
{
|
||||||
|
CSimpleIniA::TNamesDepend values;
|
||||||
|
|
||||||
|
ini->GetAllValues(section.c_str(), key.c_str(), values);
|
||||||
|
|
||||||
|
// sort the values into the original load order
|
||||||
|
values.sort(CSimpleIniA::Entry::LoadOrder());
|
||||||
|
|
||||||
|
std::vector<float> response;
|
||||||
|
|
||||||
|
// output all of the items
|
||||||
|
CSimpleIniA::TNamesDepend::const_iterator i;
|
||||||
|
for (i = values.begin(); i != values.end(); ++i) {
|
||||||
|
response.push_back(atof(i->pItem));
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
int getInt(CSimpleIniA* ini, string section, string key, int defaultValue)
|
int getInt(CSimpleIniA* ini, 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*/);
|
||||||
|
@@ -70,10 +70,15 @@ namespace alpr
|
|||||||
float plateWidthMM;
|
float plateWidthMM;
|
||||||
float plateHeightMM;
|
float plateHeightMM;
|
||||||
|
|
||||||
float charHeightMM;
|
std::vector<float> charHeightMM;
|
||||||
float charWidthMM;
|
std::vector<float> charWidthMM;
|
||||||
|
|
||||||
|
float avgCharHeightMM;
|
||||||
|
float avgCharWidthMM;
|
||||||
|
|
||||||
float charWhitespaceTopMM;
|
float charWhitespaceTopMM;
|
||||||
float charWhitespaceBotMM;
|
float charWhitespaceBotMM;
|
||||||
|
float charWhitespaceBetweenLinesMM;
|
||||||
|
|
||||||
int templateWidthPx;
|
int templateWidthPx;
|
||||||
int templateHeightPx;
|
int templateHeightPx;
|
||||||
|
@@ -179,7 +179,7 @@ for (int i = 0; i < rects.size(); i++) {
|
|||||||
int numBlobs = plateBlobs.size();
|
int numBlobs = plateBlobs.size();
|
||||||
int numBlobsInv = plateBlobsInv.size();
|
int numBlobsInv = plateBlobsInv.size();
|
||||||
|
|
||||||
float idealAspect = config->charWidthMM / config->charHeightMM;
|
float idealAspect = config->avgCharWidthMM / config->avgCharHeightMM;
|
||||||
for (int j = 0; j < numBlobs; j++) {
|
for (int j = 0; j < numBlobs; j++) {
|
||||||
cv::Rect r0 = cv::boundingRect(cv::Mat(plateBlobs[j]));
|
cv::Rect r0 = cv::boundingRect(cv::Mat(plateBlobs[j]));
|
||||||
|
|
||||||
|
@@ -48,10 +48,11 @@ namespace alpr
|
|||||||
// If it's a nice, long segment, then guess the correct box based on character height/position
|
// If it's a nice, long segment, then guess the correct box based on character height/position
|
||||||
if (tlc.longerSegment.length > tlc.charHeight * 3)
|
if (tlc.longerSegment.length > tlc.charHeight * 3)
|
||||||
{
|
{
|
||||||
float charHeightToPlateWidthRatio = pipeline_data->config->plateWidthMM / pipeline_data->config->charHeightMM;
|
|
||||||
|
float charHeightToPlateWidthRatio = pipeline_data->config->plateWidthMM / pipeline_data->config->avgCharHeightMM;
|
||||||
float idealPixelWidth = tlc.charHeight * (charHeightToPlateWidthRatio * 1.03); // Add 3% so we don't clip any characters
|
float idealPixelWidth = tlc.charHeight * (charHeightToPlateWidthRatio * 1.03); // Add 3% so we don't clip any characters
|
||||||
|
|
||||||
float charHeightToPlateHeightRatio = pipeline_data->config->plateHeightMM / pipeline_data->config->charHeightMM;
|
float charHeightToPlateHeightRatio = pipeline_data->config->plateHeightMM / pipeline_data->config->avgCharHeightMM;
|
||||||
float idealPixelHeight = tlc.charHeight * charHeightToPlateHeightRatio;
|
float idealPixelHeight = tlc.charHeight * charHeightToPlateHeightRatio;
|
||||||
|
|
||||||
|
|
||||||
@@ -76,7 +77,6 @@ namespace alpr
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
//cout << "HEYOOO!" << endl;
|
|
||||||
int expandX = (int) ((float) pipeline_data->crop_gray.cols) * 0.15f;
|
int expandX = (int) ((float) pipeline_data->crop_gray.cols) * 0.15f;
|
||||||
int expandY = (int) ((float) pipeline_data->crop_gray.rows) * 0.15f;
|
int expandY = (int) ((float) pipeline_data->crop_gray.rows) * 0.15f;
|
||||||
int w = pipeline_data->crop_gray.cols;
|
int w = pipeline_data->crop_gray.cols;
|
||||||
|
@@ -138,7 +138,7 @@ namespace alpr
|
|||||||
LineSegment right;
|
LineSegment right;
|
||||||
|
|
||||||
|
|
||||||
float charHeightToPlateWidthRatio = pipelineData->config->plateWidthMM / pipelineData->config->charHeightMM;
|
float charHeightToPlateWidthRatio = pipelineData->config->plateWidthMM / pipelineData->config->avgCharHeightMM;
|
||||||
float idealPixelWidth = tlc.charHeight * (charHeightToPlateWidthRatio * 1.03); // Add 3% so we don't clip any characters
|
float idealPixelWidth = tlc.charHeight * (charHeightToPlateWidthRatio * 1.03); // Add 3% so we don't clip any characters
|
||||||
|
|
||||||
float confidenceDiff = 0;
|
float confidenceDiff = 0;
|
||||||
@@ -242,7 +242,7 @@ namespace alpr
|
|||||||
LineSegment top;
|
LineSegment top;
|
||||||
LineSegment bottom;
|
LineSegment bottom;
|
||||||
|
|
||||||
float charHeightToPlateHeightRatio = pipelineData->config->plateHeightMM / pipelineData->config->charHeightMM;
|
float charHeightToPlateHeightRatio = pipelineData->config->plateHeightMM / pipelineData->config->avgCharHeightMM;
|
||||||
float idealPixelHeight = tlc.charHeight * charHeightToPlateHeightRatio;
|
float idealPixelHeight = tlc.charHeight * charHeightToPlateHeightRatio;
|
||||||
|
|
||||||
float confidenceDiff = 0;
|
float confidenceDiff = 0;
|
||||||
@@ -304,7 +304,7 @@ namespace alpr
|
|||||||
// Get the height difference
|
// Get the height difference
|
||||||
|
|
||||||
float heightRatio = tlc.charHeight / plateHeightPx;
|
float heightRatio = tlc.charHeight / plateHeightPx;
|
||||||
float idealHeightRatio = (pipelineData->config->charHeightMM / pipelineData->config->plateHeightMM);
|
float idealHeightRatio = (pipelineData->config->avgCharHeightMM / pipelineData->config->plateHeightMM);
|
||||||
float heightRatioDiff = abs(heightRatio - idealHeightRatio);
|
float heightRatioDiff = abs(heightRatio - idealHeightRatio);
|
||||||
|
|
||||||
scoreKeeper.setScore("SCORING_PLATEHEIGHT_WEIGHT", heightRatioDiff, SCORING_PLATEHEIGHT_WEIGHT);
|
scoreKeeper.setScore("SCORING_PLATEHEIGHT_WEIGHT", heightRatioDiff, SCORING_PLATEHEIGHT_WEIGHT);
|
||||||
|
@@ -70,7 +70,7 @@ namespace alpr
|
|||||||
this->bottom = pipeline_data->textLines[lineidx].bottomLine;
|
this->bottom = pipeline_data->textLines[lineidx].bottomLine;
|
||||||
|
|
||||||
float avgCharHeight = pipeline_data->textLines[lineidx].lineHeight;
|
float avgCharHeight = pipeline_data->textLines[lineidx].lineHeight;
|
||||||
float height_to_width_ratio = pipeline_data->config->charHeightMM / pipeline_data->config->charWidthMM;
|
float height_to_width_ratio = pipeline_data->config->charHeightMM[lineidx] / pipeline_data->config->charWidthMM[lineidx];
|
||||||
float avgCharWidth = avgCharHeight / height_to_width_ratio;
|
float avgCharWidth = avgCharHeight / height_to_width_ratio;
|
||||||
|
|
||||||
removeSmallContours(pipeline_data->thresholds, avgCharHeight, pipeline_data->textLines[lineidx]);
|
removeSmallContours(pipeline_data->thresholds, avgCharHeight, pipeline_data->textLines[lineidx]);
|
||||||
|
@@ -338,7 +338,19 @@ namespace alpr
|
|||||||
// 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;
|
// For multiline plates, we want to target the biggest line for character analysis, since it should be easier to spot.
|
||||||
|
float larger_char_height_mm = 0;
|
||||||
|
float larger_char_width_mm = 0;
|
||||||
|
for (unsigned int i = 0; i < config->charHeightMM.size(); i++)
|
||||||
|
{
|
||||||
|
if (config->charHeightMM[i] > larger_char_height_mm)
|
||||||
|
{
|
||||||
|
larger_char_height_mm = config->charHeightMM[i];
|
||||||
|
larger_char_width_mm = config->charWidthMM[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float idealAspect=larger_char_width_mm / larger_char_height_mm;
|
||||||
float aspecttolerance=0.25;
|
float aspecttolerance=0.25;
|
||||||
|
|
||||||
|
|
||||||
@@ -619,31 +631,6 @@ namespace alpr
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterAnalysis::verifySize(Mat r, float minHeightPx, float maxHeightPx)
|
|
||||||
{
|
|
||||||
//Char sizes 45x90
|
|
||||||
float aspect=config->charWidthMM / config->charHeightMM;
|
|
||||||
float charAspect= (float)r.cols/(float)r.rows;
|
|
||||||
float error=0.35;
|
|
||||||
//float minHeight=TEMPLATE_PLATE_HEIGHT * .35;
|
|
||||||
//float maxHeight=TEMPLATE_PLATE_HEIGHT * .65;
|
|
||||||
//We have a different aspect ratio for number 1, and it can be ~0.2
|
|
||||||
float minAspect=0.2;
|
|
||||||
float maxAspect=aspect+aspect*error;
|
|
||||||
//area of pixels
|
|
||||||
float area=countNonZero(r);
|
|
||||||
//bb area
|
|
||||||
float bbArea=r.cols*r.rows;
|
|
||||||
//% of pixel in area
|
|
||||||
float percPixels=area/bbArea;
|
|
||||||
|
|
||||||
//if(DEBUG)
|
|
||||||
//cout << "Aspect: "<< aspect << " ["<< minAspect << "," << maxAspect << "] " << "Area "<< percPixels <<" Char aspect " << charAspect << " Height char "<< r.rows << "\n";
|
|
||||||
if(percPixels < 0.8 && charAspect > minAspect && charAspect < maxAspect && r.rows >= minHeightPx && r.rows < maxHeightPx)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<Point> CharacterAnalysis::getCharArea(LineSegment topLine, LineSegment bottomLine)
|
vector<Point> CharacterAnalysis::getCharArea(LineSegment topLine, LineSegment bottomLine)
|
||||||
{
|
{
|
||||||
|
@@ -65,8 +65,6 @@ namespace alpr
|
|||||||
std::vector<cv::Point> getCharArea(LineSegment topLine, LineSegment bottomLine);
|
std::vector<cv::Point> getCharArea(LineSegment topLine, LineSegment bottomLine);
|
||||||
void filterBetweenLines(cv::Mat img, TextContours& textContours, std::vector<TextLine> textLines );
|
void filterBetweenLines(cv::Mat img, TextContours& textContours, std::vector<TextLine> textLines );
|
||||||
|
|
||||||
bool verifySize(cv::Mat r, float minHeightPx, float maxHeightPx);
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user