From 75c6784369be54b53e613e6c80d3f12798ad3306 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Sun, 20 Sep 2015 21:04:55 -0400 Subject: [PATCH] Refactored histogram to support horizontal histograms --- src/openalpr/CMakeLists.txt | 4 +- src/openalpr/segmentation/histogram.cpp | 259 ++++++++++++++++++ src/openalpr/segmentation/histogram.h | 60 ++++ .../segmentation/histogramhorizontal.cpp | 28 ++ .../segmentation/histogramhorizontal.h | 35 +++ .../segmentation/histogramvertical.cpp | 37 +++ src/openalpr/segmentation/histogramvertical.h | 43 +++ .../segmentation/verticalhistogram.cpp | 222 --------------- src/openalpr/segmentation/verticalhistogram.h | 71 ----- 9 files changed, 465 insertions(+), 294 deletions(-) create mode 100644 src/openalpr/segmentation/histogram.cpp create mode 100644 src/openalpr/segmentation/histogram.h create mode 100644 src/openalpr/segmentation/histogramhorizontal.cpp create mode 100644 src/openalpr/segmentation/histogramhorizontal.h create mode 100644 src/openalpr/segmentation/histogramvertical.cpp create mode 100644 src/openalpr/segmentation/histogramvertical.h delete mode 100644 src/openalpr/segmentation/verticalhistogram.cpp delete mode 100644 src/openalpr/segmentation/verticalhistogram.h diff --git a/src/openalpr/CMakeLists.txt b/src/openalpr/CMakeLists.txt index 2219465..0f6a104 100644 --- a/src/openalpr/CMakeLists.txt +++ b/src/openalpr/CMakeLists.txt @@ -18,7 +18,9 @@ set(lpr_source_files postprocess/regexrule.cpp binarize_wolf.cpp segmentation/charactersegmenter.cpp - segmentation/verticalhistogram.cpp + segmentation/histogram.cpp + segmentation/histogramvertical.cpp + segmentation/histogramhorizontal.cpp edges/edgefinder.cpp edges/platecorners.cpp edges/platelines.cpp diff --git a/src/openalpr/segmentation/histogram.cpp b/src/openalpr/segmentation/histogram.cpp new file mode 100644 index 0000000..afa009c --- /dev/null +++ b/src/openalpr/segmentation/histogram.cpp @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2015 OpenALPR Technology, Inc. + * Open source 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 . +*/ + +#include "histogram.h" + +using namespace cv; +using namespace std; + +namespace alpr +{ + + Histogram::Histogram() + { + + } + + Histogram::~Histogram() + { + histoImg.release(); + colHeights.clear(); + } + + void Histogram::analyzeImage(cv::Mat inputImage, cv::Mat mask, bool use_y_axis) + { + + int max_col_size = 0; + + int columnCount; + + if (use_y_axis) + { + // Calculate the histogram for vertical stripes + for (int col = 0; col < inputImage.cols; col++) + { + columnCount = 0; + + for (int row = 0; row < inputImage.rows; row++) + { + if (inputImage.at(row, col) > 0 && mask.at(row, col) > 0) + columnCount++; + } + + this->colHeights.push_back(columnCount); + + if (columnCount > max_col_size) + max_col_size = columnCount; + } + } + else + { + // Calculate the histogram for horizontal stripes + for (int row = 0; row < inputImage.rows; row++) + { + columnCount = 0; + + for (int col = 0; col < inputImage.cols; col++) + { + if (inputImage.at(row, col) > 0 && mask.at(row, col) > 0) + columnCount++; + } + + this->colHeights.push_back(columnCount); + + if (columnCount > max_col_size) + max_col_size = columnCount; + } + } + + drawAndWait(inputImage); + + int histo_width = this->colHeights.size(); + int histo_height = max_col_size + 10; + + histoImg = Mat::zeros(Size(histo_width, histo_height), CV_8U); + + // Draw the columns onto an Mat image + for (unsigned int col = 0; col < histoImg.cols; col++) + { + if (col >= this->colHeights.size()) + break; + + int columnCount = this->colHeights[col]; + for (; columnCount > 0; columnCount--) + histoImg.at(histo_height - columnCount, col) = 255; + } + + drawAndWait(histoImg); + } + + int Histogram::getLocalMinimum(int leftX, int rightX) + { + int minimum = histoImg.rows + 1; + int lowestX = leftX; + + for (int i = leftX; i <= rightX; i++) + { + if (colHeights[i] < minimum) + { + lowestX = i; + minimum = colHeights[i]; + } + } + + return lowestX; + } + + int Histogram::getLocalMaximum(int leftX, int rightX) + { + int maximum = -1; + int highestX = leftX; + + for (int i = leftX; i <= rightX; i++) + { + if (colHeights[i] > maximum) + { + highestX = i; + maximum = colHeights[i]; + } + } + + return highestX; + } + + int Histogram::getHeightAt(int x) + { + return colHeights[x]; + } + + + + int Histogram::detect_peak( + const double* data, /* the data */ + int data_count, /* row count of data */ + int* emi_peaks, /* emission peaks will be put here */ + int* num_emi_peaks, /* number of emission peaks found */ + int max_emi_peaks, /* maximum number of emission peaks */ + int* absop_peaks, /* absorption peaks will be put here */ + int* num_absop_peaks, /* number of absorption peaks found */ + int max_absop_peaks, /* maximum number of absorption peaks + */ + double delta, /* delta used for distinguishing peaks */ + int emi_first /* should we search emission peak first of + absorption peak first? */ + ) + { + int i; + double mx; + double mn; + int mx_pos = 0; + int mn_pos = 0; + int is_detecting_emi = emi_first; + + + mx = data[0]; + mn = data[0]; + + *num_emi_peaks = 0; + *num_absop_peaks = 0; + + for(i = 1; i < data_count; ++i) + { + if(data[i] > mx) + { + mx_pos = i; + mx = data[i]; + } + if(data[i] < mn) + { + mn_pos = i; + mn = data[i]; + } + + if(is_detecting_emi && + data[i] < mx - delta) + { + if(*num_emi_peaks >= max_emi_peaks) /* not enough spaces */ + return 1; + + emi_peaks[*num_emi_peaks] = mx_pos; + ++ (*num_emi_peaks); + + is_detecting_emi = 0; + + i = mx_pos - 1; + + mn = data[mx_pos]; + mn_pos = mx_pos; + } + else if((!is_detecting_emi) && + data[i] > mn + delta) + { + if(*num_absop_peaks >= max_absop_peaks) + return 2; + + absop_peaks[*num_absop_peaks] = mn_pos; + ++ (*num_absop_peaks); + + is_detecting_emi = 1; + + i = mn_pos - 1; + + mx = data[mn_pos]; + mx_pos = mn_pos; + } + } + + return 0; + } + + + vector > Histogram::get1DHits(int yOffset) + { + + vector > hits; + + bool onSegment = false; + int curSegmentLength = 0; + for (int col = 0; col < histoImg.cols; col++) + { + bool isOn = histoImg.at(histoImg.rows - 1 - yOffset, col); + if (isOn) + { + // We're on a segment. Increment the length + onSegment = true; + curSegmentLength++; + } + + if (onSegment && (isOn == false || (col == histoImg.cols - 1))) + { + + // A segment just ended or we're at the very end of the row and we're on a segment + pair pair(col - curSegmentLength, col); + hits.push_back(pair); + + onSegment = false; + curSegmentLength = 0; + } + } + + + return hits; + } +} \ No newline at end of file diff --git a/src/openalpr/segmentation/histogram.h b/src/openalpr/segmentation/histogram.h new file mode 100644 index 0000000..8b6df0a --- /dev/null +++ b/src/openalpr/segmentation/histogram.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015 OpenALPR Technology, Inc. + * Open source 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 . +*/ + +#ifndef OPENALPR_HISTOGRAM_H +#define OPENALPR_HISTOGRAM_H + +#include "opencv2/imgproc/imgproc.hpp" +#include "utility.h" + +namespace alpr +{ + + class Histogram + { + public: + Histogram(); + virtual ~Histogram(); + + cv::Mat histoImg; + + // Returns the lowest X position between two points. + int getLocalMinimum(int leftX, int rightX); + // Returns the highest X position between two points. + int getLocalMaximum(int leftX, int rightX); + + int getHeightAt(int x); + + std::vector > get1DHits(int yOffset); + + protected: + + std::vector colHeights; + + + void analyzeImage(cv::Mat inputImage, cv::Mat mask, bool use_y_axis); + + int detect_peak(const double *data, int data_count, int *emi_peaks, + int *num_emi_peaks, int max_emi_peaks, int *absop_peaks, + int *num_absop_peaks, int max_absop_peaks, double delta, + int emi_first); + }; + +} +#endif //OPENALPR_HISTOGRAM_H diff --git a/src/openalpr/segmentation/histogramhorizontal.cpp b/src/openalpr/segmentation/histogramhorizontal.cpp new file mode 100644 index 0000000..382bcb2 --- /dev/null +++ b/src/openalpr/segmentation/histogramhorizontal.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2015 OpenALPR Technology, Inc. + * Open source 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 . +*/ + +#include "histogramhorizontal.h" + +namespace alpr +{ + HistogramHorizontal::HistogramHorizontal(cv::Mat inputImage, cv::Mat mask) { + + analyzeImage(inputImage, mask, false); + } +} \ No newline at end of file diff --git a/src/openalpr/segmentation/histogramhorizontal.h b/src/openalpr/segmentation/histogramhorizontal.h new file mode 100644 index 0000000..5887923 --- /dev/null +++ b/src/openalpr/segmentation/histogramhorizontal.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015 OpenALPR Technology, Inc. + * Open source 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 . +*/ + +#ifndef OPENALPR_HISTOGRAMHORIZONTAL_H +#define OPENALPR_HISTOGRAMHORIZONTAL_H + +#include "opencv2/imgproc/imgproc.hpp" +#include "histogram.h" + +namespace alpr +{ + class HistogramHorizontal : public Histogram + { + public: + HistogramHorizontal(cv::Mat inputImage, cv::Mat mask); + }; + +} +#endif //OPENALPR_HISTOGRAMHORIZONTAL_H diff --git a/src/openalpr/segmentation/histogramvertical.cpp b/src/openalpr/segmentation/histogramvertical.cpp new file mode 100644 index 0000000..d28ed7a --- /dev/null +++ b/src/openalpr/segmentation/histogramvertical.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015 OpenALPR Technology, Inc. + * Open source 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 . +*/ + +#include "histogramvertical.h" + +using namespace cv; +using namespace std; + +namespace alpr +{ + + HistogramVertical::HistogramVertical(Mat inputImage, Mat mask) + { + analyzeImage(inputImage, mask, true); + } + + + + + +} \ No newline at end of file diff --git a/src/openalpr/segmentation/histogramvertical.h b/src/openalpr/segmentation/histogramvertical.h new file mode 100644 index 0000000..e94ed10 --- /dev/null +++ b/src/openalpr/segmentation/histogramvertical.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015 OpenALPR Technology, Inc. + * Open source 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 . +*/ + +#ifndef OPENALPR_VERTICALHISTOGRAM_H +#define OPENALPR_VERTICALHISTOGRAM_H + +#include "opencv2/imgproc/imgproc.hpp" +#include "histogram.h" +#include "utility.h" + +namespace alpr +{ + + + + class HistogramVertical : public Histogram + { + + public: + HistogramVertical(cv::Mat inputImage, cv::Mat mask); + + + }; + +} + +#endif // OPENALPR_VERTICALHISTOGRAM_H diff --git a/src/openalpr/segmentation/verticalhistogram.cpp b/src/openalpr/segmentation/verticalhistogram.cpp deleted file mode 100644 index ee707ae..0000000 --- a/src/openalpr/segmentation/verticalhistogram.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (c) 2015 OpenALPR Technology, Inc. - * Open source 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 . -*/ - -#include "verticalhistogram.h" - -using namespace cv; -using namespace std; - -namespace alpr -{ - - VerticalHistogram::VerticalHistogram(Mat inputImage, Mat mask) - { - analyzeImage(inputImage, mask); - } - - VerticalHistogram::~VerticalHistogram() - { - histoImg.release(); - colHeights.clear(); - } - - void VerticalHistogram::analyzeImage(Mat inputImage, Mat mask) - { - - vector data; - - highestPeak = 0; - lowestValley = inputImage.rows; - - histoImg = Mat::zeros(inputImage.size(), CV_8U); - - int columnCount; - - for (int col = 0; col < inputImage.cols; col++) - { - columnCount = 0; - - for (int row = 0; row < inputImage.rows; row++) - { - if (inputImage.at(row, col) > 0 && mask.at(row, col) > 0) - columnCount++; - } - - this->colHeights.push_back(columnCount); - - if (columnCount < lowestValley) - lowestValley = columnCount; - if (columnCount > highestPeak) - highestPeak = columnCount; - - data.push_back(columnCount); - for (; columnCount > 0; columnCount--) - histoImg.at(inputImage.rows - columnCount, col) = 255; - } - -// int MAX_PEAK=200; -// int emi_peaks[MAX_PEAK]; -// int absorp_peaks[MAX_PEAK]; -// int emi_count = 0; -// int absorp_count = 0; -// double delta = highestPeak * (1.0 / 3.0); -// int emission_first = 0; -// -// detect_peak(data.data(), data.size(), emi_peaks, &emi_count, MAX_PEAK, -// absorp_peaks, &absorp_count, MAX_PEAK, -// delta, emission_first); -// -// Mat colorDebugImg; -// cvtColor(histoImg, colorDebugImg, CV_GRAY2BGR ); -// -// for (int i = 0; i < emi_count; i++) -// { -// cout << "EMI PEAK: " << emi_peaks[i] << " : " << data[emi_peaks[i]] << endl; -// circle(colorDebugImg, Point(emi_peaks[i], histoImg.rows - data[emi_peaks[i]]), 2, Scalar(0,255,0), -1); -// } -// -// for (int i = 0; i < absorp_count; i++) -// { -// cout << "ABSORB PEAK: " << absorp_peaks[i] << " : " << data[absorp_peaks[i]] << endl; -// circle(colorDebugImg, Point(absorp_peaks[i], histoImg.rows - data[absorp_peaks[i]]), 2, Scalar(0,0,255), -1); -// } -// -// -// drawAndWait(&colorDebugImg); - } - - int VerticalHistogram::getLocalMinimum(int leftX, int rightX) - { - int minimum = histoImg.rows + 1; - int lowestX = leftX; - - for (int i = leftX; i <= rightX; i++) - { - if (colHeights[i] < minimum) - { - lowestX = i; - minimum = colHeights[i]; - } - } - - return lowestX; - } - - int VerticalHistogram::getLocalMaximum(int leftX, int rightX) - { - int maximum = -1; - int highestX = leftX; - - for (int i = leftX; i <= rightX; i++) - { - if (colHeights[i] > maximum) - { - highestX = i; - maximum = colHeights[i]; - } - } - - return highestX; - } - - int VerticalHistogram::getHeightAt(int x) - { - return colHeights[x]; - } - - int VerticalHistogram::detect_peak( - const double* data, /* the data */ - int data_count, /* row count of data */ - int* emi_peaks, /* emission peaks will be put here */ - int* num_emi_peaks, /* number of emission peaks found */ - int max_emi_peaks, /* maximum number of emission peaks */ - int* absop_peaks, /* absorption peaks will be put here */ - int* num_absop_peaks, /* number of absorption peaks found */ - int max_absop_peaks, /* maximum number of absorption peaks - */ - double delta, /* delta used for distinguishing peaks */ - int emi_first /* should we search emission peak first of - absorption peak first? */ - ) -{ - int i; - double mx; - double mn; - int mx_pos = 0; - int mn_pos = 0; - int is_detecting_emi = emi_first; - - - mx = data[0]; - mn = data[0]; - - *num_emi_peaks = 0; - *num_absop_peaks = 0; - - for(i = 1; i < data_count; ++i) - { - if(data[i] > mx) - { - mx_pos = i; - mx = data[i]; - } - if(data[i] < mn) - { - mn_pos = i; - mn = data[i]; - } - - if(is_detecting_emi && - data[i] < mx - delta) - { - if(*num_emi_peaks >= max_emi_peaks) /* not enough spaces */ - return 1; - - emi_peaks[*num_emi_peaks] = mx_pos; - ++ (*num_emi_peaks); - - is_detecting_emi = 0; - - i = mx_pos - 1; - - mn = data[mx_pos]; - mn_pos = mx_pos; - } - else if((!is_detecting_emi) && - data[i] > mn + delta) - { - if(*num_absop_peaks >= max_absop_peaks) - return 2; - - absop_peaks[*num_absop_peaks] = mn_pos; - ++ (*num_absop_peaks); - - is_detecting_emi = 1; - - i = mn_pos - 1; - - mx = data[mn_pos]; - mx_pos = mn_pos; - } - } - - return 0; -} - -} \ No newline at end of file diff --git a/src/openalpr/segmentation/verticalhistogram.h b/src/openalpr/segmentation/verticalhistogram.h deleted file mode 100644 index d27da7a..0000000 --- a/src/openalpr/segmentation/verticalhistogram.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2015 OpenALPR Technology, Inc. - * Open source 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 . -*/ - -#ifndef OPENALPR_VERTICALHISTOGRAM_H -#define OPENALPR_VERTICALHISTOGRAM_H - -#include "opencv2/imgproc/imgproc.hpp" -#include "utility.h" - -namespace alpr -{ - - struct Valley - { - int startIndex; - int endIndex; - int width; - int pixelsWithin; - }; - - enum HistogramDirection { RISING, FALLING, FLAT }; - - class VerticalHistogram - { - - public: - VerticalHistogram(cv::Mat inputImage, cv::Mat mask); - virtual ~VerticalHistogram(); - - cv::Mat histoImg; - - // Returns the lowest X position between two points. - int getLocalMinimum(int leftX, int rightX); - // Returns the highest X position between two points. - int getLocalMaximum(int leftX, int rightX); - - int getHeightAt(int x); - - private: - std::vector colHeights; - int highestPeak; - int lowestValley; - std::vector valleys; - - void analyzeImage(cv::Mat inputImage, cv::Mat mask); - - int detect_peak(const double* data, int data_count, int* emi_peaks, - int* num_emi_peaks, int max_emi_peaks, int* absop_peaks, - int* num_absop_peaks, int max_absop_peaks, double delta, - int emi_first ); - }; - -} - -#endif // OPENALPR_VERTICALHISTOGRAM_H