diff --git a/src/openalpr/config.cpp b/src/openalpr/config.cpp index 8c93106..7186f46 100644 --- a/src/openalpr/config.cpp +++ b/src/openalpr/config.cpp @@ -33,7 +33,7 @@ namespace alpr 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); bool getBoolean(CSimpleIniA* ini, std::string section, std::string key, bool defaultValue); - + std::vector getAllFloats(CSimpleIniA* ini, string section, string key); 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) { CSimpleIniA iniObj; + iniObj.SetMultiKey(true); iniObj.LoadFile(configFile.c_str()); CSimpleIniA* ini = &iniObj; @@ -243,10 +244,23 @@ namespace alpr plateWidthMM = getFloat(ini, "", "plate_width_mm", 100); plateHeightMM = getFloat(ini, "", "plate_height_mm", 100); - charHeightMM = getFloat(ini, "", "char_height_mm", 100); - charWidthMM = getFloat(ini, "", "char_width_mm", 100); + charHeightMM = getAllFloats(ini, "", "char_height_mm"); + 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); 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); templateHeightPx = getInt(ini, "", "template_max_height_px", 100); @@ -358,6 +372,27 @@ namespace alpr float val = atof(pszValue); return val; } + + std::vector 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 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) { const char * pszValue = ini->GetValue(section.c_str(), key.c_str(), NULL /*default*/); diff --git a/src/openalpr/config.h b/src/openalpr/config.h index 268ffde..72deb78 100644 --- a/src/openalpr/config.h +++ b/src/openalpr/config.h @@ -70,10 +70,15 @@ namespace alpr float plateWidthMM; float plateHeightMM; - float charHeightMM; - float charWidthMM; + std::vector charHeightMM; + std::vector charWidthMM; + + float avgCharHeightMM; + float avgCharWidthMM; + float charWhitespaceTopMM; float charWhitespaceBotMM; + float charWhitespaceBetweenLinesMM; int templateWidthPx; int templateHeightPx; diff --git a/src/openalpr/detection/detectormorph.cpp b/src/openalpr/detection/detectormorph.cpp index 0324aa4..5e99495 100644 --- a/src/openalpr/detection/detectormorph.cpp +++ b/src/openalpr/detection/detectormorph.cpp @@ -179,7 +179,7 @@ for (int i = 0; i < rects.size(); i++) { int numBlobs = plateBlobs.size(); int numBlobsInv = plateBlobsInv.size(); - float idealAspect = config->charWidthMM / config->charHeightMM; + float idealAspect = config->avgCharWidthMM / config->avgCharHeightMM; for (int j = 0; j < numBlobs; j++) { cv::Rect r0 = cv::boundingRect(cv::Mat(plateBlobs[j])); diff --git a/src/openalpr/edges/edgefinder.cpp b/src/openalpr/edges/edgefinder.cpp index 34b256a..80105de 100644 --- a/src/openalpr/edges/edgefinder.cpp +++ b/src/openalpr/edges/edgefinder.cpp @@ -48,10 +48,11 @@ namespace alpr // If it's a nice, long segment, then guess the correct box based on character height/position 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 charHeightToPlateHeightRatio = pipeline_data->config->plateHeightMM / pipeline_data->config->charHeightMM; + float charHeightToPlateHeightRatio = pipeline_data->config->plateHeightMM / pipeline_data->config->avgCharHeightMM; float idealPixelHeight = tlc.charHeight * charHeightToPlateHeightRatio; @@ -76,7 +77,6 @@ namespace alpr else { - //cout << "HEYOOO!" << endl; int expandX = (int) ((float) pipeline_data->crop_gray.cols) * 0.15f; int expandY = (int) ((float) pipeline_data->crop_gray.rows) * 0.15f; int w = pipeline_data->crop_gray.cols; diff --git a/src/openalpr/edges/platecorners.cpp b/src/openalpr/edges/platecorners.cpp index 24d50de..a67f969 100644 --- a/src/openalpr/edges/platecorners.cpp +++ b/src/openalpr/edges/platecorners.cpp @@ -138,7 +138,7 @@ namespace alpr 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 confidenceDiff = 0; @@ -242,7 +242,7 @@ namespace alpr LineSegment top; LineSegment bottom; - float charHeightToPlateHeightRatio = pipelineData->config->plateHeightMM / pipelineData->config->charHeightMM; + float charHeightToPlateHeightRatio = pipelineData->config->plateHeightMM / pipelineData->config->avgCharHeightMM; float idealPixelHeight = tlc.charHeight * charHeightToPlateHeightRatio; float confidenceDiff = 0; @@ -304,7 +304,7 @@ namespace alpr // Get the height difference 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); scoreKeeper.setScore("SCORING_PLATEHEIGHT_WEIGHT", heightRatioDiff, SCORING_PLATEHEIGHT_WEIGHT); diff --git a/src/openalpr/segmentation/charactersegmenter.cpp b/src/openalpr/segmentation/charactersegmenter.cpp index 7a14956..383c73b 100644 --- a/src/openalpr/segmentation/charactersegmenter.cpp +++ b/src/openalpr/segmentation/charactersegmenter.cpp @@ -70,7 +70,7 @@ namespace alpr this->bottom = pipeline_data->textLines[lineidx].bottomLine; 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; removeSmallContours(pipeline_data->thresholds, avgCharHeight, pipeline_data->textLines[lineidx]); diff --git a/src/openalpr/textdetection/characteranalysis.cpp b/src/openalpr/textdetection/characteranalysis.cpp index 59bfbc4..bb46af9 100644 --- a/src/openalpr/textdetection/characteranalysis.cpp +++ b/src/openalpr/textdetection/characteranalysis.cpp @@ -338,7 +338,19 @@ namespace alpr // 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) { - 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; @@ -619,31 +631,6 @@ namespace alpr 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 CharacterAnalysis::getCharArea(LineSegment topLine, LineSegment bottomLine) { diff --git a/src/openalpr/textdetection/characteranalysis.h b/src/openalpr/textdetection/characteranalysis.h index 043d5b8..78cb23c 100644 --- a/src/openalpr/textdetection/characteranalysis.h +++ b/src/openalpr/textdetection/characteranalysis.h @@ -65,8 +65,6 @@ namespace alpr std::vector getCharArea(LineSegment topLine, LineSegment bottomLine); void filterBetweenLines(cv::Mat img, TextContours& textContours, std::vector textLines ); - bool verifySize(cv::Mat r, float minHeightPx, float maxHeightPx); - };