Cleanup & reindent .cpp files

This commit is contained in:
Philippe Vaucher
2014-03-19 11:26:31 +01:00
parent e85651ef84
commit 460205e943
26 changed files with 2349 additions and 2805 deletions

View File

@@ -17,37 +17,29 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <stdio.h>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include "tclap/CmdLine.h"
#include "support/filesystem.h"
#include "support/timing.h"
#include "alpr.h"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
const std::string MAIN_WINDOW_NAME = "ALPR main window";
const bool SAVE_LAST_VIDEO_STILL = false;
const std::string LAST_VIDEO_STILL_LOCATION = "/tmp/laststill.jpg";
#include "tclap/CmdLine.h"
#include "support/filesystem.h"
#include "support/timing.h"
#include "alpr.h"
/** Function Headers */
bool detectandshow(Alpr* alpr, cv::Mat frame, std::string region, bool writeJson);
bool measureProcessingTime = false;
const std::string MAIN_WINDOW_NAME = "ALPR main window";
const bool SAVE_LAST_VIDEO_STILL = false;
const std::string LAST_VIDEO_STILL_LOCATION = "/tmp/laststill.jpg";
/** Function Headers */
bool detectandshow(Alpr* alpr, cv::Mat frame, std::string region, bool writeJson);
bool measureProcessingTime = false;
int main( int argc, const char** argv )
{
int main( int argc, const char** argv )
{
std::string filename;
std::string runtimePath = "";
bool outputJson = false;
@@ -57,7 +49,8 @@
std::string country;
int topn;
try {
try
{
TCLAP::CmdLine cmd("OpenAlpr Command Line Utility", ' ', OPENALPR_VERSION);
@@ -93,7 +86,8 @@
topn = topNArg.getValue();
measureProcessingTime = clockSwitch.getValue();
} catch (TCLAP::ArgException &e) // catch any exceptions
}
catch (TCLAP::ArgException &e) // catch any exceptions
{
std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl;
return 1;
@@ -101,7 +95,6 @@
cv::Mat frame;
Alpr alpr(country, runtimePath);
alpr.setTopN(topn);
@@ -179,7 +172,6 @@
std::cerr << "Image file not found: " << filename << std::endl;
}
}
else if (DirectoryExists(filename.c_str()))
{
@@ -212,27 +204,20 @@
return 1;
}
return 0;
}
bool detectandshow( Alpr* alpr, cv::Mat frame, std::string region, bool writeJson)
{
}
bool detectandshow( Alpr* alpr, cv::Mat frame, std::string region, bool writeJson)
{
std::vector<uchar> buffer;
cv::imencode(".bmp", frame, buffer );
timespec startTime;
getTime(&startTime);
std::vector<AlprResult> results = alpr->recognize(buffer);
if (writeJson)
{
std::cout << alpr->toJson(results) << std::endl;
@@ -256,9 +241,8 @@
if (measureProcessingTime)
std::cout << "Total Time to process image: " << diffclock(startTime, endTime) << "ms." << std::endl;
if (results.size() > 0)
return true;
return false;
}
}

View File

@@ -17,12 +17,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <sys/stat.h>
#include <numeric> // std::accumulate
@@ -34,8 +34,8 @@
//#include "utility.h"
#include "support/filesystem.h"
using namespace std;
using namespace cv;
using namespace std;
using namespace cv;
// Given a directory full of lp images (named [statecode]#.png) crop out the alphanumeric characters.
// These will be used to train the OCR
@@ -50,7 +50,6 @@ int main( int argc, const char** argv )
string outDir;
Mat frame;
//Check if user specify image to process
if(argc == 5)
{
@@ -59,8 +58,9 @@ int main( int argc, const char** argv )
inDir = argv[3];
outDir = argv[4];
}else{
}
else
{
printf("Use:\n\t%s [country] [benchmark name] [img input dir] [results output dir]\n",argv[0]);
printf("\tex: %s us speed ./speed/usimages ./speed\n",argv[0]);
printf("\n");
@@ -68,7 +68,6 @@ int main( int argc, const char** argv )
return 0;
}
if (DirectoryExists(inDir.c_str()) == false)
{
printf("Input dir does not exist\n");
@@ -80,11 +79,9 @@ int main( int argc, const char** argv )
return 0;
}
vector<string> files = getFilesInDir(inDir.c_str());
sort( files.begin(), files.end(), stringCompare );
if (benchmarkName.compare("segocr") == 0)
{
Config* config = new Config(country);
@@ -92,7 +89,6 @@ int main( int argc, const char** argv )
OCR* ocr = new OCR(config);
for (int i = 0; i< files.size(); i++)
{
if (hasEnding(files[i], ".png"))
@@ -129,19 +125,15 @@ int main( int argc, const char** argv )
rotated.copyTo(frame);
}
CharacterSegmenter charSegmenter(frame, charRegion.thresholdsInverted(), config);
ocr->performOCR(charSegmenter.getThresholds(), charSegmenter.characters);
ocr->postProcessor->analyze(statecode, 25);
cout << files[i] << "," << statecode << "," << ocr->postProcessor->bestChars << endl;
imshow("Current LP", frame);
waitKey(5);
}
}
@@ -166,7 +158,6 @@ int main( int argc, const char** argv )
imshow("Current LP", frame);
waitKey(5);
}
}
@@ -197,7 +188,6 @@ int main( int argc, const char** argv )
vector<double> ocrTimes;
vector<double> postProcessTimes;
for (int i = 0; i< files.size(); i++)
{
if (hasEnding(files[i], ".png"))
@@ -207,7 +197,6 @@ int main( int argc, const char** argv )
string fullpath = inDir + "/" + files[i];
frame = imread( fullpath.c_str() );
getTime(&startTime);
alpr.recognize(frame);
getTime(&endTime);
@@ -244,7 +233,6 @@ int main( int argc, const char** argv )
{
lpAnalysisPositiveTimes.push_back(analysisTime);
getTime(&startTime);
ocr.performOCR(lp.charSegmenter->getThresholds(), lp.charSegmenter->characters);
getTime(&endTime);
@@ -273,7 +261,6 @@ int main( int argc, const char** argv )
cout << endl << "---------------------" << endl;
cout << "End to End Time Statistics:" << endl;
outputStats(endToEndTimes);
cout << endl;
@@ -333,7 +320,6 @@ int main( int argc, const char** argv )
imshow("Current LP", frame);
waitKey(5);
}
}

View File

