mirror of
https://github.com/kerberos-io/openalpr-base.git
synced 2025-10-07 01:42:49 +08:00
Split up the plate mask code into its own class
This commit is contained in:
@@ -24,6 +24,7 @@ set(lpr_source_files
|
|||||||
colorfilter.cpp
|
colorfilter.cpp
|
||||||
textdetection/characteranalysis.cpp
|
textdetection/characteranalysis.cpp
|
||||||
textdetection/characteranalysis2l.cpp
|
textdetection/characteranalysis2l.cpp
|
||||||
|
textdetection/platemask.cpp
|
||||||
pipeline_data.cpp
|
pipeline_data.cpp
|
||||||
trex.c
|
trex.c
|
||||||
cjson.c
|
cjson.c
|
||||||
|
@@ -38,7 +38,6 @@ CharacterRegion::CharacterRegion(PipelineData* pipeline_data)
|
|||||||
charAnalysis = new CharacterAnalysis(pipeline_data);
|
charAnalysis = new CharacterAnalysis(pipeline_data);
|
||||||
charAnalysis->analyze();
|
charAnalysis->analyze();
|
||||||
pipeline_data->plate_inverted = charAnalysis->thresholdsInverted;
|
pipeline_data->plate_inverted = charAnalysis->thresholdsInverted;
|
||||||
pipeline_data->plate_mask = charAnalysis->plateMask;
|
|
||||||
|
|
||||||
if (this->debug && charAnalysis->linePolygon.size() > 0)
|
if (this->debug && charAnalysis->linePolygon.size() > 0)
|
||||||
{
|
{
|
||||||
|
@@ -56,7 +56,9 @@ void LicensePlateCandidate::recognize()
|
|||||||
{
|
{
|
||||||
PlateLines plateLines(config);
|
PlateLines plateLines(config);
|
||||||
|
|
||||||
plateLines.processImage(pipeline_data->plate_mask, &charRegion, 1.10);
|
if (pipeline_data->hasPlateBorder)
|
||||||
|
plateLines.processImage(pipeline_data->plateBorderMask, &charRegion, 1.10);
|
||||||
|
|
||||||
plateLines.processImage(pipeline_data->crop_gray, &charRegion, 0.9);
|
plateLines.processImage(pipeline_data->crop_gray, &charRegion, 0.9);
|
||||||
|
|
||||||
PlateCorners cornerFinder(pipeline_data->crop_gray, &plateLines, &charRegion, config);
|
PlateCorners cornerFinder(pipeline_data->crop_gray, &plateLines, &charRegion, config);
|
||||||
|
@@ -23,7 +23,10 @@ class PipelineData
|
|||||||
cv::Rect regionOfInterest;
|
cv::Rect regionOfInterest;
|
||||||
|
|
||||||
cv::Mat crop_gray;
|
cv::Mat crop_gray;
|
||||||
cv::Mat plate_mask;
|
|
||||||
|
bool hasPlateBorder;
|
||||||
|
cv::Mat plateBorderMask;
|
||||||
|
|
||||||
std::vector<cv::Mat> thresholds;
|
std::vector<cv::Mat> thresholds;
|
||||||
|
|
||||||
std::vector<cv::Point2f> plate_corners;
|
std::vector<cv::Point2f> plate_corners;
|
||||||
|
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "characteranalysis.h"
|
#include "characteranalysis.h"
|
||||||
|
#include "platemask.h"
|
||||||
|
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@@ -27,7 +28,7 @@ CharacterAnalysis::CharacterAnalysis(PipelineData* pipeline_data)
|
|||||||
this->pipeline_data = pipeline_data;
|
this->pipeline_data = pipeline_data;
|
||||||
this->config = pipeline_data->config;
|
this->config = pipeline_data->config;
|
||||||
|
|
||||||
this->hasPlateMask = false;
|
this->isTwoLine = true;
|
||||||
|
|
||||||
if (this->config->debugCharAnalysis)
|
if (this->config->debugCharAnalysis)
|
||||||
cout << "Starting CharacterAnalysis identification" << endl;
|
cout << "Starting CharacterAnalysis identification" << endl;
|
||||||
@@ -92,9 +93,13 @@ void CharacterAnalysis::analyze()
|
|||||||
cout << " -- Character Analysis Filter Time: " << diffclock(startTime, endTime) << "ms." << endl;
|
cout << " -- Character Analysis Filter Time: " << diffclock(startTime, endTime) << "ms." << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->plateMask = findOuterBoxMask();
|
PlateMask plateMask(pipeline_data);
|
||||||
|
plateMask.findOuterBoxMask(charSegments, allContours, allHierarchy);
|
||||||
|
|
||||||
if (hasPlateMask)
|
pipeline_data->hasPlateBorder = plateMask.hasPlateMask;
|
||||||
|
pipeline_data->plateBorderMask = plateMask.getMask();
|
||||||
|
|
||||||
|
if (plateMask.hasPlateMask)
|
||||||
{
|
{
|
||||||
// Filter out bad contours now that we have an outer box mask...
|
// Filter out bad contours now that we have an outer box mask...
|
||||||
for (uint i = 0; i < pipeline_data->thresholds.size(); i++)
|
for (uint i = 0; i < pipeline_data->thresholds.size(); i++)
|
||||||
@@ -195,166 +200,7 @@ int CharacterAnalysis::getGoodIndicesCount(vector<bool> goodIndices)
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat CharacterAnalysis::findOuterBoxMask()
|
|
||||||
{
|
|
||||||
double min_parent_area = config->templateHeightPx * config->templateWidthPx * 0.10; // Needs to be at least 10% of the plate area to be considered.
|
|
||||||
|
|
||||||
int winningIndex = -1;
|
|
||||||
int winningParentId = -1;
|
|
||||||
int bestCharCount = 0;
|
|
||||||
double lowestArea = 99999999999999;
|
|
||||||
|
|
||||||
if (this->config->debugCharAnalysis)
|
|
||||||
cout << "CharacterAnalysis::findOuterBoxMask" << endl;
|
|
||||||
|
|
||||||
for (uint imgIndex = 0; imgIndex < allContours.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++)
|
|
||||||
{
|
|
||||||
if (charSegments[imgIndex][i]) charsRecognized++;
|
|
||||||
if (charSegments[imgIndex][i] && allHierarchy[imgIndex][i][3] != -1)
|
|
||||||
{
|
|
||||||
parentId = allHierarchy[imgIndex][i][3];
|
|
||||||
hasParent = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (charsRecognized == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (hasParent)
|
|
||||||
{
|
|
||||||
double boxArea = contourArea(allContours[imgIndex][parentId]);
|
|
||||||
if (boxArea < min_parent_area)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ((charsRecognized > bestCharCount) ||
|
|
||||||
(charsRecognized == bestCharCount && boxArea < lowestArea))
|
|
||||||
//(boxArea < lowestArea)
|
|
||||||
{
|
|
||||||
bestCharCount = charsRecognized;
|
|
||||||
winningIndex = imgIndex;
|
|
||||||
winningParentId = parentId;
|
|
||||||
lowestArea = boxArea;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->config->debugCharAnalysis)
|
|
||||||
cout << "Winning image index (findOuterBoxMask) is: " << winningIndex << endl;
|
|
||||||
|
|
||||||
if (winningIndex != -1 && bestCharCount >= 3)
|
|
||||||
{
|
|
||||||
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 j = 0; j < allContours[winningIndex].size(); j++)
|
|
||||||
{
|
|
||||||
if (allHierarchy[winningIndex][j][3] == winningParentId)
|
|
||||||
{
|
|
||||||
double arclength = arcLength(allContours[winningIndex][j], false);
|
|
||||||
if (arclength > longestChildLength)
|
|
||||||
{
|
|
||||||
longestChildIndex = j;
|
|
||||||
longestChildLength = arclength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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],
|
|
||||||
winningParentId, // draw this contour
|
|
||||||
cv::Scalar(255,255,255), // in
|
|
||||||
CV_FILLED,
|
|
||||||
8,
|
|
||||||
allHierarchy[winningIndex],
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
// Morph Open the mask to get rid of any little connectors to non-plate portions
|
|
||||||
int morph_elem = 2;
|
|
||||||
int morph_size = 3;
|
|
||||||
Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
|
|
||||||
|
|
||||||
//morphologyEx( mask, mask, MORPH_CLOSE, element );
|
|
||||||
morphologyEx( mask, mask, MORPH_OPEN, element );
|
|
||||||
|
|
||||||
//morph_size = 1;
|
|
||||||
//element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
|
|
||||||
//dilate(mask, mask, element);
|
|
||||||
|
|
||||||
// Drawing the edge black effectively erodes the image. This may clip off some extra junk from the edges.
|
|
||||||
// We'll want to do the contour again and find the larges one so that we remove the clipped portion.
|
|
||||||
|
|
||||||
vector<vector<Point> > contoursSecondRound;
|
|
||||||
|
|
||||||
findContours(mask, contoursSecondRound, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
|
|
||||||
int biggestContourIndex = -1;
|
|
||||||
double largestArea = 0;
|
|
||||||
for (uint c = 0; c < contoursSecondRound.size(); c++)
|
|
||||||
{
|
|
||||||
double area = contourArea(contoursSecondRound[c]);
|
|
||||||
if (area > largestArea)
|
|
||||||
{
|
|
||||||
biggestContourIndex = c;
|
|
||||||
largestArea = area;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (biggestContourIndex != -1)
|
|
||||||
{
|
|
||||||
mask = Mat::zeros(pipeline_data->thresholds[winningIndex].size(), CV_8U);
|
|
||||||
|
|
||||||
vector<Point> smoothedMaskPoints;
|
|
||||||
approxPolyDP(contoursSecondRound[biggestContourIndex], smoothedMaskPoints, 2, true);
|
|
||||||
|
|
||||||
vector<vector<Point> > tempvec;
|
|
||||||
tempvec.push_back(smoothedMaskPoints);
|
|
||||||
//fillPoly(mask, smoothedMaskPoints.data(), smoothedMaskPoints, Scalar(255,255,255));
|
|
||||||
drawContours(mask, tempvec,
|
|
||||||
0, // draw this contour
|
|
||||||
cv::Scalar(255,255,255), // in
|
|
||||||
CV_FILLED,
|
|
||||||
8,
|
|
||||||
allHierarchy[winningIndex],
|
|
||||||
0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->config->debugCharAnalysis)
|
|
||||||
{
|
|
||||||
vector<Mat> debugImgs;
|
|
||||||
Mat debugImgMasked = Mat::zeros(pipeline_data->thresholds[winningIndex].size(), CV_8U);
|
|
||||||
|
|
||||||
pipeline_data->thresholds[winningIndex].copyTo(debugImgMasked, mask);
|
|
||||||
|
|
||||||
debugImgs.push_back(mask);
|
|
||||||
debugImgs.push_back(pipeline_data->thresholds[winningIndex]);
|
|
||||||
debugImgs.push_back(debugImgMasked);
|
|
||||||
|
|
||||||
Mat dashboard = drawImageDashboard(debugImgs, CV_8U, 1);
|
|
||||||
displayImage(config, "Winning outer box", dashboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
hasPlateMask = true;
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasPlateMask = false;
|
|
||||||
Mat fullMask = Mat::zeros(pipeline_data->thresholds[0].size(), CV_8U);
|
|
||||||
bitwise_not(fullMask, fullMask);
|
|
||||||
return fullMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat CharacterAnalysis::getCharacterMask()
|
Mat CharacterAnalysis::getCharacterMask()
|
||||||
{
|
{
|
||||||
@@ -827,13 +673,15 @@ std::vector< bool > CharacterAnalysis::filterByOuterMask(vector< vector< Point >
|
|||||||
float MINIMUM_PERCENT_LEFT_AFTER_MASK = 0.1;
|
float MINIMUM_PERCENT_LEFT_AFTER_MASK = 0.1;
|
||||||
float MINIMUM_PERCENT_OF_CHARS_INSIDE_PLATE_MASK = 0.6;
|
float MINIMUM_PERCENT_OF_CHARS_INSIDE_PLATE_MASK = 0.6;
|
||||||
|
|
||||||
if (hasPlateMask == false)
|
if (this->pipeline_data->hasPlateBorder == false)
|
||||||
return goodIndices;
|
return goodIndices;
|
||||||
|
|
||||||
vector<bool> passingIndices;
|
vector<bool> passingIndices;
|
||||||
for (uint i = 0; i < goodIndices.size(); i++)
|
for (uint i = 0; i < goodIndices.size(); i++)
|
||||||
passingIndices.push_back(false);
|
passingIndices.push_back(false);
|
||||||
|
|
||||||
|
cv::Mat plateMask = pipeline_data->plateBorderMask;
|
||||||
|
|
||||||
Mat tempMaskedContour = Mat::zeros(plateMask.size(), CV_8U);
|
Mat tempMaskedContour = Mat::zeros(plateMask.size(), CV_8U);
|
||||||
Mat tempFullContour = Mat::zeros(plateMask.size(), CV_8U);
|
Mat tempFullContour = Mat::zeros(plateMask.size(), CV_8U);
|
||||||
|
|
||||||
|
@@ -32,8 +32,6 @@ class CharacterAnalysis
|
|||||||
CharacterAnalysis(PipelineData* pipeline_data);
|
CharacterAnalysis(PipelineData* pipeline_data);
|
||||||
virtual ~CharacterAnalysis();
|
virtual ~CharacterAnalysis();
|
||||||
|
|
||||||
bool hasPlateMask;
|
|
||||||
cv::Mat plateMask;
|
|
||||||
|
|
||||||
cv::Mat bestThreshold;
|
cv::Mat bestThreshold;
|
||||||
std::vector<std::vector<cv::Point> > bestContours;
|
std::vector<std::vector<cv::Point> > bestContours;
|
||||||
@@ -52,6 +50,7 @@ class CharacterAnalysis
|
|||||||
LineSegment charBoxRight;
|
LineSegment charBoxRight;
|
||||||
|
|
||||||
bool thresholdsInverted;
|
bool thresholdsInverted;
|
||||||
|
bool isTwoLine;
|
||||||
|
|
||||||
std::vector<std::vector<std::vector<cv::Point> > > allContours;
|
std::vector<std::vector<std::vector<cv::Point> > > allContours;
|
||||||
std::vector<std::vector<cv::Vec4i> > allHierarchy;
|
std::vector<std::vector<cv::Vec4i> > allHierarchy;
|
||||||
|
201
src/openalpr/textdetection/platemask.cpp
Normal file
201
src/openalpr/textdetection/platemask.cpp
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 New Designs Unlimited, LLC
|
||||||
|
* Opensource Automated License Plate Recognition [http://www.openalpr.com]
|
||||||
|
*
|
||||||
|
* This file is part of OpenAlpr.
|
||||||
|
*
|
||||||
|
* OpenAlpr is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License
|
||||||
|
* version 3 as published by the Free Software Foundation
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "platemask.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace cv;
|
||||||
|
|
||||||
|
PlateMask::PlateMask(PipelineData* pipeline_data) {
|
||||||
|
this->pipeline_data = pipeline_data;
|
||||||
|
this->hasPlateMask = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PlateMask::~PlateMask() {
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Mat PlateMask::getMask() {
|
||||||
|
return this->plateMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlateMask::findOuterBoxMask(vector<vector<bool> > charSegments,
|
||||||
|
vector<vector<vector<Point> > > allContours,
|
||||||
|
vector<vector<Vec4i> > allHierarchy)
|
||||||
|
{
|
||||||
|
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.
|
||||||
|
|
||||||
|
int winningIndex = -1;
|
||||||
|
int winningParentId = -1;
|
||||||
|
int bestCharCount = 0;
|
||||||
|
double lowestArea = 99999999999999;
|
||||||
|
|
||||||
|
if (pipeline_data->config->debugCharAnalysis)
|
||||||
|
cout << "CharacterAnalysis::findOuterBoxMask" << endl;
|
||||||
|
|
||||||
|
for (uint imgIndex = 0; imgIndex < allContours.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++)
|
||||||
|
{
|
||||||
|
if (charSegments[imgIndex][i]) charsRecognized++;
|
||||||
|
if (charSegments[imgIndex][i] && allHierarchy[imgIndex][i][3] != -1)
|
||||||
|
{
|
||||||
|
parentId = allHierarchy[imgIndex][i][3];
|
||||||
|
hasParent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (charsRecognized == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (hasParent)
|
||||||
|
{
|
||||||
|
double boxArea = contourArea(allContours[imgIndex][parentId]);
|
||||||
|
if (boxArea < min_parent_area)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((charsRecognized > bestCharCount) ||
|
||||||
|
(charsRecognized == bestCharCount && boxArea < lowestArea))
|
||||||
|
//(boxArea < lowestArea)
|
||||||
|
{
|
||||||
|
bestCharCount = charsRecognized;
|
||||||
|
winningIndex = imgIndex;
|
||||||
|
winningParentId = parentId;
|
||||||
|
lowestArea = boxArea;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pipeline_data->config->debugCharAnalysis)
|
||||||
|
cout << "Winning image index (findOuterBoxMask) is: " << winningIndex << endl;
|
||||||
|
|
||||||
|
if (winningIndex != -1 && bestCharCount >= 3)
|
||||||
|
{
|
||||||
|
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 j = 0; j < allContours[winningIndex].size(); j++)
|
||||||
|
{
|
||||||
|
if (allHierarchy[winningIndex][j][3] == winningParentId)
|
||||||
|
{
|
||||||
|
double arclength = arcLength(allContours[winningIndex][j], false);
|
||||||
|
if (arclength > longestChildLength)
|
||||||
|
{
|
||||||
|
longestChildIndex = j;
|
||||||
|
longestChildLength = arclength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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],
|
||||||
|
winningParentId, // draw this contour
|
||||||
|
cv::Scalar(255,255,255), // in
|
||||||
|
CV_FILLED,
|
||||||
|
8,
|
||||||
|
allHierarchy[winningIndex],
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
// Morph Open the mask to get rid of any little connectors to non-plate portions
|
||||||
|
int morph_elem = 2;
|
||||||
|
int morph_size = 3;
|
||||||
|
Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
|
||||||
|
|
||||||
|
//morphologyEx( mask, mask, MORPH_CLOSE, element );
|
||||||
|
morphologyEx( mask, mask, MORPH_OPEN, element );
|
||||||
|
|
||||||
|
//morph_size = 1;
|
||||||
|
//element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
|
||||||
|
//dilate(mask, mask, element);
|
||||||
|
|
||||||
|
// Drawing the edge black effectively erodes the image. This may clip off some extra junk from the edges.
|
||||||
|
// We'll want to do the contour again and find the larges one so that we remove the clipped portion.
|
||||||
|
|
||||||
|
vector<vector<Point> > contoursSecondRound;
|
||||||
|
|
||||||
|
findContours(mask, contoursSecondRound, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
|
||||||
|
int biggestContourIndex = -1;
|
||||||
|
double largestArea = 0;
|
||||||
|
for (uint c = 0; c < contoursSecondRound.size(); c++)
|
||||||
|
{
|
||||||
|
double area = contourArea(contoursSecondRound[c]);
|
||||||
|
if (area > largestArea)
|
||||||
|
{
|
||||||
|
biggestContourIndex = c;
|
||||||
|
largestArea = area;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (biggestContourIndex != -1)
|
||||||
|
{
|
||||||
|
mask = Mat::zeros(pipeline_data->thresholds[winningIndex].size(), CV_8U);
|
||||||
|
|
||||||
|
vector<Point> smoothedMaskPoints;
|
||||||
|
approxPolyDP(contoursSecondRound[biggestContourIndex], smoothedMaskPoints, 2, true);
|
||||||
|
|
||||||
|
vector<vector<Point> > tempvec;
|
||||||
|
tempvec.push_back(smoothedMaskPoints);
|
||||||
|
//fillPoly(mask, smoothedMaskPoints.data(), smoothedMaskPoints, Scalar(255,255,255));
|
||||||
|
drawContours(mask, tempvec,
|
||||||
|
0, // draw this contour
|
||||||
|
cv::Scalar(255,255,255), // in
|
||||||
|
CV_FILLED,
|
||||||
|
8,
|
||||||
|
allHierarchy[winningIndex],
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pipeline_data->config->debugCharAnalysis)
|
||||||
|
{
|
||||||
|
vector<Mat> debugImgs;
|
||||||
|
Mat debugImgMasked = Mat::zeros(pipeline_data->thresholds[winningIndex].size(), CV_8U);
|
||||||
|
|
||||||
|
pipeline_data->thresholds[winningIndex].copyTo(debugImgMasked, mask);
|
||||||
|
|
||||||
|
debugImgs.push_back(mask);
|
||||||
|
debugImgs.push_back(pipeline_data->thresholds[winningIndex]);
|
||||||
|
debugImgs.push_back(debugImgMasked);
|
||||||
|
|
||||||
|
Mat dashboard = drawImageDashboard(debugImgs, CV_8U, 1);
|
||||||
|
displayImage(pipeline_data->config, "Winning outer box", dashboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPlateMask = true;
|
||||||
|
this->plateMask = mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPlateMask = false;
|
||||||
|
Mat fullMask = Mat::zeros(pipeline_data->thresholds[0].size(), CV_8U);
|
||||||
|
bitwise_not(fullMask, fullMask);
|
||||||
|
|
||||||
|
this->plateMask = fullMask;
|
||||||
|
}
|
46
src/openalpr/textdetection/platemask.h
Normal file
46
src/openalpr/textdetection/platemask.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 New Designs Unlimited, LLC
|
||||||
|
* Opensource Automated License Plate Recognition [http://www.openalpr.com]
|
||||||
|
*
|
||||||
|
* This file is part of OpenAlpr.
|
||||||
|
*
|
||||||
|
* OpenAlpr is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License
|
||||||
|
* version 3 as published by the Free Software Foundation
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OPENALPR_PLATEMASK_H
|
||||||
|
#define OPENALPR_PLATEMASK_H
|
||||||
|
|
||||||
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
|
#include "pipeline_data.h"
|
||||||
|
|
||||||
|
class PlateMask {
|
||||||
|
public:
|
||||||
|
PlateMask(PipelineData* pipeline_data);
|
||||||
|
virtual ~PlateMask();
|
||||||
|
|
||||||
|
bool hasPlateMask;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
PipelineData* pipeline_data;
|
||||||
|
cv::Mat plateMask;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* OPENALPR_PLATEMASK_H */
|
||||||
|
|
Reference in New Issue
Block a user