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