@@ -31,8 +31,8 @@
#include "support/filesystem.h"
#include "ocr.h"
using namespace std;
using namespace cv;
using namespace std;
using namespace cv;
// Given a directory full of lp images (named [statecode]#.png) crop out the alphanumeric characters.
// These will be used to train the OCR
@@ -61,7 +61,6 @@ const int DASHBOARD_COLUMNS = 3;
#endif
void showDashboard(vector<Mat> images, vector<bool> selectedImages, int selectedIndex);
vector<char> showCharSelection(Mat image, vector<Rect> charRegions, string state);
@@ -72,21 +71,20 @@ int main( int argc, const char** argv )
string outDir;
Mat frame;
//Check if user specify image to process
if(argc == 3)
{
inDir = argv[1];
outDir = argv[2];
}else{
}
else
{
printf("Use:\n\t%s indirectory outdirectory\n",argv[0]);
printf("Ex: \n\t%s ./pics/ ./out \n",argv[0]);
return 0;
}
if (DirectoryExists(outDir.c_str()) == false)
{
printf("Output dir does not exist\n");
@@ -125,7 +123,6 @@ int main( int argc, const char** argv )
imshow ("Original", frame);
char statecode[3];
statecode[0] = files[i][0];
statecode[1] = files[i][1];
@@ -155,7 +152,6 @@ int main( int argc, const char** argv )
ocr.postProcessor->analyze(statecodestr, 25);
cout << "OCR results: " << ocr.postProcessor->bestChars << endl;
vector<bool> selectedBoxes(charSegmenter.getThresholds().size());
for (int z = 0; z < charSegmenter.getThresholds().size(); z++)
selectedBoxes[z] = false;
@@ -169,8 +165,6 @@ int main( int argc, const char** argv )
showDashboard(charSegmenter.getThresholds(), selectedBoxes, 0);
char waitkey = (char) waitKey(50);
while (waitkey != 'n' && waitkey != 'p') // Next image
@@ -267,7 +261,6 @@ int main( int argc, const char** argv )
if (i < -1)
i = -1;
}
}
@@ -275,7 +268,6 @@ int main( int argc, const char** argv )
}
void showDashboard(vector<Mat> images, vector<bool> selectedImages, int selectedIndex)
{
vector<Mat> vecCopy;
@@ -315,7 +307,6 @@ vector<char> showCharSelection(Mat image, vector<Rect> charRegions, string state
for (int i = 0; i < charRegions.size(); i++)
humanInputs[i] = (char) SPACE_KEY;
char waitkey = (char) waitKey(50);
while (waitkey != ENTER_KEY && waitkey != ESCAPE_KEY)
{
@@ -365,6 +356,5 @@ vector<char> showCharSelection(Mat image, vector<Rect> charRegions, string state
destroyWindow("Character selector");
return humanInputs;
}

View File

@@ -17,19 +17,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <iostream>
#include <stdio.h>
#include <fstream>
#include <sys/stat.h>
#include "support/filesystem.h"
using namespace std;
using namespace cv;
using namespace std;
using namespace cv;
// Takes a directory full of single char images, and plops them on a big tif files
// Also creates a box file so Tesseract can recognize it
@@ -38,18 +36,18 @@ int main( int argc, const char** argv )
string inDir;
//Check if user specify image to process
if(argc == 2)
{
inDir = argv[1];
}else{
}
else
{
printf("Use:\n\t%s input dir \n",argv[0]);
return 0;
}
if (DirectoryExists(inDir.c_str()) == false)
{
printf("Output dir does not exist\n");
@@ -59,7 +57,6 @@ int main( int argc, const char** argv )
cout << "Usage: " << endl;
cout << "\tinputdir -- input dir for benchmark data" << endl;
if (DirectoryExists(inDir.c_str()))
{
const int X_OFFSET = 10;
@@ -78,7 +75,6 @@ int main( int argc, const char** argv )
sort( files.begin(), files.end(), stringCompare );
int tiles_per_row = ((float) (HORIZONTAL_RESOLUTION - (PAGE_MARGIN_X * 2))) / ((float) TILE_WIDTH);
int lines = files.size() / (tiles_per_row);
int vertical_resolution = (lines * TILE_HEIGHT) + (PAGE_MARGIN_Y * 2) ;
@@ -130,7 +126,6 @@ int main( int argc, const char** argv )
Rect cropRect(0, tallestRect.y - Y_OFFSET, tallestRect.width, tallestRect.height);
//cout << "Cropped: " << cropRect.x << ":" << cropRect.y << " -- " << cropRect.width << ":" << cropRect.height << endl;
Mat cropped(characterImg, cropRect);
cvtColor(cropped, cropped, CV_BGR2GRAY);
@@ -141,7 +136,6 @@ int main( int argc, const char** argv )
cropped.copyTo(bigTif(destinationRect));
int x1= destinationRect.x - 2;
int y1 = (vertical_resolution - destinationRect.y - destinationRect.height) - 2;
int x2 = (destinationRect.x + destinationRect.width) + 2;
@@ -156,12 +150,10 @@ int main( int argc, const char** argv )
waitKey(2);
}
}
imwrite("combined.tif", bigTif);
ofstream boxFile("combined.box", std::ios::out);
boxFile << boxFileOut.str();

View File

@@ -17,11 +17,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <iostream>
#include <stdio.h>
#include <sys/stat.h>
#include "regiondetector.h"
@@ -30,13 +30,13 @@
#include "utility.h"
#include "support/filesystem.h"
using namespace std;
using namespace cv;
using namespace std;
using namespace cv;
// Given a directory full of pre-cropped images, identify the state that each image belongs to.
// This is used to sort our own positive image database as a first step before grabbing characters to use to train the OCR.
bool detectPlate( StateIdentifier* identifier, Mat frame);
bool detectPlate( StateIdentifier* identifier, Mat frame);
int main( int argc, const char** argv )
{
@@ -45,7 +45,6 @@ int main( int argc, const char** argv )
string outDir;
Mat frame;
//Check if user specify image to process
if(argc == 3 )
{
@@ -53,8 +52,9 @@ int main( int argc, const char** argv )
outDir = argv[2];
outDir = outDir + "/";
}else{
}
else
{
printf("Use:\n\t%s directory \n",argv[0]);
printf("Ex: \n\t%s ./pics/ \n",argv[0]);
return 0;
@@ -99,7 +99,6 @@ int main( int argc, const char** argv )
ostringstream convert; // stream used for the conversion
convert << i; // insert the textual representation of 'Number' in the characters in the stream
string copyCommand = "cp \"" + fullpath + "\" " + outDir + code + convert.str() + ".png";
system( copyCommand.c_str() );
waitKey(50);
@@ -114,4 +113,4 @@ int main( int argc, const char** argv )
}
bool detectPlate( StateIdentifier* identifier, Mat frame);
bool detectPlate( StateIdentifier* identifier, Mat frame);

View File

@@ -20,9 +20,6 @@
#include "alpr.h"
#include "alpr_impl.h"
// ALPR code
Alpr::Alpr(const std::string country, const std::string runtimeDir)
@@ -40,7 +37,6 @@ std::vector<AlprResult> Alpr::recognize(std::string filepath)
}
std::vector<AlprResult> Alpr::recognize(std::vector<unsigned char> imageBuffer)
{
// Not sure if this actually works
@@ -49,13 +45,11 @@ std::vector<AlprResult> Alpr::recognize(std::vector<unsigned char> imageBuffer)
return impl->recognize(img);
}
string Alpr::toJson(const vector< AlprResult > results)
{
return impl->toJson(results);
}
void Alpr::setDetectRegion(bool detectRegion)
{
impl->setDetectRegion(detectRegion);
@@ -74,11 +68,8 @@ bool Alpr::isLoaded()
return true;
}
// Results code
AlprResult::AlprResult()
{

View File

@@ -19,7 +19,6 @@
#include "alpr_impl.h"
AlprImpl::AlprImpl(const std::string country, const std::string runtimeDir)
{
config = new Config(country, runtimeDir);
@@ -68,7 +67,6 @@ AlprImpl::~AlprImpl()
delete ocr;
}
std::vector<AlprResult> AlprImpl::recognize(cv::Mat img)
{
timespec startTime;
@@ -76,10 +74,8 @@ std::vector<AlprResult> AlprImpl::recognize(cv::Mat img)
vector<AlprResult> response;
vector<Rect> plateRegions = plateDetector->detect(img);
// Recognize.
for (int i = 0; i < plateRegions.size(); i++)
@@ -91,7 +87,6 @@ std::vector<AlprResult> AlprImpl::recognize(cv::Mat img)
lp.recognize();
if (lp.confidence > 10)
{
AlprResult plateResult;
@@ -114,7 +109,6 @@ std::vector<AlprResult> AlprImpl::recognize(cv::Mat img)
}
}
ocr->performOCR(lp.charSegmenter->getThresholds(), lp.charSegmenter->characters);
ocr->postProcessor->analyze(plateResult.region, topN);
@@ -162,7 +156,6 @@ std::vector<AlprResult> AlprImpl::recognize(cv::Mat img)
line(img, lp.plateCorners[z], lp.plateCorners[(z + 1) % 4], Scalar(255,0,255), 2);
}
}
else
{
@@ -210,8 +203,6 @@ string AlprImpl::toJson(const vector< AlprResult > results)
return response;
}
cJSON* AlprImpl::createJsonObj(const AlprResult* result)
{
cJSON *root, *coords, *candidates;
@@ -228,7 +219,7 @@ cJSON* AlprImpl::createJsonObj(const AlprResult* result)
cJSON_AddNumberToObject(root,"processing_time_ms", result->processing_time_ms);
cJSON_AddItemToObject(root, "coordinates", coords=cJSON_CreateArray());
for (int i=0;i<4;i++)
for (int i=0; i<4; i++)
{
cJSON *coords_object;
coords_object = cJSON_CreateObject();
@@ -238,7 +229,6 @@ cJSON* AlprImpl::createJsonObj(const AlprResult* result)
cJSON_AddItemToArray(coords, coords_object);
}
cJSON_AddItemToObject(root, "candidates", candidates=cJSON_CreateArray());
for (int i = 0; i < result->topNPlates.size(); i++)
{
@@ -254,7 +244,6 @@ cJSON* AlprImpl::createJsonObj(const AlprResult* result)
return root;
}
void AlprImpl::setDetectRegion(bool detectRegion)
{
this->detectRegion = detectRegion;

View File

@@ -33,14 +33,13 @@
#include "binarize_wolf.h"
// *************************************************************
// glide a window across the image and
// create two maps: mean and standard deviation.
// *************************************************************
float calcLocalStats (Mat &im, Mat &map_m, Mat &map_s, int winx, int winy) {
float calcLocalStats (Mat &im, Mat &map_m, Mat &map_s, int winx, int winy)
{
float m,s,max_s;
long sum, sum_sq;
@@ -52,14 +51,14 @@ float calcLocalStats (Mat &im, Mat &map_m, Mat &map_s, int winx, int winy) {
int y_firstth= wyh;
float winarea = winx*winy;
max_s = 0;
for (int j = y_firstth ; j<=y_lastth; j++)
{
// Calculate the initial window at the beginning of the line
sum = sum_sq = 0;
for (int wy=0 ; wy<winy; wy++)
for (int wx=0 ; wx<winx; wx++) {
for (int wx=0 ; wx<winx; wx++)
{
foo = im.uget(wx,j-wyh+wy);
sum += foo;
sum_sq += foo*foo;
@@ -72,10 +71,12 @@ float calcLocalStats (Mat &im, Mat &map_m, Mat &map_s, int winx, int winy) {
map_s.fset(x_firstth, j, s);
// Shift the window, add and remove new/old values to the histogram
for (int i=1 ; i <= im.cols-winx; i++) {
for (int i=1 ; i <= im.cols-winx; i++)
{
// Remove the left old column and add the right new column
for (int wy=0; wy<winy; ++wy) {
for (int wy=0; wy<winy; ++wy)
{
foo = im.uget(i-1,j-wyh+wy);
sum -= foo;
sum_sq -= foo*foo;
@@ -95,14 +96,13 @@ float calcLocalStats (Mat &im, Mat &map_m, Mat &map_s, int winx, int winy) {
return max_s;
}
/**********************************************************
* The binarization routine
**********************************************************/
void NiblackSauvolaWolfJolion (Mat im, Mat output, NiblackVersion version,
int winx, int winy, float k) {
int winx, int winy, float k)
{
float dR = BINARIZEWOLF_DEFAULTDR;
@@ -129,16 +129,19 @@ void NiblackSauvolaWolfJolion (Mat im, Mat output, NiblackVersion version,
// Create the threshold surface, including border processing
// ----------------------------------------------------
for (int j = y_firstth ; j<=y_lastth; j++) {
for (int j = y_firstth ; j<=y_lastth; j++)
{
// NORMAL, NON-BORDER AREA IN THE MIDDLE OF THE WINDOW:
for (int i=0 ; i <= im.cols-winx; i++) {
for (int i=0 ; i <= im.cols-winx; i++)
{
m = map_m.fget(i+wxh, j);
s = map_s.fget(i+wxh, j);
// Calculate the threshold
switch (version) {
switch (version)
{
case NIBLACK:
th = m + k*s;
@@ -159,7 +162,8 @@ void NiblackSauvolaWolfJolion (Mat im, Mat output, NiblackVersion version,
thsurf.fset(i+wxh,j,th);
if (i==0) {
if (i==0)
{
// LEFT BORDER
for (int i=0; i<=x_firstth; ++i)
thsurf.fset(i,j,th);
@@ -205,8 +209,6 @@ void NiblackSauvolaWolfJolion (Mat im, Mat output, NiblackVersion version,
thsurf.fset(i,u,th);
}
for (int y=0; y<im.rows; ++y)
for (int x=0; x<im.cols; ++x)
{

View File

@@ -29,7 +29,6 @@ CharacterAnalysis::CharacterAnalysis(Mat img, Config* config)
if (this->config->debugCharAnalysis)
cout << "Starting CharacterAnalysis identification" << endl;
if (img.type() != CV_8U)
cvtColor( img, this->img_gray, CV_BGR2GRAY );
else
@@ -49,15 +48,11 @@ CharacterAnalysis::~CharacterAnalysis()
thresholds.clear();
}
void CharacterAnalysis::analyze()
{
thresholds = produceThresholds(img_gray, config);
/*
// Morph Close the gray image to make it easier to detect blobs
int morph_elem = 1;
@@ -73,11 +68,9 @@ void CharacterAnalysis::analyze()
}
*/
timespec startTime;
getTime(&startTime);
for (int i = 0; i < thresholds.size(); i++)
{
vector<vector<Point> > contours;
@@ -95,9 +88,6 @@ void CharacterAnalysis::analyze()
allHierarchy.push_back(hierarchy);
}
if (config->debugTiming)
{
timespec endTime;
@@ -106,7 +96,6 @@ void CharacterAnalysis::analyze()
}
//Mat img_equalized = equalizeBrightness(img_gray);
getTime(&startTime);
for (int i = 0; i < thresholds.size(); i++)
@@ -125,8 +114,6 @@ void CharacterAnalysis::analyze()
cout << " -- Character Analysis Filter Time: " << diffclock(startTime, endTime) << "ms." << endl;
}
this->plateMask = findOuterBoxMask();
if (hasPlateMask)
@@ -138,7 +125,6 @@ void CharacterAnalysis::analyze()
}
}
int bestFitScore = -1;
int bestFitIndex = -1;
for (int i = 0; i < thresholds.size(); i++)
@@ -149,7 +135,6 @@ void CharacterAnalysis::analyze()
int segmentCount = getGoodIndicesCount(charSegments[i]);
if (segmentCount > bestFitScore)
{
bestFitScore = segmentCount;
@@ -165,11 +150,9 @@ void CharacterAnalysis::analyze()
if (this->config->debugCharAnalysis)
cout << "Best fit score: " << bestFitScore << " Index: " << bestFitIndex << endl;
if (bestFitScore <= 1)
return;
//getColorMask(img, allContours, allHierarchy, charSegments);
if (this->config->debugCharAnalysis)
@@ -196,15 +179,11 @@ void CharacterAnalysis::analyze()
cv::Scalar(0,255,0), // in green
1); // with a thickness of 1
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(img_gray, bestContours, bestCharSegments);
if (this->linePolygon.size() > 0)
@@ -223,14 +202,11 @@ void CharacterAnalysis::analyze()
this->charBoxLeft = LineSegment(this->charArea[3].x, this->charArea[3].y, this->charArea[0].x, this->charArea[0].y);
this->charBoxRight = LineSegment(this->charArea[2].x, this->charArea[2].y, this->charArea[1].x, this->charArea[1].y);
}
}
this->thresholdsInverted = isPlateInverted();
}
int CharacterAnalysis::getGoodIndicesCount(vector<bool> goodIndices)
@@ -245,8 +221,6 @@ int CharacterAnalysis::getGoodIndicesCount(vector<bool> goodIndices)
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.
@@ -256,7 +230,6 @@ Mat CharacterAnalysis::findOuterBoxMask()
int bestCharCount = 0;
double lowestArea = 99999999999999;
if (this->config->debugCharAnalysis)
cout << "CharacterAnalysis::findOuterBoxMask" << endl;
@@ -297,15 +270,11 @@ Mat CharacterAnalysis::findOuterBoxMask()
}
}
}
if (this->config->debugCharAnalysis)
cout << "Winning image index is: " << winningIndex << endl;
if (winningIndex != -1 && bestCharCount >= 3)
{
int longestChildIndex = -1;
@@ -327,10 +296,6 @@ Mat CharacterAnalysis::findOuterBoxMask()
}
}
Mat mask = Mat::zeros(thresholds[winningIndex].size(), CV_8U);
// get rid of the outline by drawing a 1 pixel width black line
@@ -343,7 +308,6 @@ Mat CharacterAnalysis::findOuterBoxMask()
0
);
// Morph Open the mask to get rid of any little connectors to non-plate portions
int morph_elem = 2;
int morph_size = 3;
@@ -356,7 +320,6 @@ Mat CharacterAnalysis::findOuterBoxMask()
//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.
@@ -394,8 +357,6 @@ Mat CharacterAnalysis::findOuterBoxMask()
0
);
}
if (this->config->debugCharAnalysis)
@@ -422,11 +383,8 @@ Mat CharacterAnalysis::findOuterBoxMask()
bitwise_not(fullMask, fullMask);
return fullMask;
}
Mat CharacterAnalysis::getCharacterMask()
{
@@ -437,7 +395,6 @@ Mat CharacterAnalysis::getCharacterMask()
if (bestCharSegments[i] == false)
continue;
drawContours(charMask, bestContours,
i, // draw this contour
cv::Scalar(255,255,255), // in
@@ -458,12 +415,9 @@ Mat CharacterAnalysis::getCharacterMask()
);
}
return charMask;
}
// 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)
{
@@ -481,7 +435,6 @@ vector<Point> CharacterAnalysis::getBestVotedLines(Mat img, vector<vector<Point>
charRegions.push_back(boundingRect(contours[i]));
}
// Find the best fit line segment that is parallel with the most char segments
if (charRegions.size() <= 1)
{
@@ -499,7 +452,6 @@ vector<Point> CharacterAnalysis::getBestVotedLines(Mat img, vector<vector<Point>
//Mat tempImg;
//result.copyTo(tempImg);
Rect* leftRect;
Rect* rightRect;
if (charRegions[i].x < charRegions[k].x)
@@ -534,7 +486,6 @@ vector<Point> CharacterAnalysis::getBestVotedLines(Mat img, vector<vector<Point>
//cv::line(tempImg, Point(x1, y1), Point(x2, y2), Scalar(0, 0, 255));
topLines.push_back(LineSegment(x1, y1, x2, y2));
if (leftRect->y > rightRect->y) // Rising line, use the bottom right corner of the rect
{
x1 = leftRect->x + leftRect->width;
@@ -565,7 +516,6 @@ vector<Point> CharacterAnalysis::getBestVotedLines(Mat img, vector<vector<Point>
float SCORING_MIN_THRESHOLD = 0.97;
float SCORING_MAX_THRESHOLD = 1.03;
int curScore = 0;
for (int charidx = 0; charidx < charRegions.size(); charidx++)
{
@@ -588,7 +538,6 @@ vector<Point> CharacterAnalysis::getBestVotedLines(Mat img, vector<vector<Point>
}
// Tie goes to the one with longer line segments
if ((curScore > bestScore) ||
(curScore == bestScore && topLines[i].length > bestScoreDistance))
@@ -600,7 +549,6 @@ vector<Point> CharacterAnalysis::getBestVotedLines(Mat img, vector<vector<Point>
}
}
if (this->config->debugCharAnalysis)
{
cout << "The winning score is: " << bestScore << endl;
@@ -621,21 +569,16 @@ vector<Point> CharacterAnalysis::getBestVotedLines(Mat img, vector<vector<Point>
Point bottomRight = Point(img.cols, bottomLines[bestScoreIndex].getPointAt(img.cols));
Point bottomLeft = Point(0, bottomLines[bestScoreIndex].getPointAt(0));
bestStripe.push_back(topLeft);
bestStripe.push_back(topRight);
bestStripe.push_back(bottomRight);
bestStripe.push_back(bottomLeft);
}
return bestStripe;
}
vector<bool> CharacterAnalysis::filter(Mat img, vector<vector<Point> > contours, vector<Vec4i> hierarchy)
{
static int STARTING_MIN_HEIGHT = round (((float) img.rows) * config->charAnalysisMinPercent);
@@ -643,7 +586,6 @@ vector<bool> CharacterAnalysis::filter(Mat img, vector<vector<Point> > contours,
static int HEIGHT_STEP = round (((float) img.rows) * config->charAnalysisHeightStepSize);
static int NUM_STEPS = config->charAnalysisNumSteps;
vector<bool> charSegments;
int bestFitScore = -1;
for (int i = 0; i < NUM_STEPS; i++)
@@ -655,7 +597,6 @@ vector<bool> CharacterAnalysis::filter(Mat img, vector<vector<Point> > contours,
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...
continue;
@@ -668,7 +609,6 @@ vector<bool> CharacterAnalysis::filter(Mat img, vector<vector<Point> > contours,
vector<Point> lines = getBestVotedLines(img, contours, goodIndices);
goodIndices = this->filterBetweenLines(img, contours, hierarchy, lines, goodIndices);
int segmentCount = getGoodIndicesCount(goodIndices);
if (segmentCount > bestFitScore)
@@ -678,11 +618,9 @@ vector<bool> CharacterAnalysis::filter(Mat img, vector<vector<Point> > contours,
}
}
return charSegments;
}
// 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)
{
@@ -690,7 +628,6 @@ vector<bool> CharacterAnalysis::filterByBoxSize(vector< vector< Point> > contour
float idealAspect=config->charWidthMM / config->charHeightMM;
float aspecttolerance=0.25;
vector<bool> includedIndices(contours.size());
for (int j = 0; j < contours.size(); j++)
includedIndices.push_back(false);
@@ -706,7 +643,8 @@ vector<bool> CharacterAnalysis::filterByBoxSize(vector< vector< Point> > contour
float minWidth = mr.height * 0.2;
//Crop image
//Mat auxRoi(img, mr);
if(mr.height >= minHeightPx && mr.height <= maxHeightPx && mr.width > minWidth){
if(mr.height >= minHeightPx && mr.height <= maxHeightPx && mr.width > minWidth)
{
float charAspect= (float)mr.width/(float)mr.height;
@@ -719,8 +657,6 @@ vector<bool> CharacterAnalysis::filterByBoxSize(vector< vector< Point> > contour
}
vector< bool > CharacterAnalysis::filterContourHoles(vector< vector< Point > > contours, vector< Vec4i > hierarchy, vector< bool > goodIndices)
{
@@ -752,7 +688,6 @@ vector< bool > CharacterAnalysis::filterContourHoles(vector< vector< Point > > c
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)
@@ -826,7 +761,6 @@ vector<bool> CharacterAnalysis::filterByParentContour( vector< vector< Point> >
return includedIndices;
}
vector<bool> CharacterAnalysis::filterBetweenLines(Mat img, vector<vector<Point> > contours, vector<Vec4i> hierarchy, vector<Point> outerPolygon, vector<bool> goodIndices)
{
static float MIN_AREA_PERCENT_WITHIN_LINES = 0.88;
@@ -835,7 +769,6 @@ vector<bool> CharacterAnalysis::filterBetweenLines(Mat img, vector<vector<Point>
for (int j = 0; j < contours.size(); j++)
includedIndices[j] = false;
if (outerPolygon.size() == 0)
return includedIndices;
@@ -855,7 +788,6 @@ vector<bool> CharacterAnalysis::filterBetweenLines(Mat img, vector<vector<Point>
Mat innerArea = Mat::zeros(img.size(), CV_8U);
fillConvexPoly(outerMask, outerPolygon.data(), outerPolygon.size(), Scalar(255,255,255));
for (int i = 0; i < contours.size(); i++)
{
if (goodIndices[i] == false)
@@ -871,10 +803,8 @@ vector<bool> CharacterAnalysis::filterBetweenLines(Mat img, vector<vector<Point>
0
);
bitwise_and(innerArea, outerMask, innerArea);
vector<vector<Point> > tempContours;
findContours(innerArea, tempContours,
CV_RETR_EXTERNAL, // retrieve the external contours
@@ -889,7 +819,6 @@ vector<bool> CharacterAnalysis::filterBetweenLines(Mat img, vector<vector<Point>
}
if (areaBetweenLines / totalArea >= MIN_AREA_PERCENT_WITHIN_LINES)
{
includedIndices[i] = true;
@@ -909,7 +838,6 @@ std::vector< bool > CharacterAnalysis::filterByOuterMask(vector< vector< Point >
if (hasPlateMask == false)
return goodIndices;
vector<bool> passingIndices;
for (int i = 0; i < goodIndices.size(); i++)
passingIndices.push_back(false);
@@ -952,8 +880,6 @@ std::vector< bool > CharacterAnalysis::filterByOuterMask(vector< vector< Point >
return passingIndices;
}
bool CharacterAnalysis::isPlateInverted()
{
@@ -964,14 +890,12 @@ bool CharacterAnalysis::isPlateInverted()
if (this->config->debugCharAnalysis)
cout << "CharacterAnalysis, plate inverted: MEAN: " << meanVal << " : " << bestThreshold.type() << endl;
if (meanVal[0] < 100) // Half would be 122.5. Give it a little extra oomf before saying it needs inversion. Most states aren't inverted.
return true;
return false;
}
bool CharacterAnalysis::verifySize(Mat r, float minHeightPx, float maxHeightPx)
{
//Char sizes 45x90
@@ -999,7 +923,6 @@ bool CharacterAnalysis::verifySize(Mat r, float minHeightPx, float maxHeightPx)
}
vector<Point> CharacterAnalysis::getCharArea()
{
const int MAX = 100000;

View File

@@ -28,19 +28,15 @@ CharacterRegion::CharacterRegion(Mat img, Config* config)
this->confidence = 0;
if (this->debug)
cout << "Starting CharacterRegion identification" << endl;
timespec startTime;
getTime(&startTime);
charAnalysis = new CharacterAnalysis(img, config);
charAnalysis->analyze();
if (this->debug)
{
vector<Mat> tempDash;
@@ -53,7 +49,6 @@ CharacterRegion::CharacterRegion(Mat img, Config* config)
tempDash.push_back(tmp);
}
Mat bestVal(charAnalysis->bestThreshold.size(), charAnalysis->bestThreshold.type());
charAnalysis->bestThreshold.copyTo(bestVal);
cvtColor(bestVal, bestVal, CV_GRAY2BGR);
@@ -69,8 +64,6 @@ CharacterRegion::CharacterRegion(Mat img, Config* config)
displayImage(config, "Character Region Step 1 Thresholds", drawImageDashboard(tempDash, bestVal.type(), 3));
}
if (this->debug)
{
/*
@@ -95,15 +88,12 @@ CharacterRegion::CharacterRegion(Mat img, Config* config)
cv::Scalar(0,255,0), // in green
1); // with a thickness of 1
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));
if (charAnalysis->linePolygon.size() > 0)
{
@@ -120,7 +110,6 @@ CharacterRegion::CharacterRegion(Mat img, Config* config)
else if (absangle > 1)
confidenceDrainers += (10 - absangle) * 5;
if (confidenceDrainers >= 100)
this->confidence=1;
else
@@ -128,8 +117,6 @@ CharacterRegion::CharacterRegion(Mat img, Config* config)
}
if (config->debugTiming)
{
timespec endTime;
@@ -144,8 +131,6 @@ CharacterRegion::~CharacterRegion()
delete(charAnalysis);
}
Mat CharacterRegion::getPlateMask()
{
return charAnalysis->plateMask;

View File

@@ -34,7 +34,6 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
timespec startTime;
getTime(&startTime);
Mat img_gray(img.size(), CV_8U);
cvtColor( img, img_gray, CV_BGR2GRAY );
@@ -43,22 +42,14 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
if (invertedColors)
bitwise_not(img_gray, img_gray);
charAnalysis = new CharacterAnalysis(img_gray, config);
charAnalysis->analyze();
if (this->config->debugCharSegmenter)
{
displayImage(config, "CharacterSegmenter Thresholds", drawImageDashboard(charAnalysis->thresholds, CV_8U, 3));
}
if (this->config->debugCharSegmenter)
{
@@ -93,8 +84,6 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
imgDbgGeneral.push_back(bordered);
}
if (charAnalysis->linePolygon.size() > 0)
{
this->top = LineSegment(charAnalysis->linePolygon[0].x, charAnalysis->linePolygon[0].y, charAnalysis->linePolygon[1].x, charAnalysis->linePolygon[1].y);
@@ -108,7 +97,6 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
if (charAnalysis->bestCharSegments[i] == false)
continue;
Rect mr = boundingRect(charAnalysis->bestContours[i]);
charWidths.push_back(mr.width);
@@ -122,26 +110,21 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
// Do the histogram analysis to figure out char regions
timespec startTime;
getTime(&startTime);
vector<Mat> allHistograms;
vector<Rect> allBoxes;
for (int i = 0; i < charAnalysis->allContours.size(); i++)
{
Mat histogramMask = Mat::zeros(charAnalysis->thresholds[i].size(), CV_8U);
fillConvexPoly(histogramMask, charAnalysis->linePolygon.data(), charAnalysis->linePolygon.size(), Scalar(255,255,255));
VerticalHistogram vertHistogram(charAnalysis->thresholds[i], histogramMask);
if (this->config->debugCharSegmenter)
{
Mat histoCopy(vertHistogram.histoImg.size(), vertHistogram.histoImg.type());
@@ -154,7 +137,6 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
float score = 0;
vector<Rect> charBoxes = getHistogramBoxes(vertHistogram, avgCharWidth, avgCharHeight, &score);
if (this->config->debugCharSegmenter)
{
for (int cboxIdx = 0; cboxIdx < charBoxes.size(); cboxIdx++)
@@ -171,7 +153,6 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
//drawAndWait(&histogramMask);
}
float medianCharWidth = avgCharWidth;
vector<int> widthValues;
// Compute largest char width
@@ -182,7 +163,6 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
medianCharWidth = median(widthValues.data(), widthValues.size());
if (config->debugTiming)
{
timespec endTime;
@@ -193,7 +173,6 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
//ColorFilter colorFilter(img, charAnalysis->getCharacterMask());
vector<Rect> candidateBoxes = getBestCharBoxes(charAnalysis->thresholds[0], allBoxes, medianCharWidth);
if (this->config->debugCharSegmenter)
{
// Setup the dashboard images to show the cleaning filters
@@ -211,7 +190,6 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
}
}
getTime(&startTime);
filterEdgeBoxes(charAnalysis->thresholds, candidateBoxes, medianCharWidth, avgCharHeight);
@@ -238,7 +216,6 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
if (this->config->debugCharSegmenter)
{
Mat imgDash = drawImageDashboard(charAnalysis->thresholds, CV_8U, 3);
displayImage(config, "Segmentation after cleaning", imgDash);
@@ -250,11 +227,6 @@ CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* con
}
}
if (config->debugTiming)
{
timespec endTime;
@@ -268,11 +240,6 @@ CharacterSegmenter::~CharacterSegmenter()
delete charAnalysis;
}
// Given a histogram and the horizontal line boundaries, respond with an array of boxes where the characters are
// Scores the histogram quality as well based on num chars, char volume, and even separation
vector<Rect> CharacterSegmenter::getHistogramBoxes(VerticalHistogram histogram, float avgCharWidth, float avgCharHeight, float* score)
@@ -328,15 +295,12 @@ vector<Rect> CharacterSegmenter::getHistogramBoxes(VerticalHistogram histogram,
}
return charBoxes;
}
vector<Rect> CharacterSegmenter::getBestCharBoxes(Mat img, vector<Rect> charBoxes, float avgCharWidth)
{
float MAX_SEGMENT_WIDTH = avgCharWidth * 1.55;
// This histogram is based on how many char boxes (from ALL of the many thresholded images) are covering each column
@@ -344,7 +308,6 @@ vector<Rect> CharacterSegmenter::getBestCharBoxes(Mat img, vector<Rect> charBoxe
Mat histoImg = Mat::zeros(Size(img.cols, img.rows), CV_8U);
int columnCount;
for (int col = 0; col < img.cols; col++)
@@ -370,7 +333,6 @@ vector<Rect> CharacterSegmenter::getBestCharBoxes(Mat img, vector<Rect> charBoxe
float bestRowScore = 0;
vector<Rect> bestBoxes;
for (int row = 0; row < histoImg.rows; row++)
{
vector<Rect> validBoxes;
@@ -427,8 +389,6 @@ vector<Rect> CharacterSegmenter::getBestCharBoxes(Mat img, vector<Rect> charBoxe
}
}
if (rowScore > bestRowScore)
{
bestRowScore = rowScore;
@@ -437,8 +397,6 @@ vector<Rect> CharacterSegmenter::getBestCharBoxes(Mat img, vector<Rect> charBoxe
}
}
if (this->config->debugCharSegmenter)
{
cvtColor(histoImg, histoImg, CV_GRAY2BGR);
@@ -455,7 +413,6 @@ vector<Rect> CharacterSegmenter::getBestCharBoxes(Mat img, vector<Rect> charBoxe
}
return bestBoxes;
}
@@ -489,11 +446,9 @@ vector<Rect> CharacterSegmenter::get1DHits(Mat img, int yOffset)
}
return hits;
}
void CharacterSegmenter::removeSmallContours(vector<Mat> thresholds, vector<vector<vector<Point > > > allContours, float avgCharWidth, float avgCharHeight)
{
//const float MIN_CHAR_AREA = 0.02 * avgCharWidth * avgCharHeight; // To clear out the tiny specks
@@ -559,8 +514,6 @@ vector<Rect> CharacterSegmenter::combineCloseBoxes( vector<Rect> charBoxes, floa
newCharBoxes.push_back(charBoxes[i]);
}
}
return newCharBoxes;
@@ -575,7 +528,6 @@ void CharacterSegmenter::cleanCharRegions(vector<Mat> thresholds, vector<Rect> c
Mat mask = getCharBoxMask(thresholds[0], charRegions);
for (int i = 0; i < thresholds.size(); i++)
{
bitwise_and(thresholds[i], mask, thresholds[i]);
@@ -598,7 +550,6 @@ void CharacterSegmenter::cleanCharRegions(vector<Mat> thresholds, vector<Rect> c
const float MIN_SPECKLE_HEIGHT = ((float)charRegions[j].height) * MIN_SPECKLE_HEIGHT_PERCENT;
const float MIN_CONTOUR_AREA = ((float)charRegions[j].area()) * MIN_CONTOUR_AREA_PERCENT;
int tallestContourHeight = 0;
float totalArea = 0;
for (int c = 0; c < contours.size(); c++)
@@ -608,8 +559,6 @@ void CharacterSegmenter::cleanCharRegions(vector<Mat> thresholds, vector<Rect> c
if (charRegions[j].contains(contours[c][0]) == false)
continue;
Rect r = boundingRect(contours[c]);
if (r.height <= MIN_SPECKLE_HEIGHT || r.width <= MIN_SPECKLE_WIDTH_PX)
@@ -629,7 +578,6 @@ void CharacterSegmenter::cleanCharRegions(vector<Mat> thresholds, vector<Rect> c
totalArea += contourArea(contours[c]);
}
//else if (r.height > tallestContourHeight)
//{
@@ -639,8 +587,6 @@ void CharacterSegmenter::cleanCharRegions(vector<Mat> thresholds, vector<Rect> c
}
if (totalArea < MIN_CONTOUR_AREA)
{
// Character is not voluminous enough. Erase it.
@@ -652,7 +598,6 @@ void CharacterSegmenter::cleanCharRegions(vector<Mat> thresholds, vector<Rect> c
rectangle(imgDbgCleanStages[i], boxTop, COLOR_DEBUG_MIN_AREA, -1);
}
rectangle(thresholds[i], charRegions[j], Scalar(0, 0, 0), -1);
}
else if (tallestContourHeight < ((float) charRegions[j].height * MIN_CONTOUR_HEIGHT_PERCENT))
@@ -670,7 +615,6 @@ void CharacterSegmenter::cleanCharRegions(vector<Mat> thresholds, vector<Rect> c
}
Mat closureElement = getStructuringElement( 1,
Size( 2 + 1, 2+1 ),
Point( 1, 1 ) );
@@ -692,7 +636,6 @@ void CharacterSegmenter::cleanCharRegions(vector<Mat> thresholds, vector<Rect> c
}
void CharacterSegmenter::cleanBasedOnColor(vector<Mat> thresholds, Mat colorMask, vector<Rect> charRegions)
{
// If I knock out x% of the contour area from this thing (after applying the color filter)
@@ -710,8 +653,6 @@ void CharacterSegmenter::cleanBasedOnColor(vector<Mat> thresholds, Mat colorMask
bitwise_and(thresholds[i], boxChar, boxChar);
float meanBefore = mean(boxChar, boxChar)[0];
Mat thresholdCopy(thresholds[i].size(), CV_8U);
@@ -738,7 +679,6 @@ void CharacterSegmenter::cleanBasedOnColor(vector<Mat> thresholds, Mat colorMask
// Mat tmpx = drawImageDashboard(tmpDebug, tmpytmp.type(), 1);
//drawAndWait( &tmpx );
cout << "Segmentation Filter Clean by Color: Removed Threshold " << i << " charregion " << j << endl;
cout << "Segmentation Filter Clean by Color: before=" << meanBefore << " after=" << meanAfter << endl;
@@ -751,7 +691,6 @@ void CharacterSegmenter::cleanBasedOnColor(vector<Mat> thresholds, Mat colorMask
}
}
void CharacterSegmenter::cleanMostlyFullBoxes(vector<Mat> thresholds, const vector<Rect> charRegions)
{
float MAX_FILLED = 0.95 * 255;
@@ -825,7 +764,6 @@ vector<Rect> CharacterSegmenter::filterMostlyEmptyBoxes(vector<Mat> thresholds,
height = boundingRect(allPointsInBox).height;
}
if (height >= ((float) charRegions[j].height * MIN_CONTOUR_HEIGHT_PERCENT))
{
boxScores[j] = boxScores[j] + 1;
@@ -835,7 +773,6 @@ vector<Rect> CharacterSegmenter::filterMostlyEmptyBoxes(vector<Mat> thresholds,
drawX(imgDbgCleanStages[i], charRegions[j], COLOR_DEBUG_EMPTYFILTER, 3);
}
}
}
@@ -902,7 +839,6 @@ void CharacterSegmenter::filterEdgeBoxes(vector<Mat> thresholds, const vector<Re
//while (charBoxes.size() > 0 && charBoxes[0].width < MIN_SEGMENT_WIDTH_EDGES)
// charBoxes.erase(charBoxes.begin() + 0);
// TECHNIQUE #1
// Check for long vertical lines. Once the line is too long, mask the whole region
@@ -969,7 +905,6 @@ void CharacterSegmenter::filterEdgeBoxes(vector<Mat> thresholds, const vector<Re
col++;
}
if (leftEdgeX != 0)
leftEdges.push_back(leftEdgeX);
if (rightEdgeX != thresholds[i].cols)
@@ -1007,7 +942,6 @@ void CharacterSegmenter::filterEdgeBoxes(vector<Mat> thresholds, const vector<Re
warpAffine( mask, mask, rot_mat, mask.size() );
}
// If our edge mask covers more than x% of the char region, mask the whole thing...
const float MAX_COVERAGE_PERCENT = 0.6;
int leftCoveragePx = leftEdge - charRegions[0].x;
@@ -1034,7 +968,6 @@ void CharacterSegmenter::filterEdgeBoxes(vector<Mat> thresholds, const vector<Re
bitwise_and(thresholds[i], mask, thresholds[i]);
}
if (this->config->debugCharSegmenter)
{
cout << "Edge Filter: left=" << leftEdge << " right=" << rightEdge << endl;
@@ -1163,7 +1096,6 @@ int CharacterSegmenter::getLongestBlobLengthBetweenLines(Mat img, int col)
}
return longestBlobLength;
}
@@ -1183,7 +1115,6 @@ int CharacterSegmenter::isSkinnyLineInsideBox(Mat threshold, Rect box, vector<ve
Mat boxMask = Mat::zeros(threshold.size(), CV_8U);
rectangle(boxMask, slightlySmallerBox, Scalar(255, 255, 255), -1);
for (int i = 0; i < contours.size(); i++)
{
// Only bother with the big boxes
@@ -1215,7 +1146,6 @@ int CharacterSegmenter::isSkinnyLineInsideBox(Mat threshold, Rect box, vector<ve
if (tallestContourIdx != -1)
{
//cout << "Edge Filter: " << tallestContourHeight << " -- " << avgCharHeight << endl;
if (tallestContourHeight >= avgCharHeight * 0.9 &&
((tallestContourWidth < config->segmentationMinBoxWidthPx) || (tallestContourArea < avgCharWidth * avgCharHeight * 0.1)))
@@ -1230,8 +1160,6 @@ int CharacterSegmenter::isSkinnyLineInsideBox(Mat threshold, Rect box, vector<ve
return -1;
}
Mat CharacterSegmenter::getCharBoxMask(Mat img_threshold, vector<Rect> charBoxes)
{

View File

@@ -17,11 +17,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "colorfilter.h"
ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config)
{
@@ -32,7 +29,6 @@ ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config)
this->debug = config->debugColorFiler;
this->grayscale = imageIsGrayscale(image);
if (this->debug)
@@ -48,7 +44,6 @@ ColorFilter::ColorFilter(Mat image, Mat characterMask, Config* config)
findCharColors();
if (config->debugTiming)
{
timespec endTime;
@@ -120,8 +115,6 @@ void ColorFilter::findCharColors()
vector<Vec4i> hierarchy;
findContours(erodedCharMask, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
vector<float> hMeans, sMeans, vMeans;
vector<float> hStdDevs, sStdDevs, vStdDevs;
@@ -149,9 +142,6 @@ void ColorFilter::findCharColors()
hierarchy
);
//drawAndWait(&singleCharMask);
Scalar mean;
@@ -174,7 +164,6 @@ void ColorFilter::findCharColors()
sStdDevs.push_back(stddev[1]);
vStdDevs.push_back(stddev[2]);
}
if (hMeans.size() == 0)
@@ -184,11 +173,9 @@ void ColorFilter::findCharColors()
int bestSatIndex = this->getMajorityOpinion(sMeans, .65, 35);
int bestValIndex = this->getMajorityOpinion(vMeans, .65, 30);
if (sMeans[bestSatIndex] < MINIMUM_SATURATION)
return;
bool doHueFilter = false, doSatFilter = false, doValFilter = false;
float hueMin, hueMax;
float satMin, satMax;
@@ -259,8 +246,6 @@ void ColorFilter::findCharColors()
cout << "ColorFilter Val: " << bestValIndex << " : " << setw(7) << vMeans[bestValIndex] << " -- " << valMin << "-" << valMax << endl;
}
Mat imgDebugHueOnly = Mat::zeros(hsv.size(), hsv.type());
Mat imgDebug = Mat::zeros(hsv.size(), hsv.type());
Mat imgDistanceFromCenter = Mat::zeros(hsv.size(), CV_8U);
@@ -318,7 +303,6 @@ void ColorFilter::findCharColors()
else
this->colorMask.at<uchar>(row, col) = 0;
if ((hPasses && sPasses) || (hPasses && vPasses) || (sPasses && vPasses))
{
vDistance = pow(vDistance, 0.9);
@@ -333,8 +317,6 @@ void ColorFilter::findCharColors()
}
}
vector<Mat> debugImagesSet;
if (this->debug)
@@ -346,7 +328,6 @@ void ColorFilter::findCharColors()
debugImagesSet.push_back(addLabel(maskCopy, "color Mask Before"));
}
Mat bigElement = getStructuringElement( 1,
Size( 3 + 1, 3+1 ),
Point( 1, 1 ) );
@@ -378,15 +359,12 @@ void ColorFilter::findCharColors()
debugImagesSet.push_back(addLabel(debugMask, "COLOR Hues off"));
Mat dashboard = drawImageDashboard(debugImagesSet, imgDebugHueOnly.type(), 3);
displayImage(config, "Color Filter Images", dashboard);
}
}
// Goes through an array of values, picks the winner based on the highest percentage of other values that are within the maxValDifference
// Return -1 if it fails.
int ColorFilter::getMajorityOpinion(vector<float> values, float minPercentAgreement, float maxValDifference)

View File

@@ -19,7 +19,6 @@
#include "config.h"
Config::Config(const std::string country, const std::string runtimeBaseDir)
{
this->runtimeBaseDir = runtimeBaseDir;
@@ -93,12 +92,10 @@ void Config::loadValues(string country)
ocrImageWidthPx = round(((float) templateWidthPx) * ocrImagePercent);
ocrImageHeightPx = round(((float)templateHeightPx) * ocrImagePercent);
float stateIdImagePercent = getFloat("common", "state_id_img_size_percent", 100);
stateIdImageWidthPx = round(((float)templateWidthPx) * ocrImagePercent);
stateIdimageHeightPx = round(((float)templateHeightPx) * ocrImagePercent);
charAnalysisMinPercent = getFloat(country, "char_analysis_min_pct", 0);
charAnalysisHeightRange = getFloat(country, "char_analysis_height_range", 0);
charAnalysisHeightStepSize = getFloat(country, "char_analysis_height_step_size", 0);
@@ -150,7 +147,6 @@ void Config::debugOff()
debugPostProcess = false;
}
string Config::getCascadeRuntimeDir()
{
return this->runtimeBaseDir + CASCADE_DIR;
@@ -168,9 +164,6 @@ string Config::getTessdataPrefix()
return "TESSDATA_PREFIX=" + this->runtimeBaseDir + "/ocr/";
}
float Config::getFloat(string section, string key, float defaultValue)
{
const char * pszValue = ini->GetValue(section.c_str(), key.c_str(), NULL /*default*/);

View File

@@ -17,15 +17,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "featurematcher.h"
//const int DEFAULT_QUERY_FEATURES = 305;
//const int DEFAULT_TRAINING_FEATURES = 305;
const float MAX_DISTANCE_TO_MATCH = 100.0f;
FeatureMatcher::FeatureMatcher(Config* config)
{
this->config = config;
@@ -35,7 +32,6 @@ FeatureMatcher::FeatureMatcher(Config* config)
//this->descriptorMatcher = DescriptorMatcher::create( "FlannBased" );
this->detector = new FastFeatureDetector(10, true);
this->extractor = new BRISK(10, 1, 0.9);
}
@@ -52,7 +48,6 @@ FeatureMatcher::~FeatureMatcher()
}
bool FeatureMatcher::isLoaded()
{
if( detector.empty() || extractor.empty() || descriptorMatcher.empty() )
@@ -68,9 +63,6 @@ int FeatureMatcher::numTrainingElements()
return billMapping.size();
}
void FeatureMatcher::surfStyleMatching( const Mat& queryDescriptors, vector<KeyPoint> queryKeypoints,
vector<DMatch>& matches12 )
{
@@ -78,15 +70,12 @@ void FeatureMatcher::surfStyleMatching( const Mat& queryDescriptors, vector<KeyP
this->descriptorMatcher->radiusMatch(queryDescriptors, matchesKnn, MAX_DISTANCE_TO_MATCH);
vector<DMatch> tempMatches;
_surfStyleMatching(queryDescriptors, matchesKnn, tempMatches);
crisscrossFiltering(queryKeypoints, tempMatches, matches12);
}
void FeatureMatcher::_surfStyleMatching(const Mat& queryDescriptors, vector<vector<DMatch> > matchesKnn, vector<DMatch>& matches12)
{
@@ -138,7 +127,6 @@ void FeatureMatcher::_surfStyleMatching(const Mat& queryDescriptors, vector<vect
matches12.push_back(matchesKnn[descInd][0]);
}
//}
}
else if (matchesKnn[descInd].size() == 1)
@@ -156,11 +144,8 @@ void FeatureMatcher::_surfStyleMatching(const Mat& queryDescriptors, vector<vect
//matches12.push_back(match);
//}
}
}
// Compares the matches keypoints for parallel lines. Removes matches that are criss-crossing too much
@@ -196,8 +181,6 @@ void FeatureMatcher::crisscrossFiltering(const vector<KeyPoint> queryKeypoints,
matchIdx.push_back(j);
}
// Iterate through each line (n^2) removing the one with the most criss-crosses until there are none left.
int mostIntersections = 1;
while (mostIntersections > 0 && vlines.size() > 0)
@@ -251,7 +234,6 @@ void FeatureMatcher::crisscrossFiltering(const vector<KeyPoint> queryKeypoints,
}
// Returns true if successful, false otherwise
bool FeatureMatcher::loadRecognitionSet(string country)
{
@@ -259,7 +241,6 @@ bool FeatureMatcher::loadRecognitionSet(string country)
out << config->getKeypointsRuntimeDir() << "/" << country << "/";
string country_dir = out.str();
if (DirectoryExists(country_dir.c_str()))
{
vector<Mat> trainImages;
@@ -283,7 +264,6 @@ bool FeatureMatcher::loadRecognitionSet(string country)
return -1;
}
Mat descriptors;
vector<KeyPoint> keypoints;
@@ -299,7 +279,6 @@ bool FeatureMatcher::loadRecognitionSet(string country)
}
this->descriptorMatcher->add(trainImages);
this->descriptorMatcher->train();
@@ -310,8 +289,6 @@ bool FeatureMatcher::loadRecognitionSet(string country)
}
RecognitionResult FeatureMatcher::recognize( const Mat& queryImg, bool drawOnImage, Mat* outputImage,
bool debug_on, vector<int> debug_matches_array
)
@@ -326,8 +303,6 @@ RecognitionResult FeatureMatcher::recognize( const Mat& queryImg, bool drawOnIma
detector->detect( queryImg, queryKeypoints );
extractor->compute(queryImg, queryKeypoints, queryDescriptors);
if (queryKeypoints.size() <= 5)
{
// Cut it loose if there's less than 5 keypoints... nothing would ever match anyway and it could crash the matcher.
@@ -338,17 +313,17 @@ RecognitionResult FeatureMatcher::recognize( const Mat& queryImg, bool drawOnIma
return result;
}
vector<DMatch> filteredMatches;
surfStyleMatching( queryDescriptors, queryKeypoints, filteredMatches );
// Create and initialize the counts to 0
std::vector<int> bill_match_counts( billMapping.size() );
for (int i = 0; i < billMapping.size(); i++) { bill_match_counts[i] = 0; }
for (int i = 0; i < billMapping.size(); i++)
{
bill_match_counts[i] = 0;
}
for (int i = 0; i < filteredMatches.size(); i++)
{
@@ -356,8 +331,6 @@ RecognitionResult FeatureMatcher::recognize( const Mat& queryImg, bool drawOnIma
//if (filteredMatches[i].imgIdx
}
float max_count = 0; // represented as a percent (0 to 100)
int secondmost_count = 0;
int maxcount_index = -1;
@@ -380,7 +353,6 @@ RecognitionResult FeatureMatcher::recognize( const Mat& queryImg, bool drawOnIma
else if (score > 100)
score = 100;
if (score > 0)
{
result.haswinner = true;
@@ -427,5 +399,4 @@ RecognitionResult FeatureMatcher::recognize( const Mat& queryImg, bool drawOnIma
return result;
}

View File

@@ -19,7 +19,6 @@
#include "licenseplatecandidate.h"
LicensePlateCandidate::LicensePlateCandidate(Mat frame, Rect regionOfInterest, Config* config)
{
this->config = config;
@@ -38,7 +37,6 @@ void LicensePlateCandidate::recognize()
{
charSegmenter = NULL;
this->confidence = 0;
int expandX = round(this->plateRegion.width * 0.15);
@@ -46,19 +44,14 @@ void LicensePlateCandidate::recognize()
// expand box by 15% in all directions
Rect expandedRegion = expandRect( this->plateRegion, expandX, expandY, frame.cols, frame.rows) ;
Mat plate_bgr = Mat(frame, expandedRegion);
resize(plate_bgr, plate_bgr, Size(config->templateWidthPx, config->templateHeightPx));
Mat plate_bgr_cleaned = Mat(plate_bgr.size(), plate_bgr.type());
this->cleanupColors(plate_bgr, plate_bgr_cleaned);
CharacterRegion charRegion(plate_bgr, config);
if (charRegion.confidence > 10)
{
@@ -75,13 +68,10 @@ void LicensePlateCandidate::recognize()
{
this->plateCorners = transformPointsToOriginalImage(frame, plate_bgr, expandedRegion, smallPlateCorners);
this->deskewed = deSkewPlate(frame, this->plateCorners);
charSegmenter = new CharacterSegmenter(deskewed, charRegion.thresholdsInverted(), config);
//this->recognizedText = ocr->recognizedText;
//strcpy(this->recognizedText, ocr.recognizedText);
@@ -91,13 +81,8 @@ void LicensePlateCandidate::recognize()
charRegion.confidence = 0;
}
}
// Re-maps the coordinates from the smallImage to the coordinate space of the bigImage.
vector<Point2f> LicensePlateCandidate::transformPointsToOriginalImage(Mat bigImage, Mat smallImage, Rect region, vector<Point> corners)
{
@@ -116,7 +101,6 @@ vector<Point2f> LicensePlateCandidate::transformPointsToOriginalImage(Mat bigIma
return cornerPoints;
}
Mat LicensePlateCandidate::deSkewPlate(Mat inputImage, vector<Point2f> corners)
{
@@ -159,7 +143,6 @@ Mat LicensePlateCandidate::deSkewPlate(Mat inputImage, vector<Point2f> corners)
return deskewed;
}
void LicensePlateCandidate::cleanupColors(Mat inputImage, Mat outputImage)
{
if (this->config->debugGeneral)
@@ -190,10 +173,8 @@ void LicensePlateCandidate::cleanupColors(Mat inputImage, Mat outputImage)
//ycrcb.release();
}
bilateralFilter(intermediate, outputImage, 3, 25, 35);
if (this->config->debugGeneral)
{
displayImage(config, "After cleanup", outputImage);

View File

@@ -17,9 +17,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ocr.h"
OCR::OCR(Config* config)
@@ -36,7 +33,6 @@ OCR::OCR(Config* config)
strcpy(tessdataPrefix.data(), config->getTessdataPrefix().c_str());
putenv(tessdataPrefix.data());
tesseract->Init("", config->ocrLanguage.c_str() );
tesseract->SetVariable("save_blob_choices", "T");
//tesseract->SetVariable("tessedit_char_whitelist", "ABCDEFGHIJKLMNPQRSTUVWXYZ1234567890");
@@ -50,18 +46,14 @@ OCR::~OCR()
delete tesseract;
}
void OCR::performOCR(vector<Mat> thresholds, vector<Rect> charRegions)
{
timespec startTime;
getTime(&startTime);
postProcessor->clear();
for (int i = 0; i < thresholds.size(); i++)
{
@@ -69,7 +61,6 @@ void OCR::performOCR(vector<Mat> thresholds, vector<Rect> charRegions)
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());
for (int j = 0; j < charRegions.size(); j++)
{
Rect expandedRegion = expandRect( charRegions[j], 2, 2, thresholds[i].cols, thresholds[i].rows) ;
@@ -79,7 +70,8 @@ void OCR::performOCR(vector<Mat> thresholds, vector<Rect> charRegions)
tesseract::ResultIterator* ri = tesseract->GetIterator();
tesseract::PageIteratorLevel level = tesseract::RIL_SYMBOL;
do {
do
{
const char* symbol = ri->GetUTF8Text(level);
float conf = ri->Confidence(level);
@@ -88,21 +80,21 @@ void OCR::performOCR(vector<Mat> thresholds, vector<Rect> charRegions)
int pointsize = 0;
const char* fontName = ri->WordFontAttributes(&dontcare, &dontcare, &dontcare, &dontcare, &dontcare, &dontcare, &pointsize, &fontindex);
if(symbol != 0 && pointsize >= config->ocrMinFontSize) {
if(symbol != 0 && pointsize >= config->ocrMinFontSize)
{
postProcessor->addLetter(*symbol, j, conf);
if (this->config->debugOcr)
printf("charpos%d: threshold %d: symbol %s, conf: %f font: %s (index %d) size %dpx", j, i, symbol, conf, fontName, fontindex, pointsize);
bool indent = false;
tesseract::ChoiceIterator ci(*ri);
do {
do
{
const char* choice = ci.GetUTF8Text();
postProcessor->addLetter(*choice, j, ci.Confidence());
//letterScores.addScore(*choice, j, ci.Confidence() - MIN_CONFIDENCE);
if (this->config->debugOcr)
{
@@ -112,19 +104,20 @@ void OCR::performOCR(vector<Mat> thresholds, vector<Rect> charRegions)
}
indent = true;
} while(ci.Next());
}
while(ci.Next());
}
if (this->config->debugOcr)
printf("---------------------------------------------\n");
delete[] symbol;
} while((ri->Next(level)));
}
while((ri->Next(level)));
delete ri;
}
}
if (config->debugTiming)
@@ -134,7 +127,4 @@ void OCR::performOCR(vector<Mat> thresholds, vector<Rect> charRegions)
cout << "OCR Time: " << diffclock(startTime, endTime) << "ms." << endl;
}
}

View File

@@ -17,7 +17,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "platecorners.h"
PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, CharacterRegion* charRegion, Config* config)
@@ -27,8 +26,6 @@ PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, CharacterRegi
if (this->config->debugPlateCorners)
cout << "PlateCorners constructor" << endl;
this->inputImage = inputImage;
this->plateLines = plateLines;
this->charRegion = charRegion;
@@ -36,7 +33,6 @@ PlateCorners::PlateCorners(Mat inputImage, PlateLines* plateLines, CharacterRegi
this->bestHorizontalScore = 9999999999999;
this->bestVerticalScore = 9999999999999;
Point topPoint = charRegion->getTopLine().midpoint();
Point bottomPoint = charRegion->getBottomLine().closestPointOnSegmentTo(topPoint);
this->charHeight = distanceBetweenPoints(topPoint, bottomPoint);
@@ -65,7 +61,6 @@ vector<Point> PlateCorners::findPlateCorners()
int horizontalLines = this->plateLines->horizontalLines.size();
int verticalLines = this->plateLines->verticalLines.size();
// layout horizontal lines
for (int h1 = NO_LINE; h1 < horizontalLines; h1++)
{
@@ -89,7 +84,6 @@ vector<Point> PlateCorners::findPlateCorners()
}
}
if (this->config->debugPlateCorners)
{
@@ -100,15 +94,11 @@ vector<Point> PlateCorners::findPlateCorners()
for (int i = 0; i < 4; i++)
circle(imgCorners, charRegion->getCharArea()[i], 2, Scalar(0, 0, 0));
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->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);
displayImage(config, "Winning top/bottom Boundaries", imgCorners);
}
@@ -127,8 +117,6 @@ vector<Point> PlateCorners::findPlateCorners()
corners.push_back(bestBottom.intersection(bestRight));
corners.push_back(bestBottom.intersection(bestLeft));
if (config->debugTiming)
{
timespec endTime;
@@ -139,7 +127,6 @@ vector<Point> PlateCorners::findPlateCorners()
return corners;
}
void PlateCorners::scoreVerticals(int v1, int v2)
{
@@ -148,8 +135,6 @@ void PlateCorners::scoreVerticals(int v1, int v2)
LineSegment left;
LineSegment right;
float charHeightToPlateWidthRatio = config->plateWidthMM / config->charHeightMM;
float idealPixelWidth = this->charHeight * (charHeightToPlateWidthRatio * 1.05); // Add 10% so we don't clip any characters
@@ -183,8 +168,6 @@ void PlateCorners::scoreVerticals(int v1, int v2)
score += SCORING_MISSING_SEGMENT_PENALTY_VERTICAL;
}
// Make sure this line is to the left of our license plate letters
if (left.isPointBelowLine(charRegion->getCharBoxLeft().midpoint()) == false)
return;
@@ -193,7 +176,6 @@ void PlateCorners::scoreVerticals(int v1, int v2)
if (right.isPointBelowLine(charRegion->getCharBoxRight().midpoint()))
return;
/////////////////////////////////////////////////////////////////////////
// Score "Distance from the edge...
/////////////////////////////////////////////////////////////////////////
@@ -204,7 +186,6 @@ void PlateCorners::scoreVerticals(int v1, int v2)
float distanceFromEdge = leftDistanceFromEdge + rightDistanceFromEdge;
score += distanceFromEdge * SCORING_VERTICALDISTANCE_FROMEDGE_WEIGHT;
/////////////////////////////////////////////////////////////////////////
// Score "Boxiness" of the 4 lines. How close is it to a parallelogram?
/////////////////////////////////////////////////////////////////////////
@@ -213,12 +194,10 @@ void PlateCorners::scoreVerticals(int v1, int v2)
score += (verticalAngleDiff) * SCORING_BOXINESS_WEIGHT;
//////////////////////////////////////////////////////////////////////////
// SCORE the shape wrt character position and height relative to position
//////////////////////////////////////////////////////////////////////////
Point leftMidLinePoint = left.closestPointOnSegmentTo(charRegion->getCharBoxLeft().midpoint());
Point rightMidLinePoint = right.closestPointOnSegmentTo(charRegion->getCharBoxRight().midpoint());
@@ -230,7 +209,6 @@ void PlateCorners::scoreVerticals(int v1, int v2)
{
float scorecomponent;
if (this->config->debugPlateCorners)
{
cout << "xx xx Score: charHeight " << this->charHeight << endl;
@@ -277,7 +255,6 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
float charHeightToPlateHeightRatio = config->plateHeightMM / config->charHeightMM;
float idealPixelHeight = this->charHeight * charHeightToPlateHeightRatio;
if (h1 == NO_LINE && h2 == NO_LINE)
{
// return;
@@ -308,39 +285,26 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
score += SCORING_MISSING_SEGMENT_PENALTY_HORIZONTAL;
}
// Make sure this line is above our license plate letters
if (top.isPointBelowLine(charRegion->getCharBoxTop().midpoint()) == false)
return;
// Make sure this line is below our license plate letters
if (bottom.isPointBelowLine(charRegion->getCharBoxBottom().midpoint()))
return;
// We now have 4 possible lines. Let's put them to the test and score them...
/////////////////////////////////////////////////////////////////////////
// Score "Boxiness" of the 4 lines. How close is it to a parallelogram?
/////////////////////////////////////////////////////////////////////////
float horizontalAngleDiff = abs(top.angle - bottom.angle);
score += (horizontalAngleDiff) * SCORING_BOXINESS_WEIGHT;
// if (this->debug)
// cout << "PlateCorners boxiness score: " << (horizontalAngleDiff + verticalAngleDiff) * SCORING_BOXINESS_WEIGHT << endl;
//////////////////////////////////////////////////////////////////////////
// SCORE the shape wrt character position and height relative to position
//////////////////////////////////////////////////////////////////////////
@@ -351,7 +315,6 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
// Get the height difference
float heightRatio = charHeight / plateHeightPx;
float idealHeightRatio = (config->charHeightMM / config->plateHeightMM);
//if (leftRatio < MIN_CHAR_HEIGHT_RATIO || leftRatio > MAX_CHAR_HEIGHT_RATIO || rightRatio < MIN_CHAR_HEIGHT_RATIO || rightRatio > MAX_CHAR_HEIGHT_RATIO)
@@ -367,7 +330,6 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
// float idealBottomDistance = charHeight * (BOTTOM_WHITESPACE_HEIGHT_MM / CHARACTER_HEIGHT_MM);
// float distScore = abs(topDistance - idealTopDistance) + abs(bottomDistance - idealBottomDistance);
score += heightRatioDiff * SCORING_PLATEHEIGHT_WEIGHT;
//////////////////////////////////////////////////////////////////////////
@@ -398,14 +360,11 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
float charanglediff = abs(charAngle - top.angle) + abs(charAngle - bottom.angle);
score += charanglediff * SCORING_ANGLE_MATCHES_LPCHARS_WEIGHT;
// if (this->debug)
// cout << "PlateCorners boxiness score: " << charanglediff * SCORING_ANGLE_MATCHES_LPCHARS_WEIGHT << endl;
if (score < this->bestHorizontalScore)
{
float scorecomponent;
@@ -442,6 +401,4 @@ void PlateCorners::scoreHorizontals(int h1, int h2)
bestBottom = LineSegment(bottom.p1.x, bottom.p1.y, bottom.p2.x, bottom.p2.y);
}
}

View File

@@ -19,7 +19,6 @@
#include "platelines.h"
PlateLines::PlateLines(Config* config)
{
this->config = config;
@@ -28,7 +27,6 @@ PlateLines::PlateLines(Config* config)
if (debug)
cout << "PlateLines constructor" << endl;
}
PlateLines::~PlateLines()
@@ -36,18 +34,14 @@ PlateLines::~PlateLines()
}
void PlateLines::processImage(Mat inputImage, float sensitivity)
{
if (this->debug)
cout << "PlateLines findLines" << endl;
timespec startTime;
getTime(&startTime);
Mat smoothed(inputImage.size(), inputImage.type());
inputImage.copyTo(smoothed);
int morph_elem = 2;
@@ -65,12 +59,9 @@ void PlateLines::processImage(Mat inputImage, float sensitivity)
element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
morphologyEx( smoothed, smoothed, MORPH_OPEN, element );
Mat edges(inputImage.size(), inputImage.type());
Canny(smoothed, edges, 66, 133);
vector<LineSegment> hlines = this->getLines(edges, sensitivity, false);
vector<LineSegment> vlines = this->getLines(edges, sensitivity, true);
for (int i = 0; i < hlines.size(); i++)
@@ -78,9 +69,6 @@ void PlateLines::processImage(Mat inputImage, float sensitivity)
for (int i = 0; i < vlines.size(); i++)
this->verticalLines.push_back(vlines[i]);
// if debug is enabled, draw the image
if (this->debug)
{
@@ -109,8 +97,6 @@ void PlateLines::processImage(Mat inputImage, float sensitivity)
displayImage(config, "Hough Lines", dashboard);
}
if (config->debugTiming)
{
timespec endTime;
@@ -119,7 +105,6 @@ void PlateLines::processImage(Mat inputImage, float sensitivity)
}
//smoothed.release();
//////////////// METHOD2!!!!!!!////////////////////
/*
@@ -127,18 +112,13 @@ void PlateLines::processImage(Mat inputImage, float sensitivity)
Mat imgCanny;
GaussianBlur(inputImage, imgBlur, Size(9, 9), 1, 1);
Canny(imgBlur, imgCanny, 10, 30, 3);
//int morph_elem = 2;
//int morph_size = 1;
//Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
morphologyEx( imgCanny, imgCanny, MORPH_CLOSE, element );
Mat imgShaped;
imgCanny.copyTo(imgShaped);
//Find contours of possibles characters
@@ -187,7 +167,6 @@ vector<LineSegment> PlateLines::getLines(Mat edges, bool vertical)
vector<double> errors;
lswms.run(edges, lsegs, errors);
for( size_t i = 0; i < lsegs.size(); i++ )
{
@@ -247,7 +226,6 @@ vector<LineSegment> PlateLines::getLines(Mat edges, bool vertical)
}
*/
vector<LineSegment> PlateLines::getLines(Mat edges, float sensitivityMultiplier, bool vertical)
{
if (this->debug)
@@ -267,7 +245,6 @@ vector<LineSegment> PlateLines::getLines(Mat edges, float sensitivityMultiplier,
HoughLines( edges, allLines, 1, CV_PI/180, sensitivity, 0, 0 );
for( size_t i = 0; i < allLines.size(); i++ )
{
float rho = allLines[i][0], theta = allLines[i][1];
@@ -325,20 +302,14 @@ vector<LineSegment> PlateLines::getLines(Mat edges, float sensitivityMultiplier,
}
}
return filteredLines;
}
Mat PlateLines::customGrayscaleConversion(Mat src)
{
Mat img_hsv;
cvtColor(src,img_hsv,CV_BGR2HSV);
Mat grayscale = Mat(img_hsv.size(), CV_8U );
Mat hue(img_hsv.size(), CV_8U );
@@ -352,7 +323,6 @@ Mat PlateLines::customGrayscaleConversion(Mat src)
int pixval = pow(v, 1.05);
if (pixval > 255)
pixval = 255;
grayscale.at<uchar>(row, col) = pixval;

View File

@@ -19,7 +19,6 @@
#include "postprocess.h"
PostProcess::PostProcess(Config* config)
{
this->config = config;
@@ -29,7 +28,6 @@ PostProcess::PostProcess(Config* config)
std::ifstream infile(filename.str().c_str());
string region, pattern;
while (infile >> region >> pattern)
{
@@ -61,7 +59,8 @@ PostProcess::~PostProcess()
// TODO: Delete all entries in rules vector
map<string, vector<RegexRule*> >::iterator iter;
for (iter = rules.begin(); iter != rules.end(); ++iter) {
for (iter = rules.begin(); iter != rules.end(); ++iter)
{
for (int i = 0; i < iter->second.size(); i++)
{
delete iter->second[i];
@@ -70,7 +69,6 @@ PostProcess::~PostProcess()
}
}
void PostProcess::addLetter(char letter, int charposition, float score)
{
if (score < config->postProcessMinConfidence)
@@ -96,7 +94,6 @@ void PostProcess::insertLetter(char letter, int charposition, float score)
score = score - config->postProcessMinConfidence;
int existingIndex = -1;
if (letters.size() < charposition + 1)
{
@@ -134,7 +131,6 @@ void PostProcess::insertLetter(char letter, int charposition, float score)
}
void PostProcess::clear()
{
for (int i = 0; i < letters.size(); i++)
@@ -157,8 +153,6 @@ void PostProcess::analyze(string templateregion, int topn)
timespec startTime;
getTime(&startTime);
// Get a list of missing positions
for (int i = letters.size() -1; i >= 0; i--)
{
@@ -168,11 +162,9 @@ void PostProcess::analyze(string templateregion, int topn)
}
}
if (letters.size() == 0)
return;
// Sort the letters as they are
for (int i = 0; i < letters.size(); i++)
{
@@ -180,8 +172,6 @@ void PostProcess::analyze(string templateregion, int topn)
sort(letters[i].begin(), letters[i].end(), letterCompare);
}
if (this->config->debugPostProcess)
{
@@ -211,7 +201,6 @@ void PostProcess::analyze(string templateregion, int topn)
vector<Letter> tmp;
findAllPermutations(tmp, 0, config->postProcessMaxSubstitutions);
timespec sortStartTime;
getTime(&sortStartTime);
@@ -228,11 +217,8 @@ void PostProcess::analyze(string templateregion, int topn)
cout << " -- PostProcess Sort Time: " << diffclock(sortStartTime, sortEndTime) << "ms." << endl;
}
matchesTemplate = false;
if (templateregion != "")
{
vector<RegexRule*> regionRules = rules[templateregion];
@@ -251,8 +237,6 @@ void PostProcess::analyze(string templateregion, int topn)
}
}
if (i >= topn - 1)
break;
//if (matchesTemplate || i >= TOP_N - 1)
@@ -289,12 +273,9 @@ void PostProcess::analyze(string templateregion, int topn)
}
if (this->config->debugPostProcess)
{
// Print top words
for (int i = 0; i < allPossibilities.size(); i++)
{
@@ -309,8 +290,6 @@ void PostProcess::analyze(string templateregion, int topn)
cout << allPossibilities.size() << " total permutations" << endl;
}
if (config->debugTiming)
{
timespec endTime;
@@ -372,7 +351,6 @@ vector<int> PostProcess::getMaxDepth(int topn)
nextLeastDropCharPos = getNextLeastDrop(depth);
}
return depth;
}
@@ -465,14 +443,10 @@ void PostProcess::findAllPermutations(vector<Letter> prevletters, int charPos, i
findAllPermutations(prevletters, charPos + 1, substitutionsLeft);
}
}
bool wordCompare( const PPResult &left, const PPResult &right ){
bool wordCompare( const PPResult &left, const PPResult &right )
{
if (left.totalscore < right.totalscore)
return false;
return true;
@@ -486,7 +460,6 @@ bool letterCompare( const Letter &left, const Letter &right )
return true;
}
RegexRule::RegexRule(string region, string pattern)
{
this->original = pattern;
@@ -529,7 +502,6 @@ RegexRule::RegexRule(string region, string pattern)
// cout << "AA Skip position: " << skipPositions[z] << endl;
}
bool RegexRule::match(string text)
{
if (text.length() != numchars)

View File

@@ -17,11 +17,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "regiondetector.h"
RegionDetector::RegionDetector(Config* config)
{
this->config = config;
@@ -38,7 +35,6 @@ RegionDetector::RegionDetector(Config* config)
this->plate_cascade = new CascadeClassifier();
}
if( this->plate_cascade->load( config->getCascadeRuntimeDir() + config->country + ".xml" ) )
{
this->loaded = true;
@@ -49,8 +45,6 @@ RegionDetector::RegionDetector(Config* config)
printf("--(!)Error loading classifier\n");
}
}
RegionDetector::~RegionDetector()
@@ -58,14 +52,11 @@ RegionDetector::~RegionDetector()
delete this->plate_cascade;
}
bool RegionDetector::isLoaded()
{
return this->loaded;
}
vector<Rect> RegionDetector::detect(Mat frame)
{
@@ -77,7 +68,6 @@ vector<Rect> RegionDetector::detect(Mat frame)
return regionsOfInterest;
}
/** @function detectAndDisplay */
vector<Rect> RegionDetector::doCascade(Mat frame)
{
@@ -111,7 +101,6 @@ vector<Rect> RegionDetector::doCascade(Mat frame)
minSize, maxSize );
}
if (config->debugTiming)
{
timespec endTime;
@@ -119,8 +108,6 @@ vector<Rect> RegionDetector::doCascade(Mat frame)
cout << "LBP Time: " << diffclock(startTime, endTime) << "ms." << endl;
}
for( int i = 0; i < plates.size(); i++ )
{
plates[i].x = plates[i].x / scale_factor;

View File

@@ -23,7 +23,7 @@ snippets(
bool a_bIsUtf8,
bool a_bUseMultiKey,
bool a_bUseMultiLine
)
)
{
// LOADING DATA
@@ -74,7 +74,8 @@ snippets(
// output all of the items
CSimpleIniA::TNamesDepend::const_iterator i;
for (i = values.begin(); i != values.end(); ++i) {
for (i = values.begin(); i != values.end(); ++i)
{
printf("key-name = '%s'\n", i->pItem);
}

View File

@@ -19,7 +19,6 @@
#include "stateidentifier.h"
StateIdentifier::StateIdentifier(Config* config)
{
this->config = config;
@@ -66,19 +65,16 @@ int StateIdentifier::recognize(Mat img, char* stateCode)
plateImg.copyTo(debugImg);
vector<int> matchesArray(featureMatcher->numTrainingElements());
RecognitionResult result = featureMatcher->recognize(plateImg, true, &debugImg, true, matchesArray );
if (this->config->debugStateId)
{
displayImage(config, "State Identifier1", plateImg);
displayImage(config, "State Identifier", debugImg);
cout << result.haswinner << " : " << result.confidence << " : " << result.winner << endl;
}
if (config->debugTiming)
{
timespec endTime;
@@ -86,12 +82,10 @@ int StateIdentifier::recognize(Mat img, char* stateCode)
cout << "State Identification Time: " << diffclock(startTime, endTime) << "ms." << endl;
}
if (result.haswinner == false)
return 0;
strcpy(stateCode, result.winner.c_str());
return result.confidence;
}

View File

@@ -1,12 +1,13 @@
#include "filesystem.h"
bool hasEnding (std::string const &fullString, std::string const &ending)
{
if (fullString.length() >= ending.length()) {
if (fullString.length() >= ending.length())
{
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
} else {
}
else
{
return false;
}
}
@@ -47,14 +48,18 @@ std::vector<std::string> getFilesInDir(const char* dirPath)
std::vector<std::string> files;
struct dirent *ent;
if ((dir = opendir (dirPath)) != NULL) {
if ((dir = opendir (dirPath)) != NULL)
{
/* print all the files and directories within directory */
while ((ent = readdir (dir)) != NULL) {
while ((ent = readdir (dir)) != NULL)
{
if (strcmp(ent->d_name, ".") != 0 && strcmp(ent->d_name, "..") != 0)
files.push_back(ent->d_name);
}
closedir (dir);
} else {
}
else
{
/* could not open directory */
perror ("");
return files;
@@ -63,8 +68,8 @@ std::vector<std::string> getFilesInDir(const char* dirPath)
return files;
}
bool stringCompare( const std::string &left, const std::string &right ){
bool stringCompare( const std::string &left, const std::string &right )
{
for( std::string::const_iterator lit = left.begin(), rit = right.begin(); lit != left.end() && rit != right.end(); ++lit, ++rit )
if( tolower( *lit ) < tolower( *rit ) )
return true;

View File

@@ -1,9 +1,7 @@
#include "timing.h"
timespec diff(timespec start, timespec end);
#ifdef WINDOWS
// Windows timing code
@@ -37,20 +35,25 @@ int clock_gettime(int X, timespec *tv)
static int initialized = 0;
static BOOL usePerformanceCounter = 0;
if (!initialized) {
if (!initialized)
{
LARGE_INTEGER performanceFrequency;
initialized = 1;
usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency);
if (usePerformanceCounter) {
if (usePerformanceCounter)
{
QueryPerformanceCounter(&offset);
frequencyToMicroseconds = (double)performanceFrequency.QuadPart / 1000000.;
} else {
}
else
{
offset = getFILETIMEoffset();
frequencyToMicroseconds = 10.;
}
}
if (usePerformanceCounter) QueryPerformanceCounter(&t);
else {
else
{
GetSystemTimeAsFileTime(&f);
t.QuadPart = f.dwHighDateTime;
t.QuadPart <<= 32;
@@ -65,8 +68,6 @@ int clock_gettime(int X, timespec *tv)
return (0);
}
void getTime(timespec* time)
{
clock_gettime(0, time);
@@ -77,17 +78,19 @@ double diffclock(timespec time1,timespec time2)
timespec delta = diff(time1,time2);
double milliseconds = (delta.tv_sec * 1000) + (((double) delta.tv_usec) / 1000.0);
return milliseconds;
}
timespec diff(timespec start, timespec end)
{
timespec temp;
if ((end.tv_usec-start.tv_usec)<0) {
if ((end.tv_usec-start.tv_usec)<0)
{
temp.tv_sec = end.tv_sec-start.tv_sec-1;
temp.tv_usec = 1000000+end.tv_usec-start.tv_usec;
} else {
}
else
{
temp.tv_sec = end.tv_sec-start.tv_sec;
temp.tv_usec = end.tv_usec-start.tv_usec;
}
@@ -96,8 +99,6 @@ timespec diff(timespec start, timespec end)
#else
void getTime(timespec* time)
{
@@ -120,24 +121,24 @@ double diffclock(timespec time1,timespec time2)
timespec delta = diff(time1,time2);
double milliseconds = (delta.tv_sec * 1000) + (((double) delta.tv_nsec) / 1000000.0);
return milliseconds;
}
timespec diff(timespec start, timespec end)
{
timespec temp;
if ((end.tv_nsec-start.tv_nsec)<0) {
if ((end.tv_nsec-start.tv_nsec)<0)
{
temp.tv_sec = end.tv_sec-start.tv_sec-1;
temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
} else {
}
else
{
temp.tv_sec = end.tv_sec-start.tv_sec;
temp.tv_nsec = end.tv_nsec-start.tv_nsec;
}
return temp;
}
#endif

View File

@@ -17,12 +17,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "utility.h"
Rect expandRect(Rect original, int expandXPixels, int expandYPixels, int maxX, int maxY)
{
Rect expandedRegion = Rect(original);
@@ -90,8 +86,6 @@ Mat addLabel(Mat input, string label)
return newImage;
}
void drawAndWait(cv::Mat* frame)
{
cv::imshow("Temp Window", *frame);
@@ -137,7 +131,8 @@ vector<Mat> produceThresholds(const Mat img_gray, Config* config)
NiblackSauvolaWolfJolion (img_gray, thresholds[i++], WOLFJOLION, win, win, 0.05 + (k * 0.35));
bitwise_not(thresholds[i-1], thresholds[i-1]);
k = 1; win = 22;
k = 1;
win = 22;
NiblackSauvolaWolfJolion (img_gray, thresholds[i++], WOLFJOLION, win, win, 0.05 + (k * 0.35));
bitwise_not(thresholds[i-1], thresholds[i-1]);
//NiblackSauvolaWolfJolion (img_gray, thresholds[i++], WOLFJOLION, win, win, 0.05 + (k * 0.35));
@@ -151,11 +146,6 @@ vector<Mat> produceThresholds(const Mat img_gray, Config* config)
NiblackSauvolaWolfJolion (img_gray, thresholds[i++], SAUVOLA, 12, 12, 0.18 * k);
bitwise_not(thresholds[i-1], thresholds[i-1]);
if (config->debugTiming)
{
timespec endTime;
@@ -180,7 +170,6 @@ double median(int array[], int arraySize)
return arraySize % 2 ? array[arraySize / 2] : (array[arraySize / 2 - 1] + array[arraySize / 2]) / 2;
}
Mat equalizeBrightness(Mat img)
{
@@ -194,7 +183,6 @@ Mat equalizeBrightness(Mat img)
normalize(img, img, 0, 255, NORM_MINMAX);
img.convertTo(img, CV_8U); // convert back to unsigned int
return img;
}
@@ -229,7 +217,6 @@ void fillMask(Mat img, const Mat mask, Scalar color)
}
}
void drawX(Mat img, Rect rect, Scalar color, int thickness)
{
Point tl(rect.x, rect.y);
@@ -257,7 +244,6 @@ float angleBetweenPoints(Point p1, Point p2)
return atan2((float) deltaY, (float) deltaX) * (180 / CV_PI);
}
Size getSizeMaintainingAspect(Mat inputImg, int maxWidth, int maxHeight)
{
float aspect = ((float) inputImg.cols) / ((float) inputImg.rows);
@@ -272,7 +258,6 @@ Size getSizeMaintainingAspect(Mat inputImg, int maxWidth, int maxHeight)
}
}
LineSegment::LineSegment()
{
init(0, 0, 0, 0);
@@ -302,7 +287,8 @@ void LineSegment::init(int x1, int y1, int x2, int y2)
this->angle = angleBetweenPoints(p1, p2);
}
bool LineSegment::isPointBelowLine( Point tp ){
bool LineSegment::isPointBelowLine( Point tp )
{
return ((p2.x - p1.x)*(tp.y - p1.y) - (p2.y - p1.y)*(tp.x - p1.x)) > 0;
}
@@ -331,7 +317,6 @@ Point LineSegment::intersection(LineSegment line)
float c1, c2;
float intersection_X = -1, intersection_Y= -1;
c1 = p1.y - slope * p1.x; // which is same as y2 - slope * x2
c2 = line.p2.y - line.slope * line.p2.x; // which is same as y2 - slope * x2
@@ -361,8 +346,6 @@ Point LineSegment::intersection(LineSegment line)
}
Point LineSegment::midpoint()
{
// Handle the case where the line is vertical

View File

@@ -23,8 +23,6 @@ VerticalHistogram::VerticalHistogram(Mat inputImage, Mat mask)
{
analyzeImage(inputImage, mask);
}
VerticalHistogram::~VerticalHistogram()
@@ -33,13 +31,11 @@ VerticalHistogram::~VerticalHistogram()
colHeights.clear();
}
void VerticalHistogram::analyzeImage(Mat inputImage, Mat mask)
{
highestPeak = 0;
lowestValley = inputImage.rows;
histoImg = Mat::zeros(inputImage.size(), CV_8U);
int columnCount;
@@ -55,7 +51,6 @@ void VerticalHistogram::analyzeImage(Mat inputImage, Mat mask)
columnCount++;
}
this->colHeights.push_back(columnCount);
if (columnCount < lowestValley)
@@ -63,7 +58,6 @@ void VerticalHistogram::analyzeImage(Mat inputImage, Mat mask)
if (columnCount > highestPeak)
highestPeak = columnCount;
for (; columnCount > 0; columnCount--)
histoImg.at<uchar>(inputImage.rows - columnCount, col) = 255;
@@ -132,7 +126,6 @@ void VerticalHistogram::findValleys()
if (colHeights[i] > relativePeakHeight)
relativePeakHeight = colHeights[i];
prevDirection = FLAT;
}
@@ -181,7 +174,6 @@ HistogramDirection VerticalHistogram::getHistogramDirection(int index)
}
forwardAverage = forwardAverage / ((float) (1 + forwardEndIndex - index));
float diff = forwardAverage - trailingAverage;
float minDiff = ((float) (highestPeak - lowestValley)) * 0.10;