Added per-line values for character height.

Helps support multiline plates with different heights on each line
This commit is contained in:
Matt Hill
2015-09-20 21:11:14 -04:00
parent 75c6784369
commit 8d765d481d
8 changed files with 66 additions and 41 deletions

View File

@@ -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*/);

View File

@@ -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;

View File

@@ -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]));

View File

@@ -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;

View File

@@ -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);

View File

@@ -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]);

View File

@@ -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)
{ {

View File

@@ -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);
}; };