You can smooth the histogram to catch only major peaks
GaussianBlur(hist, histSmoothed, Size(9,9), 0, 0, BORDER_REPLICATE);
This operation removes noise and small variation over histogram... as is expected by GaussianBlur
.
You can also reduce the num of bins to achieve similar result.
You can play with the code below to analyse your histogram. Here is my result smoothSize=9
]
All relevant peaks are kept according to smooth size. You can reduce smalls localMax increasing smoothSize=17
or reduce the num of bins... this case uses 16 bins
BTW, to use your algorithm for peaks detection you have to be sure that the histogram is bimodal. Otherwise you have to use a more complicated peak detection, for example you should measure the power of the peak or at least its width. Use tag peaks on this site to locate examples like this
This is the code for smoothing numBins and plot.... enjoy !
#define CL_RED Scalar(0,0,255)
#define CL_GREEN Scalar(0,255,0)
#define CL_YELLOW Scalar(0,255,255 )
int smoothSize = 7;
int numBins = 8;
int borderType = BORDER_REPLICATE;
std::string winName = "Histograms";
Mat grayImg;
//HELPER FUNCTIONS
struct histSerie {
Mat hist;
std::string Title;
Scalar lineColor;
int lineWidth;
bool drawLocalMax;
histSerie(std::string _Title = "", Mat _hist = Mat(),
Scalar _lineColor = Scalar(255, 0, 0), bool _drawLocalMax = false, int _lineWidth = 1) :
Title(_Title), lineColor(_lineColor), lineWidth(_lineWidth),
drawLocalMax(_drawLocalMax) {_hist.copyTo(hist); }
};
void PlotHistSeries(const vector<histSerie> &histSeries, Mat &plotResult,
Scalar bgColor = Scalar(0, 0, 0));
void onTrackBar(int, void*);
// MAIN
int main(int argc, char* argv[])
{
Mat src;
src = imread("stones.jpg");
if (!src.data) return -1;
cvtColor(src, grayImg, CV_BGR2GRAY);
imshow("Src image", grayImg);
namedWindow(winName);
createTrackbar("n.bins 2^", winName, &numBins, 8, onTrackBar);
createTrackbar("Smooth Size", winName, &smoothSize, 35, onTrackBar);
createTrackbar("Border", winName, &borderType, BORDER_REPLICATE, onTrackBar);
onTrackBar(0, 0);
waitKey(0);
return 0;
}
void onTrackBar(int, void*)
{
Mat hist, histSmoothed, plotResult;
vector<histSerie> histSeries;
//enable this if you want to draw over srcImage
//cvtColor(grayImg, plotResult, CV_GRAY2BGR);
//Create the histogram
float range[] = { 0, 256 };
const float* histRange = { range };
int histSize = pow(2, numBins);
calcHist(&grayImg, 1, 0, Mat(), hist, 1, &histSize, &histRange, true, false);
histSeries.push_back(histSerie("Histogram bins=" + to_string(histSize),
hist, CL_RED, false, 2));
//smooth the histogram
if ((smoothSize % 2) == 0) smoothSize += 1; //use odd value
Size sz(smoothSize, smoothSize);
GaussianBlur(hist, histSmoothed, sz, 0, 0, borderType);
histSeries.push_back(histSerie("Histogram Smoothed w=" + to_string(smoothSize),
histSmoothed, CL_YELLOW, true));
//Plot and show the result
PlotHistSeries(histSeries, plotResult);
imshow(winName, plotResult);
}
void PlotHistSeries(const vector<histSerie> &histSeries, Mat &plotResult, Scalar bgColor)
{
double maxOfMax = 0;
int histSize = 0;
for (int s = 0; s < histSeries.size(); s++) {
histSize = max(histSize, histSeries[s].hist.size().height);
double minVal, maxVal;
minMaxLoc(histSeries[s].hist, &minVal, &maxVal);
maxOfMax = max(maxOfMax, maxVal);
}
//create the plot image
if (plotResult.empty())
plotResult = Mat(256, 512, CV_8UC3, bgColor);
//calculate y scale
double yScale = (double)plotResult.size().height / maxOfMax;
double xScale = (double)plotResult.size().width / histSize;
// Draw all histograms
for (int s = 0; s < histSeries.size(); s++) {
Mat ...
(more)