mirror of
https://github.com/kerberos-io/openalpr-base.git
synced 2025-10-06 14:16:49 +08:00
Refactored characteranalysis. Using TextLine class to hold character info. Needed for multiline char support
This commit is contained in:
@@ -26,6 +26,7 @@ set(lpr_source_files
|
|||||||
textdetection/characteranalysis2l.cpp
|
textdetection/characteranalysis2l.cpp
|
||||||
textdetection/platemask.cpp
|
textdetection/platemask.cpp
|
||||||
textdetection/textcontours.cpp
|
textdetection/textcontours.cpp
|
||||||
|
textdetection/textline.cpp
|
||||||
pipeline_data.cpp
|
pipeline_data.cpp
|
||||||
trex.c
|
trex.c
|
||||||
cjson.c
|
cjson.c
|
||||||
|
@@ -76,7 +76,8 @@ CharacterRegion::CharacterRegion(PipelineData* pipeline_data)
|
|||||||
else if (charSegmentCount < 5)
|
else if (charSegmentCount < 5)
|
||||||
confidenceDrainers += (5 - charSegmentCount) * 10;
|
confidenceDrainers += (5 - charSegmentCount) * 10;
|
||||||
|
|
||||||
int absangle = abs(charAnalysis->topLine.angle);
|
// Use the angle for the first line -- assume they'll always be parallel for multi-line plates
|
||||||
|
int absangle = abs(pipeline_data->textLines[0].topLine.angle);
|
||||||
if (absangle > config->maxPlateAngleDegrees)
|
if (absangle > config->maxPlateAngleDegrees)
|
||||||
confidenceDrainers += 91;
|
confidenceDrainers += 91;
|
||||||
else if (absangle > 1)
|
else if (absangle > 1)
|
||||||
@@ -102,38 +103,3 @@ CharacterRegion::~CharacterRegion()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LineSegment CharacterRegion::getTopLine()
|
|
||||||
{
|
|
||||||
return charAnalysis->topLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
LineSegment CharacterRegion::getBottomLine()
|
|
||||||
{
|
|
||||||
return charAnalysis->bottomLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<Point> CharacterRegion::getCharArea()
|
|
||||||
{
|
|
||||||
return charAnalysis->charArea;
|
|
||||||
}
|
|
||||||
|
|
||||||
LineSegment CharacterRegion::getCharBoxTop()
|
|
||||||
{
|
|
||||||
return charAnalysis->charBoxTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
LineSegment CharacterRegion::getCharBoxBottom()
|
|
||||||
{
|
|
||||||
return charAnalysis->charBoxBottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
LineSegment CharacterRegion::getCharBoxLeft()
|
|
||||||
{
|
|
||||||
return charAnalysis->charBoxLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
LineSegment CharacterRegion::getCharBoxRight()
|
|
||||||
{
|
|
||||||
return charAnalysis->charBoxRight;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@@ -39,7 +39,6 @@ class CharacterRegion
|
|||||||
|
|
||||||
LineSegment getTopLine();
|
LineSegment getTopLine();
|
||||||
LineSegment getBottomLine();
|
LineSegment getBottomLine();
|
||||||
std::vector<cv::Point> getCharArea();
|
|
||||||
|
|
||||||
LineSegment getCharBoxTop();
|
LineSegment getCharBoxTop();
|
||||||
LineSegment getCharBoxBottom();
|
LineSegment getCharBoxBottom();
|
||||||
@@ -54,20 +53,6 @@ class CharacterRegion
|
|||||||
CharacterAnalysis *charAnalysis;
|
CharacterAnalysis *charAnalysis;
|
||||||
cv::Mat findOuterBoxMask(std::vector<cv::Mat> thresholds, std::vector<std::vector<std::vector<cv::Point> > > allContours, std::vector<std::vector<cv::Vec4i> > allHierarchy);
|
cv::Mat findOuterBoxMask(std::vector<cv::Mat> thresholds, std::vector<std::vector<std::vector<cv::Point> > > allContours, std::vector<std::vector<cv::Vec4i> > allHierarchy);
|
||||||
|
|
||||||
std::vector<bool> filter(cv::Mat img, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy);
|
|
||||||
std::vector<bool> filterByBoxSize(cv::Mat img, std::vector<std::vector<cv::Point> > contours, std::vector<bool> goodIndices, float minHeightPx, float 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<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);
|
|
||||||
cv::Mat getCharacterMask(cv::Mat img, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, std::vector<bool> goodIndices);
|
|
||||||
|
|
||||||
std::vector<cv::Rect> wrapContours(std::vector<std::vector<cv::Point> > contours);
|
|
||||||
bool verifySize(cv::Mat r, float minHeightPx, float maxHeightPx);
|
|
||||||
|
|
||||||
int getGoodIndicesCount(std::vector<bool> goodIndices);
|
|
||||||
|
|
||||||
bool isPlateInverted(cv::Mat threshold, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, std::vector<bool> goodIndices);
|
bool isPlateInverted(cv::Mat threshold, std::vector<std::vector<cv::Point> > contours, std::vector<cv::Vec4i> hierarchy, std::vector<bool> goodIndices);
|
||||||
|
|
||||||
|
@@ -54,14 +54,14 @@ void LicensePlateCandidate::recognize()
|
|||||||
|
|
||||||
if (charRegion.confidence > 10)
|
if (charRegion.confidence > 10)
|
||||||
{
|
{
|
||||||
PlateLines plateLines(config);
|
PlateLines plateLines(pipeline_data);
|
||||||
|
|
||||||
if (pipeline_data->hasPlateBorder)
|
if (pipeline_data->hasPlateBorder)
|
||||||
plateLines.processImage(pipeline_data->plateBorderMask, &charRegion, 1.10);
|
plateLines.processImage(pipeline_data->plateBorderMask, 1.10);
|
||||||
|
|
||||||
plateLines.processImage(pipeline_data->crop_gray, &charRegion, 0.9);
|
plateLines.processImage(pipeline_data->crop_gray, 0.9);
|
||||||
|
|
||||||
PlateCorners cornerFinder(pipeline_data->crop_gray, &plateLines, &charRegion, config);
|
PlateCorners cornerFinder(pipeline_data->crop_gray, &plateLines, pipeline_data);
|
||||||
vector<Point> smallPlateCorners = cornerFinder.findPlateCorners();
|
vector<Point> smallPlateCorners = cornerFinder.findPlateCorners();
|
||||||
|
|
||||||
if (cornerFinder.confidence > 0)
|
if (cornerFinder.confidence > 0)
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
#include "opencv2/imgproc/imgproc.hpp"
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "textdetection/textline.h"
|
||||||
|
|
||||||
class PipelineData
|
class PipelineData
|
||||||
{
|
{
|
||||||
@@ -26,6 +27,7 @@ class PipelineData
|
|||||||
|
|
||||||
bool hasPlateBorder;
|
bool hasPlateBorder;
|
||||||
cv::Mat plateBorderMask;
|
cv::Mat plateBorderMask;
|
||||||
|
std::vector<TextLine> textLines;
|
||||||
|
|
||||||
std::vector<cv::Mat> thresholds;
|
std::vector<cv::Mat> thresholds;
|
||||||
|
|
||||||
|
@@ -22,26 +22,25 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, CharacterRegion* charRegion, Config* config)
|
PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, PipelineData* pipelineData)
|
||||||
{
|
{
|
||||||
this->config = config;
|
this->pipelineData = pipelineData;
|
||||||
|
|
||||||
if (this->config->debugPlateCorners)
|
if (pipelineData->config->debugPlateCorners)
|
||||||
cout << "PlateCorners constructor" << endl;
|
cout << "PlateCorners constructor" << endl;
|
||||||
|
|
||||||
this->inputImage = inputImage;
|
this->inputImage = inputImage;
|
||||||
this->plateLines = plateLines;
|
this->plateLines = plateLines;
|
||||||
this->charRegion = charRegion;
|
|
||||||
|
|
||||||
this->bestHorizontalScore = 9999999999999;
|
this->bestHorizontalScore = 9999999999999;
|
||||||
this->bestVerticalScore = 9999999999999;
|
this->bestVerticalScore = 9999999999999;
|
||||||
|
|
||||||
Point topPoint = charRegion->getTopLine().midpoint();
|
Point topPoint = pipelineData->textLines[0].topLine.midpoint();
|
||||||
Point bottomPoint = charRegion->getBottomLine().closestPointOnSegmentTo(topPoint);
|
Point bottomPoint = pipelineData->textLines[0].bottomLine.closestPointOnSegmentTo(topPoint);
|
||||||
this->charHeight = distanceBetweenPoints(topPoint, bottomPoint);
|
this->charHeight = distanceBetweenPoints(topPoint, bottomPoint);
|
||||||
|
|
||||||
|
|
||||||
this->charAngle = angleBetweenPoints(charRegion->getCharArea()[0], charRegion->getCharArea()[1]);
|
this->charAngle = angleBetweenPoints(pipelineData->textLines[0].textArea[0], pipelineData->textLines[0].textArea[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
PlateCorners::~PlateCorners()
|
PlateCorners::~PlateCorners()
|
||||||
@@ -50,7 +49,7 @@ PlateCorners::~PlateCorners()
|
|||||||
|
|
||||||
vector<Point> PlateCorners::findPlateCorners()
|
vector<Point> PlateCorners::findPlateCorners()
|
||||||
{
|
{
|
||||||
if (this->config->debugPlateCorners)
|
if (pipelineData->config->debugPlateCorners)
|
||||||
cout << "PlateCorners::findPlateCorners" << endl;
|
cout << "PlateCorners::findPlateCorners" << endl;
|
||||||
|
|
||||||
timespec startTime;
|
timespec startTime;
|
||||||
@@ -81,21 +80,21 @@ vector<Point> PlateCorners::findPlateCorners()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->config->debugPlateCorners)
|
if (pipelineData->config->debugPlateCorners)
|
||||||
{
|
{
|
||||||
cout << "Drawing debug stuff..." << endl;
|
cout << "Drawing debug stuff..." << endl;
|
||||||
|
|
||||||
Mat imgCorners = Mat(inputImage.size(), inputImage.type());
|
Mat imgCorners = Mat(inputImage.size(), inputImage.type());
|
||||||
inputImage.copyTo(imgCorners);
|
inputImage.copyTo(imgCorners);
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
circle(imgCorners, charRegion->getCharArea()[i], 2, Scalar(0, 0, 0));
|
circle(imgCorners, pipelineData->textLines[0].textArea[i], 2, Scalar(0, 0, 0));
|
||||||
|
|
||||||
line(imgCorners, this->bestTop.p1, this->bestTop.p2, Scalar(255, 0, 0), 1, CV_AA);
|
line(imgCorners, this->bestTop.p1, this->bestTop.p2, Scalar(255, 0, 0), 1, CV_AA);
|
||||||
line(imgCorners, this->bestRight.p1, this->bestRight.p2, Scalar(0, 0, 255), 1, CV_AA);
|
line(imgCorners, this->bestRight.p1, this->bestRight.p2, Scalar(0, 0, 255), 1, CV_AA);
|
||||||
line(imgCorners, this->bestBottom.p1, this->bestBottom.p2, Scalar(0, 0, 255), 1, CV_AA);
|
line(imgCorners, this->bestBottom.p1, this->bestBottom.p2, Scalar(0, 0, 255), 1, CV_AA);
|
||||||
line(imgCorners, this->bestLeft.p1, this->bestLeft.p2, Scalar(255, 0, 0), 1, CV_AA);
|
line(imgCorners, this->bestLeft.p1, this->bestLeft.p2, Scalar(255, 0, 0), 1, CV_AA);
|
||||||
|
|
||||||
displayImage(config, "Winning top/bottom Boundaries", imgCorners);
|
displayImage(pipelineData->config, "Winning top/bottom Boundaries", imgCorners);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a left/right edge has been established.
|
// Check if a left/right edge has been established.
|
||||||
@@ -112,7 +111,7 @@ vector<Point> PlateCorners::findPlateCorners()
|
|||||||
corners.push_back(bestBottom.intersection(bestRight));
|
corners.push_back(bestBottom.intersection(bestRight));
|
||||||
corners.push_back(bestBottom.intersection(bestLeft));
|
corners.push_back(bestBottom.intersection(bestLeft));
|
||||||
|
|
||||||
if (config->debugTiming)
|
if (pipelineData->config->debugTiming)
|
||||||
{
|
{
|
||||||
timespec endTime;
|
timespec endTime;
|
||||||
getTime(&endTime);
|
getTime(&endTime);
|
||||||
@@ -129,7 +128,7 @@ void PlateCorners::scoreVerticals(int v1, int v2)
|
|||||||
LineSegment left;
|
LineSegment left;
|
||||||
LineSegment right;
|
LineSegment right;
|
||||||
|
|
||||||
float charHeightToPlateWidthRatio = config->plateWidthMM / config->charHeightMM;
|
float charHeightToPlateWidthRatio = pipelineData->config->plateWidthMM / pipelineData->config->charHeightMM;
|
||||||
float idealPixelWidth = this->charHeight * (charHeightToPlateWidthRatio * 1.03); // Add 3% so we don't clip any characters
|
float idealPixelWidth = this->charHeight * (charHeightToPlateWidthRatio * 1.03); // Add 3% so we don't clip any characters
|
||||||
|
|
||||||
float confidenceDiff = 0;
|
float confidenceDiff = 0;
|
||||||
@@ -138,8 +137,8 @@ void PlateCorners::scoreVerticals(int v1, int v2)
|
|||||||
if (v1 == NO_LINE && v2 == NO_LINE)
|
if (v1 == NO_LINE && v2 == NO_LINE)
|
||||||
{
|
{
|
||||||
//return;
|
//return;
|
||||||
Point centerTop = charRegion->getCharBoxTop().midpoint();
|
Point centerTop = pipelineData->textLines[0].charBoxTop.midpoint();
|
||||||
Point centerBottom = charRegion->getCharBoxBottom().midpoint();
|
Point centerBottom = pipelineData->textLines[0].charBoxBottom.midpoint();
|
||||||
LineSegment centerLine = LineSegment(centerBottom.x, centerBottom.y, centerTop.x, centerTop.y);
|
LineSegment centerLine = LineSegment(centerBottom.x, centerBottom.y, centerTop.x, centerTop.y);
|
||||||
|
|
||||||
left = centerLine.getParallelLine(idealPixelWidth / 2);
|
left = centerLine.getParallelLine(idealPixelWidth / 2);
|
||||||
@@ -174,11 +173,11 @@ void PlateCorners::scoreVerticals(int v1, int v2)
|
|||||||
score += missingSegmentPenalty;
|
score += missingSegmentPenalty;
|
||||||
|
|
||||||
// Make sure this line is to the left of our license plate letters
|
// Make sure this line is to the left of our license plate letters
|
||||||
if (left.isPointBelowLine(charRegion->getCharBoxLeft().midpoint()) == false)
|
if (left.isPointBelowLine(pipelineData->textLines[0].charBoxLeft.midpoint()) == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Make sure this line is to the right of our license plate letters
|
// Make sure this line is to the right of our license plate letters
|
||||||
if (right.isPointBelowLine(charRegion->getCharBoxRight().midpoint()))
|
if (right.isPointBelowLine(pipelineData->textLines[0].charBoxRight.midpoint()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
@@ -212,8 +211,8 @@ void PlateCorners::scoreVerticals(int v1, int v2)
|
|||||||
// SCORE the shape wrt character position and height relative to position
|
// SCORE the shape wrt character position and height relative to position
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
Point leftMidLinePoint = left.closestPointOnSegmentTo(charRegion->getCharBoxLeft().midpoint());
|
Point leftMidLinePoint = left.closestPointOnSegmentTo(pipelineData->textLines[0].charBoxLeft.midpoint());
|
||||||
Point rightMidLinePoint = right.closestPointOnSegmentTo(charRegion->getCharBoxRight().midpoint());
|
Point rightMidLinePoint = right.closestPointOnSegmentTo(pipelineData->textLines[0].charBoxRight.midpoint());
|
||||||
|
|
||||||
float plateDistance = abs(idealPixelWidth - distanceBetweenPoints(leftMidLinePoint, rightMidLinePoint));
|
float plateDistance = abs(idealPixelWidth - distanceBetweenPoints(leftMidLinePoint, rightMidLinePoint));
|
||||||
|
|
||||||
@@ -223,7 +222,7 @@ void PlateCorners::scoreVerticals(int v1, int v2)
|
|||||||
{
|
{
|
||||||
float scorecomponent;
|
float scorecomponent;
|
||||||
|
|
||||||
if (this->config->debugPlateCorners)
|
if (pipelineData->config->debugPlateCorners)
|
||||||
{
|
{
|
||||||
cout << "xx xx Score: charHeight " << this->charHeight << endl;
|
cout << "xx xx Score: charHeight " << this->charHeight << endl;
|
||||||
cout << "xx xx Score: idealwidth " << idealPixelWidth << endl;
|
cout << "xx xx Score: idealwidth " << idealPixelWidth << endl;
|
||||||
@@ -278,7 +277,7 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
|
|||||||
LineSegment top;
|
LineSegment top;
|
||||||
LineSegment bottom;
|
LineSegment bottom;
|
||||||
|
|
||||||
float charHeightToPlateHeightRatio = config->plateHeightMM / config->charHeightMM;
|
float charHeightToPlateHeightRatio = pipelineData->config->plateHeightMM / pipelineData->config->charHeightMM;
|
||||||
float idealPixelHeight = this->charHeight * charHeightToPlateHeightRatio;
|
float idealPixelHeight = this->charHeight * charHeightToPlateHeightRatio;
|
||||||
|
|
||||||
float confidenceDiff = 0;
|
float confidenceDiff = 0;
|
||||||
@@ -287,8 +286,8 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
|
|||||||
if (h1 == NO_LINE && h2 == NO_LINE)
|
if (h1 == NO_LINE && h2 == NO_LINE)
|
||||||
{
|
{
|
||||||
// return;
|
// return;
|
||||||
Point centerLeft = charRegion->getCharBoxLeft().midpoint();
|
Point centerLeft = pipelineData->textLines[0].charBoxLeft.midpoint();
|
||||||
Point centerRight = charRegion->getCharBoxRight().midpoint();
|
Point centerRight = pipelineData->textLines[0].charBoxRight.midpoint();
|
||||||
LineSegment centerLine = LineSegment(centerLeft.x, centerLeft.y, centerRight.x, centerRight.y);
|
LineSegment centerLine = LineSegment(centerLeft.x, centerLeft.y, centerRight.x, centerRight.y);
|
||||||
|
|
||||||
top = centerLine.getParallelLine(idealPixelHeight / 2);
|
top = centerLine.getParallelLine(idealPixelHeight / 2);
|
||||||
@@ -323,11 +322,11 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
|
|||||||
score += missingSegmentPenalty;
|
score += missingSegmentPenalty;
|
||||||
|
|
||||||
// Make sure this line is above our license plate letters
|
// Make sure this line is above our license plate letters
|
||||||
if (top.isPointBelowLine(charRegion->getCharBoxTop().midpoint()) == false)
|
if (top.isPointBelowLine(pipelineData->textLines[0].charBoxTop.midpoint()) == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Make sure this line is below our license plate letters
|
// Make sure this line is below our license plate letters
|
||||||
if (bottom.isPointBelowLine(charRegion->getCharBoxBottom().midpoint()))
|
if (bottom.isPointBelowLine(pipelineData->textLines[0].charBoxBottom.midpoint()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// We now have 4 possible lines. Let's put them to the test and score them...
|
// We now have 4 possible lines. Let's put them to the test and score them...
|
||||||
@@ -353,7 +352,7 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
|
|||||||
// Get the height difference
|
// Get the height difference
|
||||||
|
|
||||||
float heightRatio = charHeight / plateHeightPx;
|
float heightRatio = charHeight / plateHeightPx;
|
||||||
float idealHeightRatio = (config->charHeightMM / config->plateHeightMM);
|
float idealHeightRatio = (pipelineData->config->charHeightMM / pipelineData->config->plateHeightMM);
|
||||||
//if (leftRatio < MIN_CHAR_HEIGHT_RATIO || leftRatio > MAX_CHAR_HEIGHT_RATIO || rightRatio < MIN_CHAR_HEIGHT_RATIO || rightRatio > MAX_CHAR_HEIGHT_RATIO)
|
//if (leftRatio < MIN_CHAR_HEIGHT_RATIO || leftRatio > MAX_CHAR_HEIGHT_RATIO || rightRatio < MIN_CHAR_HEIGHT_RATIO || rightRatio > MAX_CHAR_HEIGHT_RATIO)
|
||||||
float heightRatioDiff = abs(heightRatio - idealHeightRatio);
|
float heightRatioDiff = abs(heightRatio - idealHeightRatio);
|
||||||
// Ideal ratio == ~.45
|
// Ideal ratio == ~.45
|
||||||
@@ -373,7 +372,7 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
|
|||||||
// SCORE the middliness of the stuff. We want our top and bottom line to have the characters right towards the middle
|
// SCORE the middliness of the stuff. We want our top and bottom line to have the characters right towards the middle
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
Point charAreaMidPoint = charRegion->getCharBoxLeft().midpoint();
|
Point charAreaMidPoint = pipelineData->textLines[0].charBoxLeft.midpoint();
|
||||||
Point topLineSpot = top.closestPointOnSegmentTo(charAreaMidPoint);
|
Point topLineSpot = top.closestPointOnSegmentTo(charAreaMidPoint);
|
||||||
Point botLineSpot = bottom.closestPointOnSegmentTo(charAreaMidPoint);
|
Point botLineSpot = bottom.closestPointOnSegmentTo(charAreaMidPoint);
|
||||||
|
|
||||||
@@ -406,7 +405,7 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
|
|||||||
{
|
{
|
||||||
float scorecomponent;
|
float scorecomponent;
|
||||||
|
|
||||||
if (this->config->debugPlateCorners)
|
if (pipelineData->config->debugPlateCorners)
|
||||||
{
|
{
|
||||||
cout << "xx xx Score: charHeight " << this->charHeight << endl;
|
cout << "xx xx Score: charHeight " << this->charHeight << endl;
|
||||||
cout << "xx xx Score: idealHeight " << idealPixelHeight << endl;
|
cout << "xx xx Score: idealHeight " << idealPixelHeight << endl;
|
||||||
|
@@ -47,7 +47,7 @@ class PlateCorners
|
|||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PlateCorners(cv::Mat inputImage, PlateLines* plateLines, CharacterRegion* charRegion, Config* config);
|
PlateCorners(cv::Mat inputImage, PlateLines* plateLines, PipelineData* pipelineData);
|
||||||
virtual ~PlateCorners();
|
virtual ~PlateCorners();
|
||||||
|
|
||||||
std::vector<cv::Point> findPlateCorners();
|
std::vector<cv::Point> findPlateCorners();
|
||||||
@@ -56,7 +56,7 @@ class PlateCorners
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Config* config;
|
PipelineData* pipelineData;
|
||||||
cv::Mat inputImage;
|
cv::Mat inputImage;
|
||||||
float charHeight;
|
float charHeight;
|
||||||
float charAngle;
|
float charAngle;
|
||||||
@@ -69,7 +69,6 @@ class PlateCorners
|
|||||||
LineSegment bestRight;
|
LineSegment bestRight;
|
||||||
|
|
||||||
PlateLines* plateLines;
|
PlateLines* plateLines;
|
||||||
CharacterRegion* charRegion;
|
|
||||||
|
|
||||||
void scoreHorizontals( int h1, int h2 );
|
void scoreHorizontals( int h1, int h2 );
|
||||||
void scoreVerticals( int v1, int v2 );
|
void scoreVerticals( int v1, int v2 );
|
||||||
|
@@ -25,10 +25,11 @@ using namespace std;
|
|||||||
const float MIN_CONFIDENCE = 0.3;
|
const float MIN_CONFIDENCE = 0.3;
|
||||||
|
|
||||||
|
|
||||||
PlateLines::PlateLines(Config* config)
|
PlateLines::PlateLines(PipelineData* pipelineData)
|
||||||
{
|
{
|
||||||
this->config = config;
|
this->pipelineData = pipelineData;
|
||||||
this->debug = config->debugPlateLines;
|
|
||||||
|
this->debug = pipelineData->config->debugPlateLines;
|
||||||
|
|
||||||
if (debug)
|
if (debug)
|
||||||
cout << "PlateLines constructor" << endl;
|
cout << "PlateLines constructor" << endl;
|
||||||
@@ -38,7 +39,7 @@ PlateLines::~PlateLines()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlateLines::processImage(Mat inputImage, CharacterRegion* charRegion, float sensitivity)
|
void PlateLines::processImage(Mat inputImage, float sensitivity)
|
||||||
{
|
{
|
||||||
if (this->debug)
|
if (this->debug)
|
||||||
cout << "PlateLines findLines" << endl;
|
cout << "PlateLines findLines" << endl;
|
||||||
@@ -59,7 +60,6 @@ void PlateLines::processImage(Mat inputImage, CharacterRegion* charRegion, float
|
|||||||
adaptiveBilateralFilter(inputImage, smoothed, Size(3,3), 45, 45);
|
adaptiveBilateralFilter(inputImage, smoothed, Size(3,3), 45, 45);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int morph_elem = 2;
|
int morph_elem = 2;
|
||||||
int morph_size = 2;
|
int morph_size = 2;
|
||||||
Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
|
Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
|
||||||
@@ -69,11 +69,18 @@ void PlateLines::processImage(Mat inputImage, CharacterRegion* charRegion, float
|
|||||||
Canny(smoothed, edges, 66, 133);
|
Canny(smoothed, edges, 66, 133);
|
||||||
|
|
||||||
// Create a mask that is dilated based on the detected characters
|
// Create a mask that is dilated based on the detected characters
|
||||||
vector<vector<Point> > polygons;
|
|
||||||
polygons.push_back(charRegion->getCharArea());
|
|
||||||
|
|
||||||
Mat mask = Mat::zeros(inputImage.size(), CV_8U);
|
Mat mask = Mat::zeros(inputImage.size(), CV_8U);
|
||||||
|
|
||||||
|
for (uint i = 0; i < pipelineData->textLines.size(); i++)
|
||||||
|
{
|
||||||
|
vector<vector<Point> > polygons;
|
||||||
|
polygons.push_back(pipelineData->textLines[i].textArea);
|
||||||
fillPoly(mask, polygons, Scalar(255,255,255));
|
fillPoly(mask, polygons, Scalar(255,255,255));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dilate(mask, mask, getStructuringElement( 1, Size( 1 + 1, 2*1+1 ), Point( 1, 1 ) ));
|
dilate(mask, mask, getStructuringElement( 1, Size( 1 + 1, 2*1+1 ), Point( 1, 1 ) ));
|
||||||
bitwise_not(mask, mask);
|
bitwise_not(mask, mask);
|
||||||
@@ -114,10 +121,10 @@ void PlateLines::processImage(Mat inputImage, CharacterRegion* charRegion, float
|
|||||||
images.push_back(debugImgVert);
|
images.push_back(debugImgVert);
|
||||||
|
|
||||||
Mat dashboard = drawImageDashboard(images, debugImgVert.type(), 1);
|
Mat dashboard = drawImageDashboard(images, debugImgVert.type(), 1);
|
||||||
displayImage(config, "Hough Lines", dashboard);
|
displayImage(pipelineData->config, "Hough Lines", dashboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->debugTiming)
|
if (pipelineData->config->debugTiming)
|
||||||
{
|
{
|
||||||
timespec endTime;
|
timespec endTime;
|
||||||
getTime(&endTime);
|
getTime(&endTime);
|
||||||
@@ -134,8 +141,8 @@ vector<PlateLine> PlateLines::getLines(Mat edges, float sensitivityMultiplier, b
|
|||||||
if (this->debug)
|
if (this->debug)
|
||||||
cout << "PlateLines::getLines" << endl;
|
cout << "PlateLines::getLines" << endl;
|
||||||
|
|
||||||
static int HORIZONTAL_SENSITIVITY = config->plateLinesSensitivityHorizontal;
|
static int HORIZONTAL_SENSITIVITY = pipelineData->config->plateLinesSensitivityHorizontal;
|
||||||
static int VERTICAL_SENSITIVITY = config->plateLinesSensitivityVertical;
|
static int VERTICAL_SENSITIVITY = pipelineData->config->plateLinesSensitivityVertical;
|
||||||
|
|
||||||
vector<Vec2f> allLines;
|
vector<Vec2f> allLines;
|
||||||
vector<PlateLine> filteredLines;
|
vector<PlateLine> filteredLines;
|
||||||
|
@@ -37,10 +37,10 @@ class PlateLines
|
|||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PlateLines(Config* config);
|
PlateLines(PipelineData* pipelineData);
|
||||||
virtual ~PlateLines();
|
virtual ~PlateLines();
|
||||||
|
|
||||||
void processImage(cv::Mat img, CharacterRegion* charRegion, float sensitivity=1.0);
|
void processImage(cv::Mat img, float sensitivity=1.0);
|
||||||
|
|
||||||
std::vector<PlateLine> horizontalLines;
|
std::vector<PlateLine> horizontalLines;
|
||||||
std::vector<PlateLine> verticalLines;
|
std::vector<PlateLine> verticalLines;
|
||||||
@@ -49,7 +49,7 @@ class PlateLines
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Config* config;
|
PipelineData* pipelineData;
|
||||||
bool debug;
|
bool debug;
|
||||||
|
|
||||||
cv::Mat customGrayscaleConversion(cv::Mat src);
|
cv::Mat customGrayscaleConversion(cv::Mat src);
|
||||||
|
@@ -49,6 +49,8 @@ void CharacterAnalysis::analyze()
|
|||||||
timespec startTime;
|
timespec startTime;
|
||||||
getTime(&startTime);
|
getTime(&startTime);
|
||||||
|
|
||||||
|
TextLine textLine;
|
||||||
|
|
||||||
for (uint i = 0; i < pipeline_data->thresholds.size(); i++)
|
for (uint i = 0; i < pipeline_data->thresholds.size(); i++)
|
||||||
{
|
{
|
||||||
TextContours tc(pipeline_data->thresholds[i]);
|
TextContours tc(pipeline_data->thresholds[i]);
|
||||||
@@ -148,28 +150,28 @@ void CharacterAnalysis::analyze()
|
|||||||
displayImage(config, "Matching Contours", img_contours);
|
displayImage(config, "Matching Contours", img_contours);
|
||||||
}
|
}
|
||||||
|
|
||||||
//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);
|
this->linePolygon = getBestVotedLines(pipeline_data->crop_gray, bestContours);
|
||||||
|
|
||||||
if (this->linePolygon.size() > 0)
|
if (this->linePolygon.size() > 0)
|
||||||
{
|
{
|
||||||
this->topLine = LineSegment(this->linePolygon[0].x, this->linePolygon[0].y, this->linePolygon[1].x, this->linePolygon[1].y);
|
textLine.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);
|
textLine.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, linePolygon);
|
filterBetweenLines(bestThreshold, bestContours, linePolygon);
|
||||||
|
|
||||||
this->charArea = getCharArea();
|
textLine.textArea = getCharArea(textLine.topLine, textLine.bottomLine);
|
||||||
|
|
||||||
if (this->charArea.size() > 0)
|
if (textLine.textArea.size() > 0)
|
||||||
{
|
{
|
||||||
this->charBoxTop = LineSegment(this->charArea[0].x, this->charArea[0].y, this->charArea[1].x, this->charArea[1].y);
|
textLine.charBoxTop = LineSegment(textLine.textArea[0].x, textLine.textArea[0].y, textLine.textArea[1].x, textLine.textArea[1].y);
|
||||||
this->charBoxBottom = LineSegment(this->charArea[3].x, this->charArea[3].y, this->charArea[2].x, this->charArea[2].y);
|
textLine.charBoxBottom = LineSegment(textLine.textArea[3].x, textLine.textArea[3].y, textLine.textArea[2].x, textLine.textArea[2].y);
|
||||||
this->charBoxLeft = LineSegment(this->charArea[3].x, this->charArea[3].y, this->charArea[0].x, this->charArea[0].y);
|
textLine.charBoxLeft = LineSegment(textLine.textArea[3].x, textLine.textArea[3].y, textLine.textArea[0].x, textLine.textArea[0].y);
|
||||||
this->charBoxRight = LineSegment(this->charArea[2].x, this->charArea[2].y, this->charArea[1].x, this->charArea[1].y);
|
textLine.charBoxRight = LineSegment(textLine.textArea[2].x, textLine.textArea[2].y, textLine.textArea[1].x, textLine.textArea[1].y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pipeline_data->textLines.push_back(textLine);
|
||||||
|
|
||||||
this->thresholdsInverted = isPlateInverted();
|
this->thresholdsInverted = isPlateInverted();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -733,7 +735,7 @@ bool CharacterAnalysis::verifySize(Mat r, float minHeightPx, float maxHeightPx)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<Point> CharacterAnalysis::getCharArea()
|
vector<Point> CharacterAnalysis::getCharArea(LineSegment topLine, LineSegment bottomLine)
|
||||||
{
|
{
|
||||||
const int MAX = 100000;
|
const int MAX = 100000;
|
||||||
const int MIN= -1;
|
const int MIN= -1;
|
||||||
|
@@ -43,15 +43,10 @@ class CharacterAnalysis
|
|||||||
//std::vector<bool> bestCharSegments;
|
//std::vector<bool> bestCharSegments;
|
||||||
//int bestCharSegmentsCount;
|
//int bestCharSegmentsCount;
|
||||||
|
|
||||||
LineSegment topLine;
|
|
||||||
LineSegment bottomLine;
|
|
||||||
std::vector<cv::Point> linePolygon;
|
|
||||||
std::vector<cv::Point> charArea;
|
|
||||||
|
|
||||||
LineSegment charBoxTop;
|
std::vector<cv::Point> linePolygon;
|
||||||
LineSegment charBoxBottom;
|
|
||||||
LineSegment charBoxLeft;
|
|
||||||
LineSegment charBoxRight;
|
|
||||||
|
|
||||||
bool thresholdsInverted;
|
bool thresholdsInverted;
|
||||||
bool isTwoLine;
|
bool isTwoLine;
|
||||||
@@ -79,7 +74,7 @@ class CharacterAnalysis
|
|||||||
void filterContourHoles(TextContours& textContours);
|
void filterContourHoles(TextContours& textContours);
|
||||||
void filterByOuterMask(TextContours& textContours);
|
void filterByOuterMask(TextContours& textContours);
|
||||||
|
|
||||||
std::vector<cv::Point> getCharArea();
|
std::vector<cv::Point> getCharArea(LineSegment topLine, LineSegment bottomLine);
|
||||||
std::vector<cv::Point> getBestVotedLines(cv::Mat img, TextContours textContours);
|
std::vector<cv::Point> getBestVotedLines(cv::Mat img, TextContours textContours);
|
||||||
void filterBetweenLines(cv::Mat img, TextContours& textContours, std::vector<cv::Point> outerPolygon );
|
void filterBetweenLines(cv::Mat img, TextContours& textContours, std::vector<cv::Point> outerPolygon );
|
||||||
|
|
||||||
|
@@ -1,9 +1,21 @@
|
|||||||
/*
|
/*
|
||||||
* File: textcontours.cpp
|
* Copyright (c) 2014 New Designs Unlimited, LLC
|
||||||
* Author: mhill
|
* Opensource Automated License Plate Recognition [http://www.openalpr.com]
|
||||||
*
|
*
|
||||||
* Created on October 9, 2014, 7:40 PM
|
* 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 "textcontours.h"
|
#include "textcontours.h"
|
||||||
|
|
||||||
|
@@ -1,9 +1,21 @@
|
|||||||
/*
|
/*
|
||||||
* File: textcontours.h
|
* Copyright (c) 2014 New Designs Unlimited, LLC
|
||||||
* Author: mhill
|
* Opensource Automated License Plate Recognition [http://www.openalpr.com]
|
||||||
*
|
*
|
||||||
* Created on October 9, 2014, 7:40 PM
|
* 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 TEXTCONTOURS_H
|
#ifndef TEXTCONTOURS_H
|
||||||
#define TEXTCONTOURS_H
|
#define TEXTCONTOURS_H
|
||||||
|
28
src/openalpr/textdetection/textline.cpp
Normal file
28
src/openalpr/textdetection/textline.cpp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* 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 "textline.h"
|
||||||
|
|
||||||
|
TextLine::TextLine() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TextLine::~TextLine() {
|
||||||
|
}
|
45
src/openalpr/textdetection/textline.h
Normal file
45
src/openalpr/textdetection/textline.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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_TEXTLINE_H
|
||||||
|
#define OPENALPR_TEXTLINE_H
|
||||||
|
|
||||||
|
#include "utility.h"
|
||||||
|
|
||||||
|
class TextLine {
|
||||||
|
public:
|
||||||
|
TextLine();
|
||||||
|
virtual ~TextLine();
|
||||||
|
|
||||||
|
std::vector<cv::Point> textArea;
|
||||||
|
LineSegment topLine;
|
||||||
|
LineSegment bottomLine;
|
||||||
|
|
||||||
|
LineSegment charBoxTop;
|
||||||
|
LineSegment charBoxBottom;
|
||||||
|
LineSegment charBoxLeft;
|
||||||
|
LineSegment charBoxRight;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* OPENALPR_TEXTLINE_H */
|
||||||
|
|
Reference in New Issue
Block a user