Refactored CharacterAnalysis code to use a class that wraps the raw Contour data

This commit is contained in:
Matt Hill
2014-10-09 23:23:22 -04:00
parent add77f6b0c
commit d55e66d665
10 changed files with 286 additions and 186 deletions

View File

@@ -18,7 +18,6 @@
*/
#include "characteranalysis.h"
#include "platemask.h"
using namespace cv;
using namespace std;
@@ -52,19 +51,9 @@ void CharacterAnalysis::analyze()
for (uint i = 0; i < pipeline_data->thresholds.size(); i++)
{
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
TextContours tc(pipeline_data->thresholds[i]);
Mat tempThreshold(pipeline_data->thresholds[i].size(), CV_8U);
pipeline_data->thresholds[i].copyTo(tempThreshold);
findContours(tempThreshold,
contours, // a vector of contours
hierarchy,
CV_RETR_TREE, // retrieve all contours
CV_CHAIN_APPROX_SIMPLE ); // all pixels of each contours
allContours.push_back(contours);
allHierarchy.push_back(hierarchy);
allTextContours.push_back(tc);
}
if (config->debugTiming)
@@ -79,11 +68,10 @@ void CharacterAnalysis::analyze()
for (uint i = 0; i < pipeline_data->thresholds.size(); i++)
{
vector<bool> goodIndices = this->filter(pipeline_data->thresholds[i], allContours[i], allHierarchy[i]);
charSegments.push_back(goodIndices);
this->filter(pipeline_data->thresholds[i], allTextContours[i]);
if (config->debugCharAnalysis)
cout << "Threshold " << i << " had " << getGoodIndicesCount(goodIndices) << " good indices." << endl;
cout << "Threshold " << i << " had " << allTextContours[i].getGoodIndicesCount() << " good indices." << endl;
}
if (config->debugTiming)
@@ -94,7 +82,7 @@ void CharacterAnalysis::analyze()
}
PlateMask plateMask(pipeline_data);
plateMask.findOuterBoxMask(charSegments, allContours, allHierarchy);
plateMask.findOuterBoxMask(allTextContours);
pipeline_data->hasPlateBorder = plateMask.hasPlateMask;
pipeline_data->plateBorderMask = plateMask.getMask();
@@ -104,7 +92,7 @@ void CharacterAnalysis::analyze()
// Filter out bad contours now that we have an outer box mask...
for (uint i = 0; i < pipeline_data->thresholds.size(); i++)
{
charSegments[i] = filterByOuterMask(allContours[i], allHierarchy[i], charSegments[i]);
filterByOuterMask(allTextContours[i]);
}
}
@@ -115,17 +103,14 @@ void CharacterAnalysis::analyze()
//vector<bool> goodIndices = this->filter(thresholds[i], allContours[i], allHierarchy[i]);
//charSegments.push_back(goodIndices);
int segmentCount = getGoodIndicesCount(charSegments[i]);
int segmentCount = allTextContours[i].getGoodIndicesCount();
if (segmentCount > bestFitScore)
{
bestFitScore = segmentCount;
bestFitIndex = i;
bestCharSegments = charSegments[i];
bestThreshold = pipeline_data->thresholds[i];
bestContours = allContours[i];
bestHierarchy = allHierarchy[i];
bestCharSegmentsCount = segmentCount;
bestContours = allTextContours[i];
}
}
@@ -146,11 +131,11 @@ void CharacterAnalysis::analyze()
vector<vector<Point> > allowedContours;
for (uint i = 0; i < bestContours.size(); i++)
{
if (bestCharSegments[i])
allowedContours.push_back(bestContours[i]);
if (bestContours.goodIndices[i])
allowedContours.push_back(bestContours.contours[i]);
}
drawContours(img_contours, bestContours,
drawContours(img_contours, bestContours.contours,
-1, // draw all contours
cv::Scalar(255,0,0), // in blue
1); // with a thickness of 1
@@ -165,14 +150,14 @@ void CharacterAnalysis::analyze()
//charsegments = this->getPossibleCharRegions(img_threshold, allContours, allHierarchy, STARTING_MIN_HEIGHT + (bestFitIndex * HEIGHT_STEP), STARTING_MAX_HEIGHT + (bestFitIndex * HEIGHT_STEP));
this->linePolygon = getBestVotedLines(pipeline_data->crop_gray, bestContours, bestCharSegments);
this->linePolygon = getBestVotedLines(pipeline_data->crop_gray, bestContours);
if (this->linePolygon.size() > 0)
{
this->topLine = LineSegment(this->linePolygon[0].x, this->linePolygon[0].y, this->linePolygon[1].x, this->linePolygon[1].y);
this->bottomLine = LineSegment(this->linePolygon[3].x, this->linePolygon[3].y, this->linePolygon[2].x, this->linePolygon[2].y);
//this->charArea = getCharSegmentsBetweenLines(bestThreshold, bestContours, this->linePolygon);
filterBetweenLines(bestThreshold, bestContours, bestHierarchy, linePolygon, bestCharSegments);
filterBetweenLines(bestThreshold, bestContours, linePolygon);
this->charArea = getCharArea();
@@ -188,17 +173,6 @@ void CharacterAnalysis::analyze()
this->thresholdsInverted = isPlateInverted();
}
int CharacterAnalysis::getGoodIndicesCount(vector<bool> goodIndices)
{
int count = 0;
for (uint i = 0; i < goodIndices.size(); i++)
{
if (goodIndices[i])
count++;
}
return count;
}
@@ -208,15 +182,15 @@ Mat CharacterAnalysis::getCharacterMask()
for (uint i = 0; i < bestContours.size(); i++)
{
if (bestCharSegments[i] == false)
if (bestContours.goodIndices[i] == false)
continue;
drawContours(charMask, bestContours,
drawContours(charMask, bestContours.contours,
i, // draw this contour
cv::Scalar(255,255,255), // in
CV_FILLED,
8,
bestHierarchy,
bestContours.hierarchy,
1
);
@@ -226,7 +200,7 @@ Mat CharacterAnalysis::getCharacterMask()
}
// Returns a polygon "stripe" across the width of the character region. The lines are voted and the polygon starts at 0 and extends to image width
vector<Point> CharacterAnalysis::getBestVotedLines(Mat img, vector<vector<Point> > contours, vector<bool> goodIndices)
vector<Point> CharacterAnalysis::getBestVotedLines(Mat img, TextContours textContours)
{
//if (this->debug)
// cout << "CharacterAnalysis::getBestVotedLines" << endl;
@@ -235,10 +209,10 @@ vector<Point> CharacterAnalysis::getBestVotedLines(Mat img, vector<vector<Point>
vector<Rect> charRegions;
for (uint i = 0; i < contours.size(); i++)
for (uint i = 0; i < textContours.size(); i++)
{
if (goodIndices[i])
charRegions.push_back(boundingRect(contours[i]));
if (textContours.goodIndices[i])
charRegions.push_back(boundingRect(textContours.contours[i]));
}
// Find the best fit line segment that is parallel with the most char segments
@@ -308,7 +282,6 @@ vector<Point> CharacterAnalysis::getBestVotedLines(Mat img, vector<vector<Point>
//cv::line(tempImg, Point(x1, y1), Point(x2, y2), Scalar(0, 0, 255));
bottomLines.push_back(LineSegment(x1, y1, x2, y2));
//drawAndWait(&tempImg);
}
}
@@ -382,65 +355,66 @@ vector<Point> CharacterAnalysis::getBestVotedLines(Mat img, vector<vector<Point>
return bestStripe;
}
vector<bool> CharacterAnalysis::filter(Mat img, vector<vector<Point> > contours, vector<Vec4i> hierarchy)
void CharacterAnalysis::filter(Mat img, TextContours& textContours)
{
static int STARTING_MIN_HEIGHT = round (((float) img.rows) * config->charAnalysisMinPercent);
static int STARTING_MAX_HEIGHT = round (((float) img.rows) * (config->charAnalysisMinPercent + config->charAnalysisHeightRange));
static int HEIGHT_STEP = round (((float) img.rows) * config->charAnalysisHeightStepSize);
static int NUM_STEPS = config->charAnalysisNumSteps;
vector<bool> charSegments;
int bestFitScore = -1;
vector<bool> bestIndices;
for (int i = 0; i < NUM_STEPS; i++)
{
int goodIndicesCount;
//vector<bool> goodIndices(contours.size());
for (uint z = 0; z < textContours.size(); z++) textContours.goodIndices[z] = true;
vector<bool> goodIndices(contours.size());
for (uint z = 0; z < goodIndices.size(); z++) goodIndices[z] = true;
this->filterByBoxSize(textContours, STARTING_MIN_HEIGHT + (i * HEIGHT_STEP), STARTING_MAX_HEIGHT + (i * HEIGHT_STEP));
goodIndices = this->filterByBoxSize(contours, goodIndices, STARTING_MIN_HEIGHT + (i * HEIGHT_STEP), STARTING_MAX_HEIGHT + (i * HEIGHT_STEP));
goodIndicesCount = getGoodIndicesCount(goodIndices);
if ( goodIndicesCount == 0 || goodIndicesCount <= bestFitScore) // Don't bother doing more filtering if we already lost...
int goodIndices = textContours.getGoodIndicesCount();
if ( goodIndices == 0 || goodIndices <= bestFitScore) // Don't bother doing more filtering if we already lost...
continue;
goodIndices = this->filterContourHoles(contours, hierarchy, goodIndices);
this->filterContourHoles(textContours);
goodIndicesCount = getGoodIndicesCount(goodIndices);
if ( goodIndicesCount == 0 || goodIndicesCount <= bestFitScore) // Don't bother doing more filtering if we already lost...
goodIndices = textContours.getGoodIndicesCount();
if ( goodIndices == 0 || goodIndices <= bestFitScore) // Don't bother doing more filtering if we already lost...
continue;
//goodIndices = this->filterByParentContour( contours, hierarchy, goodIndices);
vector<Point> lines = getBestVotedLines(img, contours, goodIndices);
goodIndices = this->filterBetweenLines(img, contours, hierarchy, lines, goodIndices);
vector<Point> lines = getBestVotedLines(img, textContours);
this->filterBetweenLines(img, textContours, lines);
int segmentCount = getGoodIndicesCount(goodIndices);
int segmentCount = textContours.getGoodIndicesCount();
if (segmentCount > bestFitScore)
{
bestFitScore = segmentCount;
charSegments = goodIndices;
bestIndices = textContours.getIndicesCopy();
}
}
return charSegments;
textContours.setIndices(bestIndices);
}
// Goes through the contours for the plate and picks out possible char segments based on min/max height
vector<bool> CharacterAnalysis::filterByBoxSize(vector< vector< Point> > contours, vector<bool> goodIndices, int minHeightPx, int maxHeightPx)
void CharacterAnalysis::filterByBoxSize(TextContours& textContours, int minHeightPx, int maxHeightPx)
{
float idealAspect=config->charWidthMM / config->charHeightMM;
float aspecttolerance=0.25;
vector<bool> includedIndices(contours.size());
for (uint j = 0; j < contours.size(); j++)
includedIndices.push_back(false);
for (uint i = 0; i < contours.size(); i++)
for (uint i = 0; i < textContours.size(); i++)
{
if (goodIndices[i] == false)
if (textContours.goodIndices[i] == false)
continue;
textContours.goodIndices[i] = false; // Set it to not included unless it proves valid
//Create bounding rect of object
Rect mr= boundingRect(contours[i]);
Rect mr= boundingRect(textContours.contours[i]);
float minWidth = mr.height * 0.2;
//Crop image
@@ -450,27 +424,25 @@ vector<bool> CharacterAnalysis::filterByBoxSize(vector< vector< Point> > contour
float charAspect= (float)mr.width/(float)mr.height;
if (abs(charAspect - idealAspect) < aspecttolerance)
includedIndices[i] = true;
textContours.goodIndices[i] = true;
}
}
return includedIndices;
}
vector< bool > CharacterAnalysis::filterContourHoles(vector< vector< Point > > contours, vector< Vec4i > hierarchy, vector< bool > goodIndices)
void CharacterAnalysis::filterContourHoles(TextContours& textContours)
{
vector<bool> includedIndices(contours.size());
for (uint j = 0; j < contours.size(); j++)
includedIndices.push_back(false);
for (uint i = 0; i < contours.size(); i++)
for (uint i = 0; i < textContours.size(); i++)
{
if (goodIndices[i] == false)
if (textContours.goodIndices[i] == false)
continue;
int parentIndex = hierarchy[i][3];
textContours.goodIndices[i] = false; // Set it to not included unless it proves valid
int parentIndex = textContours.hierarchy[i][3];
if (parentIndex >= 0 && goodIndices[parentIndex])
if (parentIndex >= 0 && textContours.goodIndices[parentIndex])
{
// this contour is a child of an already identified contour. REMOVE it
if (this->config->debugCharAnalysis)
@@ -480,31 +452,29 @@ vector< bool > CharacterAnalysis::filterContourHoles(vector< vector< Point > > c
}
else
{
includedIndices[i] = true;
textContours.goodIndices[i] = true;
}
}
return includedIndices;
}
// Goes through the contours for the plate and picks out possible char segments based on min/max height
// returns a vector of indices corresponding to valid contours
vector<bool> CharacterAnalysis::filterByParentContour( vector< vector< Point> > contours, vector<Vec4i> hierarchy, vector<bool> goodIndices)
void CharacterAnalysis::filterByParentContour( TextContours& textContours)
{
vector<bool> includedIndices(contours.size());
for (uint j = 0; j < contours.size(); j++)
includedIndices[j] = false;
vector<int> parentIDs;
vector<int> votes;
for (uint i = 0; i < contours.size(); i++)
for (uint i = 0; i < textContours.size(); i++)
{
if (goodIndices[i] == false)
if (textContours.goodIndices[i] == false)
continue;
textContours.goodIndices[i] = false; // Set it to not included unless it proves
int voteIndex = -1;
int parentID = hierarchy[i][3];
int parentID = textContours.hierarchy[i][3];
// check if parentID is already in the lsit
for (uint j = 0; j < parentIDs.size(); j++)
{
@@ -540,35 +510,30 @@ vector<bool> CharacterAnalysis::filterByParentContour( vector< vector< Point> >
}
// Now filter out all the contours with a different parent ID (assuming the totalVotes > 2)
for (uint i = 0; i < contours.size(); i++)
for (uint i = 0; i < textContours.size(); i++)
{
if (goodIndices[i] == false)
if (textContours.goodIndices[i] == false)
continue;
if (totalVotes <= 2)
{
includedIndices[i] = true;
textContours.goodIndices[i] = true;
}
else if (hierarchy[i][3] == winningParentId)
else if (textContours.hierarchy[i][3] == winningParentId)
{
includedIndices[i] = true;
textContours.goodIndices[i] = true;
}
}
return includedIndices;
}
vector<bool> CharacterAnalysis::filterBetweenLines(Mat img, vector<vector<Point> > contours, vector<Vec4i> hierarchy, vector<Point> outerPolygon, vector<bool> goodIndices)
void CharacterAnalysis::filterBetweenLines(Mat img, TextContours& textContours, vector<Point> outerPolygon )
{
static float MIN_AREA_PERCENT_WITHIN_LINES = 0.88;
static float MAX_DISTANCE_PERCENT_FROM_LINES = 0.15;
vector<bool> includedIndices(contours.size());
for (uint j = 0; j < contours.size(); j++)
includedIndices[j] = false;
if (outerPolygon.size() == 0)
return includedIndices;
return;
vector<Point> validPoints;
@@ -587,19 +552,21 @@ vector<bool> CharacterAnalysis::filterBetweenLines(Mat img, vector<vector<Point>
fillConvexPoly(outerMask, outerPolygon.data(), outerPolygon.size(), Scalar(255,255,255));
// For each contour, determine if enough of it is between the lines to qualify
for (uint i = 0; i < contours.size(); i++)
for (uint i = 0; i < textContours.size(); i++)
{
if (goodIndices[i] == false)
if (textContours.goodIndices[i] == false)
continue;
textContours.goodIndices[i] = false; // Set it to not included unless it proves
innerArea.setTo(Scalar(0,0,0));
drawContours(innerArea, contours,
drawContours(innerArea, textContours.contours,
i, // draw this contour
cv::Scalar(255,255,255), // in
CV_FILLED,
8,
hierarchy,
textContours.hierarchy,
0
);
@@ -610,7 +577,7 @@ vector<bool> CharacterAnalysis::filterBetweenLines(Mat img, vector<vector<Point>
CV_RETR_EXTERNAL, // retrieve the external contours
CV_CHAIN_APPROX_SIMPLE ); // all pixels of each contours );
double totalArea = contourArea(contours[i]);
double totalArea = contourArea(textContours.contours[i]);
double areaBetweenLines = 0;
for (uint tempContourIdx = 0; tempContourIdx < tempContours.size(); tempContourIdx++)
@@ -635,50 +602,46 @@ vector<bool> CharacterAnalysis::filterBetweenLines(Mat img, vector<vector<Point>
int highPointValue = 999999999;
int lowPointIndex = 0;
int lowPointValue = 0;
for (uint cidx = 0; cidx < contours[i].size(); cidx++)
for (uint cidx = 0; cidx < textContours.contours[i].size(); cidx++)
{
if (contours[i][cidx].y < highPointValue)
if (textContours.contours[i][cidx].y < highPointValue)
{
highPointIndex = cidx;
highPointValue = contours[i][cidx].y;
highPointValue = textContours.contours[i][cidx].y;
}
if (contours[i][cidx].y > lowPointValue)
if (textContours.contours[i][cidx].y > lowPointValue)
{
lowPointIndex = cidx;
lowPointValue = contours[i][cidx].y;
lowPointValue = textContours.contours[i][cidx].y;
}
}
// Get the absolute distance from the top and bottom lines
Point closestTopPoint = topLine.closestPointOnSegmentTo(contours[i][highPointIndex]);
Point closestBottomPoint = bottomLine.closestPointOnSegmentTo(contours[i][lowPointIndex]);
Point closestTopPoint = topLine.closestPointOnSegmentTo(textContours.contours[i][highPointIndex]);
Point closestBottomPoint = bottomLine.closestPointOnSegmentTo(textContours.contours[i][lowPointIndex]);
float absTopDistance = distanceBetweenPoints(closestTopPoint, contours[i][highPointIndex]);
float absBottomDistance = distanceBetweenPoints(closestBottomPoint, contours[i][lowPointIndex]);
float absTopDistance = distanceBetweenPoints(closestTopPoint, textContours.contours[i][highPointIndex]);
float absBottomDistance = distanceBetweenPoints(closestBottomPoint, textContours.contours[i][lowPointIndex]);
float maxDistance = lineHeight * MAX_DISTANCE_PERCENT_FROM_LINES;
if (absTopDistance < maxDistance && absBottomDistance < maxDistance)
{
includedIndices[i] = true;
textContours.goodIndices[i] = true;
}
}
return includedIndices;
}
std::vector< bool > CharacterAnalysis::filterByOuterMask(vector< vector< Point > > contours, vector< Vec4i > hierarchy, std::vector< bool > goodIndices)
void CharacterAnalysis::filterByOuterMask(TextContours& textContours)
{
float MINIMUM_PERCENT_LEFT_AFTER_MASK = 0.1;
float MINIMUM_PERCENT_OF_CHARS_INSIDE_PLATE_MASK = 0.6;
if (this->pipeline_data->hasPlateBorder == false)
return goodIndices;
return;
vector<bool> passingIndices;
for (uint i = 0; i < goodIndices.size(); i++)
passingIndices.push_back(false);
cv::Mat plateMask = pipeline_data->plateBorderMask;
@@ -688,14 +651,18 @@ std::vector< bool > CharacterAnalysis::filterByOuterMask(vector< vector< Point >
int charsInsideMask = 0;
int totalChars = 0;
for (uint i=0; i < goodIndices.size(); i++)
vector<bool> originalindices;
for (uint i = 0; i < textContours.size(); i++)
originalindices.push_back(textContours.goodIndices[i]);
for (uint i=0; i < textContours.size(); i++)
{
if (goodIndices[i] == false)
if (textContours.goodIndices[i] == false)
continue;
totalChars++;
drawContours(tempFullContour, contours, i, Scalar(255,255,255), CV_FILLED, 8, hierarchy);
drawContours(tempFullContour, textContours.contours, i, Scalar(255,255,255), CV_FILLED, 8, textContours.hierarchy);
bitwise_and(tempFullContour, plateMask, tempMaskedContour);
float beforeMaskWhiteness = mean(tempFullContour)[0];
@@ -704,20 +671,25 @@ std::vector< bool > CharacterAnalysis::filterByOuterMask(vector< vector< Point >
if (afterMaskWhiteness / beforeMaskWhiteness > MINIMUM_PERCENT_LEFT_AFTER_MASK)
{
charsInsideMask++;
passingIndices[i] = true;
textContours.goodIndices[i] = true;
}
}
if (totalChars == 0)
return goodIndices;
{
textContours.goodIndices = originalindices;
return;
}
// Check to make sure that this is a valid box. If the box is too small (e.g., 1 char is inside, and 3 are outside)
// then don't use this to filter.
float percentCharsInsideMask = ((float) charsInsideMask) / ((float) totalChars);
if (percentCharsInsideMask < MINIMUM_PERCENT_OF_CHARS_INSIDE_PLATE_MASK)
return goodIndices;
{
textContours.goodIndices = originalindices;
return;
}
return passingIndices;
}
bool CharacterAnalysis::isPlateInverted()
@@ -771,15 +743,15 @@ vector<Point> CharacterAnalysis::getCharArea()
for (uint i = 0; i < bestContours.size(); i++)
{
if (bestCharSegments[i] == false)
if (bestContours.goodIndices[i] == false)
continue;
for (uint z = 0; z < bestContours[i].size(); z++)
for (uint z = 0; z < bestContours.contours[i].size(); z++)
{
if (bestContours[i][z].x < leftX)
leftX = bestContours[i][z].x;
if (bestContours[i][z].x > rightX)
rightX = bestContours[i][z].x;
if (bestContours.contours[i][z].x < leftX)
leftX = bestContours.contours[i][z].x;
if (bestContours.contours[i][z].x > rightX)
rightX = bestContours.contours[i][z].x;
}
}

View File

@@ -24,6 +24,8 @@
#include "utility.h"
#include "config.h"
#include "pipeline_data.h"
#include "textcontours.h"
#include "platemask.h"
class CharacterAnalysis
{
@@ -34,10 +36,12 @@ class CharacterAnalysis
cv::Mat bestThreshold;
std::vector<std::vector<cv::Point> > bestContours;
std::vector<cv::Vec4i> bestHierarchy;
std::vector<bool> bestCharSegments;
int bestCharSegmentsCount;
TextContours bestContours;
//std::vector<std::vector<cv::Point> > bestContours;
//std::vector<cv::Vec4i> bestHierarchy;
//std::vector<bool> bestCharSegments;
//int bestCharSegmentsCount;
LineSegment topLine;
LineSegment bottomLine;
@@ -52,9 +56,10 @@ class CharacterAnalysis
bool thresholdsInverted;
bool isTwoLine;
std::vector<std::vector<std::vector<cv::Point> > > allContours;
std::vector<std::vector<cv::Vec4i> > allHierarchy;
std::vector<std::vector<bool> > charSegments;
std::vector<TextContours> allTextContours;
//std::vector<std::vector<std::vector<cv::Point> > > allContours;
//std::vector<std::vector<cv::Vec4i> > allHierarchy;
//std::vector<std::vector<bool> > charSegments;
void analyze();
@@ -67,21 +72,19 @@ class CharacterAnalysis
cv::Mat findOuterBoxMask( );
bool isPlateInverted();
std::vector<bool> filter(cv::Mat img, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy);
void filter(cv::Mat img, TextContours& textContours);
std::vector<bool> filterByBoxSize(std::vector<std::vector<cv::Point> > contours, std::vector<bool> goodIndices, int minHeightPx, int maxHeightPx);
std::vector<bool> filterByParentContour( std::vector< std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, std::vector<bool> goodIndices);
std::vector<bool> filterContourHoles(std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, std::vector<bool> goodIndices);
std::vector<bool> filterByOuterMask(std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, std::vector<bool> goodIndices);
void filterByBoxSize(TextContours& textContours, int minHeightPx, int maxHeightPx);
void filterByParentContour( TextContours& textContours );
void filterContourHoles(TextContours& textContours);
void filterByOuterMask(TextContours& textContours);
std::vector<cv::Point> getCharArea();
std::vector<cv::Point> getBestVotedLines(cv::Mat img, std::vector<std::vector<cv::Point> > contours, std::vector<bool> goodIndices);
//vector<Point> getCharSegmentsBetweenLines(Mat img, vector<vector<Point> > contours, vector<Point> outerPolygon);
std::vector<bool> filterBetweenLines(cv::Mat img, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, std::vector<cv::Point> outerPolygon, std::vector<bool> goodIndices);
std::vector<cv::Point> getBestVotedLines(cv::Mat img, TextContours textContours);
void filterBetweenLines(cv::Mat img, TextContours& textContours, std::vector<cv::Point> outerPolygon );
bool verifySize(cv::Mat r, float minHeightPx, float maxHeightPx);
int getGoodIndicesCount(std::vector<bool> goodIndices);
};

View File

@@ -36,9 +36,7 @@ cv::Mat PlateMask::getMask() {
return this->plateMask;
}
void PlateMask::findOuterBoxMask(vector<vector<bool> > charSegments,
vector<vector<vector<Point> > > allContours,
vector<vector<Vec4i> > allHierarchy)
void PlateMask::findOuterBoxMask( vector<TextContours > contours )
{
double min_parent_area = pipeline_data->config->templateHeightPx * pipeline_data->config->templateWidthPx * 0.10; // Needs to be at least 10% of the plate area to be considered.
@@ -50,19 +48,19 @@ void PlateMask::findOuterBoxMask(vector<vector<bool> > charSegments,
if (pipeline_data->config->debugCharAnalysis)
cout << "CharacterAnalysis::findOuterBoxMask" << endl;
for (uint imgIndex = 0; imgIndex < allContours.size(); imgIndex++)
for (uint imgIndex = 0; imgIndex < contours.size(); imgIndex++)
{
//vector<bool> charContours = filter(thresholds[imgIndex], allContours[imgIndex], allHierarchy[imgIndex]);
int charsRecognized = 0;
int parentId = -1;
bool hasParent = false;
for (uint i = 0; i < charSegments[imgIndex].size(); i++)
for (uint i = 0; i < contours[imgIndex].goodIndices.size(); i++)
{
if (charSegments[imgIndex][i]) charsRecognized++;
if (charSegments[imgIndex][i] && allHierarchy[imgIndex][i][3] != -1)
if (contours[imgIndex].goodIndices[i]) charsRecognized++;
if (contours[imgIndex].goodIndices[i] && contours[imgIndex].hierarchy[i][3] != -1)
{
parentId = allHierarchy[imgIndex][i][3];
parentId = contours[imgIndex].hierarchy[i][3];
hasParent = true;
}
}
@@ -72,7 +70,7 @@ void PlateMask::findOuterBoxMask(vector<vector<bool> > charSegments,
if (hasParent)
{
double boxArea = contourArea(allContours[imgIndex][parentId]);
double boxArea = contourArea(contours[imgIndex].contours[parentId]);
if (boxArea < min_parent_area)
continue;
@@ -96,13 +94,13 @@ void PlateMask::findOuterBoxMask(vector<vector<bool> > charSegments,
int longestChildIndex = -1;
double longestChildLength = 0;
// Find the child with the longest permiter/arc length ( just for kicks)
for (uint i = 0; i < allContours[winningIndex].size(); i++)
for (uint i = 0; i < contours[winningIndex].size(); i++)
{
for (uint j = 0; j < allContours[winningIndex].size(); j++)
for (uint j = 0; j < contours[winningIndex].size(); j++)
{
if (allHierarchy[winningIndex][j][3] == winningParentId)
if (contours[winningIndex].hierarchy[j][3] == winningParentId)
{
double arclength = arcLength(allContours[winningIndex][j], false);
double arclength = arcLength(contours[winningIndex].contours[j], false);
if (arclength > longestChildLength)
{
longestChildIndex = j;
@@ -115,12 +113,12 @@ void PlateMask::findOuterBoxMask(vector<vector<bool> > charSegments,
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],
drawContours(mask, contours[winningIndex].contours,
winningParentId, // draw this contour
cv::Scalar(255,255,255), // in
CV_FILLED,
8,
allHierarchy[winningIndex],
contours[winningIndex].hierarchy,
0
);
@@ -169,7 +167,7 @@ void PlateMask::findOuterBoxMask(vector<vector<bool> > charSegments,
cv::Scalar(255,255,255), // in
CV_FILLED,
8,
allHierarchy[winningIndex],
contours[winningIndex].hierarchy,
0
);
}

View File

@@ -22,6 +22,7 @@
#include "opencv2/imgproc/imgproc.hpp"
#include "pipeline_data.h"
#include "textcontours.h"
class PlateMask {
public:
@@ -32,7 +33,7 @@ public:
cv::Mat getMask();
void findOuterBoxMask(std::vector<std::vector<bool> > charSegments, std::vector<std::vector<std::vector<cv::Point> > > allContours, std::vector<std::vector<cv::Vec4i> > allHierarchy);
void findOuterBoxMask(std::vector<TextContours > contours);
private:

View File

@@ -0,0 +1,86 @@
/*
* File: textcontours.cpp
* Author: mhill
*
* Created on October 9, 2014, 7:40 PM
*/
#include "textcontours.h"
using namespace std;
using namespace cv;
TextContours::TextContours() {
}
TextContours::TextContours(cv::Mat threshold) {
load(threshold);
}
TextContours::~TextContours() {
}
void TextContours::load(cv::Mat threshold) {
Mat tempThreshold(threshold.size(), CV_8U);
threshold.copyTo(tempThreshold);
findContours(tempThreshold,
contours, // a vector of contours
hierarchy,
CV_RETR_TREE, // retrieve all contours
CV_CHAIN_APPROX_SIMPLE ); // all pixels of each contours
for (uint i = 0; i < contours.size(); i++)
goodIndices.push_back(true);
}
uint TextContours::size() {
return contours.size();
}
int TextContours::getGoodIndicesCount()
{
int count = 0;
for (uint i = 0; i < goodIndices.size(); i++)
{
if (goodIndices[i])
count++;
}
return count;
}
std::vector<bool> TextContours::getIndicesCopy()
{
vector<bool> copyArray;
for (uint i = 0; i < goodIndices.size(); i++)
{
bool val = goodIndices[i];
copyArray.push_back(goodIndices[i]);
}
return copyArray;
}
void TextContours::setIndices(std::vector<bool> newIndices)
{
if (newIndices.size() == goodIndices.size())
{
for (uint i = 0; i < newIndices.size(); i++)
goodIndices[i] = newIndices[i];
}
else
{
assert("Invalid set operation on indices");
}
}

View File

@@ -0,0 +1,38 @@
/*
* File: textcontours.h
* Author: mhill
*
* Created on October 9, 2014, 7:40 PM
*/
#ifndef TEXTCONTOURS_H
#define TEXTCONTOURS_H
#include <vector>
#include "opencv2/imgproc/imgproc.hpp"
class TextContours {
public:
TextContours();
TextContours(cv::Mat threshold);
virtual ~TextContours();
void load(cv::Mat threshold);
std::vector<bool> goodIndices;
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
uint size();
int getGoodIndicesCount();
std::vector<bool> getIndicesCopy();
void setIndices(std::vector<bool> newIndices);
private:
};
#endif /* TEXTCONTOURS_H */