diff --git a/src/misc_utilities/benchmark/benchmark.cpp b/src/misc_utilities/benchmark/benchmark.cpp index 106689d..d09efce 100644 --- a/src/misc_utilities/benchmark/benchmark.cpp +++ b/src/misc_utilities/benchmark/benchmark.cpp @@ -130,7 +130,7 @@ int main( int argc, const char** argv ) } CharacterSegmenter charSegmenter(&pipeline_data); - ocr->performOCR(charSegmenter.getThresholds(), charSegmenter.characters); + ocr->performOCR(&pipeline_data); ocr->postProcessor->analyze(statecode, 25); cout << files[i] << "," << statecode << "," << ocr->postProcessor->bestChars << endl; @@ -237,7 +237,7 @@ int main( int argc, const char** argv ) lpAnalysisPositiveTimes.push_back(analysisTime); getTime(&startTime); - ocr.performOCR(lp.charSegmenter->getThresholds(), lp.charSegmenter->characters); + ocr.performOCR(&pipeline_data); getTime(&endTime); double ocrTime = diffclock(startTime, endTime); cout << "\tRegion " << z << ": OCR time: " << ocrTime << "ms." << endl; diff --git a/src/misc_utilities/classifychars.cpp b/src/misc_utilities/classifychars.cpp index b41041e..09fc44a 100644 --- a/src/misc_utilities/classifychars.cpp +++ b/src/misc_utilities/classifychars.cpp @@ -151,12 +151,12 @@ int main( int argc, const char** argv ) //ocr.cleanCharRegions(charSegmenter.thresholds, charSegmenter.characters); - ocr.performOCR(charSegmenter.getThresholds(), charSegmenter.characters); + ocr.performOCR(&pipeline_data); ocr.postProcessor->analyze(statecodestr, 25); cout << "OCR results: " << ocr.postProcessor->bestChars << endl; - vector selectedBoxes(charSegmenter.getThresholds().size()); - for (int z = 0; z < charSegmenter.getThresholds().size(); z++) + vector selectedBoxes(pipeline_data.thresholds.size()); + for (int z = 0; z < pipeline_data.thresholds.size(); z++) selectedBoxes[z] = false; int curDashboardSelection = 0; @@ -166,7 +166,7 @@ int main( int argc, const char** argv ) for (int z = 0; z < charSegmenter.characters.size(); z++) humanInputs[z] = ' '; - showDashboard(charSegmenter.getThresholds(), selectedBoxes, 0); + showDashboard(pipeline_data.thresholds, selectedBoxes, 0); char waitkey = (char) waitKey(50); @@ -176,50 +176,50 @@ int main( int argc, const char** argv ) { if (curDashboardSelection > 0) curDashboardSelection--; - showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection); + showDashboard(pipeline_data.thresholds, selectedBoxes, curDashboardSelection); } else if (waitkey == RIGHT_ARROW_KEY) // right arrow key { - if (curDashboardSelection < charSegmenter.getThresholds().size() - 1) + if (curDashboardSelection < pipeline_data.thresholds.size() - 1) curDashboardSelection++; - showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection); + showDashboard(pipeline_data.thresholds, selectedBoxes, curDashboardSelection); } 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; - showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection); + showDashboard(pipeline_data.thresholds, selectedBoxes, curDashboardSelection); } else if (waitkey == UP_ARROW_KEY) { if (curDashboardSelection - DASHBOARD_COLUMNS >= 0) curDashboardSelection -= DASHBOARD_COLUMNS; - showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection); + showDashboard(pipeline_data.thresholds, selectedBoxes, curDashboardSelection); } else if (waitkey == ENTER_KEY) { - vector tempdata = showCharSelection(charSegmenter.getThresholds()[curDashboardSelection], charSegmenter.characters, statecodestr); + vector tempdata = showCharSelection(pipeline_data.thresholds[curDashboardSelection], charSegmenter.characters, statecodestr); for (int c = 0; c < charSegmenter.characters.size(); c++) humanInputs[c] = tempdata[c]; } else if (waitkey == SPACE_KEY) { selectedBoxes[curDashboardSelection] = !selectedBoxes[curDashboardSelection]; - showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection); + showDashboard(pipeline_data.thresholds, selectedBoxes, curDashboardSelection); } else if (waitkey == 's' || waitkey == 'S' || waitkey == 'W') { if (waitkey == 'W') { selectedBoxes[curDashboardSelection] = true; - showDashboard(charSegmenter.getThresholds(), selectedBoxes, curDashboardSelection); + showDashboard(pipeline_data.thresholds, selectedBoxes, curDashboardSelection); const std::string& ocr_str = ocr.postProcessor->bestChars; humanInputs.assign(ocr_str.begin(), ocr_str.end()); } bool somethingSelected = 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]) { @@ -243,13 +243,13 @@ int main( int argc, const char** argv ) if (humanInputs[c] == ' ') continue; - for (int t = 0; t < charSegmenter.getThresholds().size(); t++) + for (int t = 0; t < pipeline_data.thresholds.size(); t++) { if (selectedBoxes[t] == false) continue; stringstream filename; - Mat cropped = charSegmenter.getThresholds()[t](charSegmenter.characters[c]); + Mat cropped = pipeline_data.thresholds[t](charSegmenter.characters[c]); filename << outDir << "/" << humanInputs[c] << "-" << t << "-" << files[i]; imwrite(filename.str(), cropped); cout << "Writing char image: " << filename.str() << endl; diff --git a/src/openalpr/alpr_impl.cpp b/src/openalpr/alpr_impl.cpp index ff63d1a..e79bbad 100644 --- a/src/openalpr/alpr_impl.cpp +++ b/src/openalpr/alpr_impl.cpp @@ -230,7 +230,7 @@ void plateAnalysisThread(void* arg) // Tesseract OCR does not appear to be threadsafe 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); const vector ppResults = dispatcher->ocr->postProcessor->getResults(); dispatcher->ocrMutex.unlock(); diff --git a/src/openalpr/characteranalysis.cpp b/src/openalpr/characteranalysis.cpp index 26d6403..c70691f 100644 --- a/src/openalpr/characteranalysis.cpp +++ b/src/openalpr/characteranalysis.cpp @@ -36,29 +36,26 @@ CharacterAnalysis::CharacterAnalysis(PipelineData* pipeline_data) CharacterAnalysis::~CharacterAnalysis() { - for (int i = 0; i < thresholds.size(); i++) - { - thresholds[i].release(); - } - thresholds.clear(); + } void CharacterAnalysis::analyze() { - thresholds = produceThresholds(pipeline_data->crop_gray, config); + pipeline_data->clearThresholds(); + pipeline_data->thresholds = produceThresholds(pipeline_data->crop_gray, config); timespec startTime; getTime(&startTime); - for (int i = 0; i < thresholds.size(); i++) + for (int i = 0; i < pipeline_data->thresholds.size(); i++) { vector > contours; vector hierarchy; - Mat tempThreshold(thresholds[i].size(), CV_8U); - thresholds[i].copyTo(tempThreshold); + Mat tempThreshold(pipeline_data->thresholds[i].size(), CV_8U); + pipeline_data->thresholds[i].copyTo(tempThreshold); findContours(tempThreshold, contours, // a vector of contours hierarchy, @@ -79,9 +76,9 @@ void CharacterAnalysis::analyze() getTime(&startTime); - for (int i = 0; i < thresholds.size(); i++) + for (int i = 0; i < pipeline_data->thresholds.size(); i++) { - vector goodIndices = this->filter(thresholds[i], allContours[i], allHierarchy[i]); + vector goodIndices = this->filter(pipeline_data->thresholds[i], allContours[i], allHierarchy[i]); charSegments.push_back(goodIndices); if (config->debugCharAnalysis) @@ -100,7 +97,7 @@ void CharacterAnalysis::analyze() if (hasPlateMask) { // 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]); } @@ -108,7 +105,7 @@ void CharacterAnalysis::analyze() int bestFitScore = -1; int bestFitIndex = -1; - for (int i = 0; i < thresholds.size(); i++) + for (int i = 0; i < pipeline_data->thresholds.size(); i++) { //vector goodIndices = this->filter(thresholds[i], allContours[i], allHierarchy[i]); //charSegments.push_back(goodIndices); @@ -120,7 +117,7 @@ void CharacterAnalysis::analyze() bestFitScore = segmentCount; bestFitIndex = i; bestCharSegments = charSegments[i]; - bestThreshold = thresholds[i]; + bestThreshold = pipeline_data->thresholds[i]; bestContours = allContours[i]; bestHierarchy = allHierarchy[i]; bestCharSegmentsCount = segmentCount; @@ -272,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 drawContours(mask, allContours[winningIndex], @@ -316,7 +313,7 @@ Mat CharacterAnalysis::findOuterBoxMask() if (biggestContourIndex != -1) { - mask = Mat::zeros(thresholds[winningIndex].size(), CV_8U); + mask = Mat::zeros(pipeline_data->thresholds[winningIndex].size(), CV_8U); vector smoothedMaskPoints; approxPolyDP(contoursSecondRound[biggestContourIndex], smoothedMaskPoints, 2, true); @@ -337,12 +334,12 @@ Mat CharacterAnalysis::findOuterBoxMask() if (this->config->debugCharAnalysis) { vector 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(thresholds[winningIndex]); + debugImgs.push_back(pipeline_data->thresholds[winningIndex]); debugImgs.push_back(debugImgMasked); Mat dashboard = drawImageDashboard(debugImgs, CV_8U, 1); @@ -354,7 +351,7 @@ Mat CharacterAnalysis::findOuterBoxMask() } 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); return fullMask; } diff --git a/src/openalpr/characteranalysis.h b/src/openalpr/characteranalysis.h index b9d66ad..92d0c2b 100644 --- a/src/openalpr/characteranalysis.h +++ b/src/openalpr/characteranalysis.h @@ -54,7 +54,6 @@ class CharacterAnalysis bool thresholdsInverted; - std::vector thresholds; std::vector > > allContours; std::vector > allHierarchy; std::vector > charSegments; diff --git a/src/openalpr/characterregion.cpp b/src/openalpr/characterregion.cpp index b6e2953..15106f7 100644 --- a/src/openalpr/characterregion.cpp +++ b/src/openalpr/characterregion.cpp @@ -43,10 +43,10 @@ CharacterRegion::CharacterRegion(PipelineData* pipeline_data) if (this->debug && charAnalysis->linePolygon.size() > 0) { vector 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()); - charAnalysis->thresholds[z].copyTo(tmp); + Mat tmp(pipeline_data->thresholds[z].size(), pipeline_data->thresholds[z].type()); + pipeline_data->thresholds[z].copyTo(tmp); cvtColor(tmp, tmp, CV_GRAY2BGR); tempDash.push_back(tmp); diff --git a/src/openalpr/charactersegmenter.cpp b/src/openalpr/charactersegmenter.cpp index 04cf417..62a99fe 100644 --- a/src/openalpr/charactersegmenter.cpp +++ b/src/openalpr/charactersegmenter.cpp @@ -48,7 +48,7 @@ CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data) 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) @@ -106,7 +106,7 @@ CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data) float avgCharWidth = median(charWidths.data(), charWidths.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 @@ -118,11 +118,11 @@ CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data) vector allBoxes; 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)); - VerticalHistogram vertHistogram(charAnalysis->thresholds[i], histogramMask); + VerticalHistogram vertHistogram(pipeline_data->thresholds[i], histogramMask); if (this->config->debugCharSegmenter) { @@ -172,16 +172,16 @@ CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data) } //ColorFilter colorFilter(img, charAnalysis->getCharacterMask()); - vector candidateBoxes = getBestCharBoxes(charAnalysis->thresholds[0], allBoxes, medianCharWidth); + vector candidateBoxes = getBestCharBoxes(pipeline_data->thresholds[0], allBoxes, medianCharWidth); if (this->config->debugCharSegmenter) { // 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 boxMask = getCharBoxMask(charAnalysis->thresholds[i], candidateBoxes); - charAnalysis->thresholds[i].copyTo(cleanImg); + Mat cleanImg = Mat::zeros(pipeline_data->thresholds[i].size(), pipeline_data->thresholds[i].type()); + Mat boxMask = getCharBoxMask(pipeline_data->thresholds[i], candidateBoxes); + pipeline_data->thresholds[i].copyTo(cleanImg); bitwise_and(cleanImg, boxMask, cleanImg); cvtColor(cleanImg, cleanImg, CV_GRAY2BGR); @@ -193,19 +193,19 @@ CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data) 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); - cleanCharRegions(charAnalysis->thresholds, candidateBoxes); - cleanMostlyFullBoxes(charAnalysis->thresholds, candidateBoxes); + cleanCharRegions(pipeline_data->thresholds, candidateBoxes); + cleanMostlyFullBoxes(pipeline_data->thresholds, candidateBoxes); //cleanBasedOnColor(thresholds, colorFilter.colorMask, candidateBoxes); - candidateBoxes = filterMostlyEmptyBoxes(charAnalysis->thresholds, candidateBoxes); - this->characters = candidateBoxes; + candidateBoxes = filterMostlyEmptyBoxes(pipeline_data->thresholds, candidateBoxes); + pipeline_data->charRegions = candidateBoxes; if (config->debugTiming) { @@ -216,7 +216,7 @@ CharacterSegmenter::CharacterSegmenter(PipelineData* pipeline_data) 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); Mat generalDash = drawImageDashboard(this->imgDbgGeneral, this->imgDbgGeneral[0].type(), 2); @@ -490,7 +490,7 @@ vector CharacterSegmenter::combineCloseBoxes( vector charBoxes, floa newCharBoxes.push_back(bigRect); 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); RotatedRect rrect(center, Size2f(bigRect.width, bigRect.height + (bigRect.height / 2)), 0); @@ -1140,7 +1140,4 @@ Mat CharacterSegmenter::getCharBoxMask(Mat img_threshold, vector charBoxes return mask; } -vector CharacterSegmenter::getThresholds() -{ - return charAnalysis->thresholds; -} + diff --git a/src/openalpr/charactersegmenter.h b/src/openalpr/charactersegmenter.h index 6b958bd..9b9c9ac 100644 --- a/src/openalpr/charactersegmenter.h +++ b/src/openalpr/charactersegmenter.h @@ -50,7 +50,6 @@ class CharacterSegmenter std::vector characters; int confidence; - std::vector getThresholds(); private: Config* config; diff --git a/src/openalpr/licenseplatecandidate.h b/src/openalpr/licenseplatecandidate.h index f63375e..6ec0c71 100644 --- a/src/openalpr/licenseplatecandidate.h +++ b/src/openalpr/licenseplatecandidate.h @@ -51,12 +51,12 @@ class LicensePlateCandidate void recognize(); - CharacterSegmenter* charSegmenter; private: PipelineData* pipeline_data; Config* config; + CharacterSegmenter* charSegmenter; cv::Mat filterByCharacterHue(std::vector > charRegionContours); std::vector findPlateCorners(cv::Mat inputImage, PlateLines plateLines, CharacterRegion charRegion); // top-left, top-right, bottom-right, bottom-left diff --git a/src/openalpr/ocr.cpp b/src/openalpr/ocr.cpp index 5e43e0d..776ea6b 100644 --- a/src/openalpr/ocr.cpp +++ b/src/openalpr/ocr.cpp @@ -52,7 +52,7 @@ OCR::~OCR() delete tesseract; } -void OCR::performOCR(vector thresholds, vector charRegions) +void OCR::performOCR(PipelineData* pipeline_data) { timespec startTime; getTime(&startTime); @@ -60,18 +60,20 @@ void OCR::performOCR(vector thresholds, vector charRegions) postProcessor->clear(); // 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; - 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 - bitwise_not(thresholds[i], thresholds[i]); - tesseract->SetImage((uchar*) thresholds[i].data, thresholds[i].size().width, thresholds[i].size().height, thresholds[i].channels(), thresholds[i].step1()); + bitwise_not(pipeline_data->thresholds[i], pipeline_data->thresholds[i]); + 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->Recognize(NULL); diff --git a/src/openalpr/ocr.h b/src/openalpr/ocr.h index 0c9c080..444cb81 100644 --- a/src/openalpr/ocr.h +++ b/src/openalpr/ocr.h @@ -26,6 +26,7 @@ #include "utility.h" #include "postprocess.h" #include "config.h" +#include "pipeline_data.h" #include "constants.h" #include "opencv2/imgproc/imgproc.hpp" @@ -39,7 +40,7 @@ class OCR OCR(Config* config); virtual ~OCR(); - void performOCR(std::vector thresholds, std::vector charRegions); + void performOCR(PipelineData* pipeline_data); PostProcess* postProcessor; //string recognizedText; diff --git a/src/openalpr/pipeline_data.cpp b/src/openalpr/pipeline_data.cpp index d0ccb71..414510d 100644 --- a/src/openalpr/pipeline_data.cpp +++ b/src/openalpr/pipeline_data.cpp @@ -16,4 +16,13 @@ PipelineData::PipelineData(Mat colorImage, Rect regionOfInterest, Config* config PipelineData::~PipelineData() { -} \ No newline at end of file +} + +void PipelineData::clearThresholds() +{ + for (int i = 0; i < thresholds.size(); i++) + { + thresholds[i].release(); + } + thresholds.clear(); +} diff --git a/src/openalpr/pipeline_data.h b/src/openalpr/pipeline_data.h index b4853ab..b403b10 100644 --- a/src/openalpr/pipeline_data.h +++ b/src/openalpr/pipeline_data.h @@ -13,6 +13,8 @@ class PipelineData PipelineData(cv::Mat colorImage, cv::Rect regionOfInterest, Config* config); virtual ~PipelineData(); + void clearThresholds(); + // Inputs Config* config; @@ -35,6 +37,7 @@ class PipelineData float overall_confidence; + std::vector charRegions; // Plate Lines std::vector horizontalLines;