count number of peaks in histogram

I have written a code to plot the histogram and i need to count the number of peaks in the histogram plot. Since i am new to it i was facing problem in finding the peaks, so please help me in solving this problem

#include "stdio.h"
#include "iostream"

#include "opencv\cxcore.h"
#include "opencv\highgui.h"
#include "opencv\cv.h"

#include "opencv2\core\core.hpp"
#include "opencv2\highgui\highgui.hpp"
#include "opencv2\imgproc\imgproc.hpp"

using namespace cv;
using namespace std;

//int main( int argc, char** argv )
//{
//  Mat src,hsv;

int main( int argc, char** argv )
{
Mat src, dst,src1;

cvtColor(src,src1,CV_RGB2GRAY);

if( !src.data )
{ return -1; }

/// Separate the image in 3 places ( B, G and R )
//vector<Mat> bgr_planes;
//split( src, bgr_planes );

/// Establish the number of bins
int histSize = 256;

/// Set the ranges ( for B,G,R) )
float range[] = { 0, 256 } ;
const float* histRange = { range };

bool uniform = true; bool accumulate = false;

Mat b_hist;

/// Compute the histograms:

calcHist( &src1, 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate );

// Draw the histograms for B, G and R
int hist_w = 512; int hist_h = 400;
int bin_w = cvRound( (double) hist_w/histSize );

Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) );

/// Normalize the result to [ 0, histImage.rows ]
normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );

/// Draw the histogram
for( int i = 1; i < histSize; i++ )
{
line( histImage, Point( bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1)) ) ,
Point( bin_w*(i), hist_h - cvRound(b_hist.at<float>(i)) ),
Scalar( 255, 0, 0), 2, 8, 0  );
}

/// Display
namedWindow("Histgram", CV_WINDOW_AUTOSIZE );
imshow("Histgram", histImage );
waitKey(0);
}

edit retag close merge delete

Sort by ยป oldest newest most voted

search for finding local maximum in a matrix. With a fast search I found this one. I think that it is what you are looking for.

edit:

Since the code in the above link was a bit outdated, I transformed it a bit including also the drawing functions.

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int drawPeaks(Mat &histImage, vector<int>& peaks, int hist_size = 256, Scalar color = Scalar(0, 0, 255))
{
int bin_w = cvRound( (double) histImage.cols / hist_size );
for(size_t i = 0; i < peaks.size(); i++)
line(histImage, Point(bin_w * peaks[i], histImage.rows), Point(bin_w * peaks[i], 0), color);

imshow("Peaks", histImage);
return EXIT_SUCCESS;
}

Mat drawHistogram(Mat &hist, int hist_h = 400, int hist_w = 1024, int hist_size = 256, Scalar color = Scalar(255, 255, 255), int type = 2)
{
int bin_w = cvRound( (double) hist_w/hist_size );

Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) );

/// Normalize the result to [ 0, histImage.rows ]
normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );

switch (type) {
case 1:
for(int i = 0; i < histImage.cols; i++)
{
const unsigned x = i;
const unsigned y = hist_h;

line(histImage, Point(bin_w * x, y),
Point(bin_w * x, y - cvRound(hist.at<float>(i))),
color);
}

break;
case 2:
for( int i = 1; i < hist_size; ++i)
{
Point pt1 = Point(bin_w * (i-1), hist_h);
Point pt2 = Point(bin_w * i, hist_h);
Point pt3 = Point(bin_w * i, hist_h - cvRound(hist.at<float>(i)));
Point pt4 = Point(bin_w * (i-1), hist_h - cvRound(hist.at<float>(i-1)));
Point pts[] = {pt1, pt2, pt3, pt4, pt1};

fillConvexPoly(histImage, pts, 5, color);
}
break;
default:
for( int i = 1; i < hist_size; ++i)
{
line( histImage, Point( bin_w * (i-1), hist_h - cvRound(hist.at<float>(i-1))) ,
Point( bin_w * (i), hist_h - cvRound(hist.at<float>(i))),
color, 1, 8, 0);
}

break;
}

imshow("Histogram", histImage);

return histImage;
}

struct Length
{
int pos1;
int pos2;
int size()
{
return pos2 - pos1 +1;
}
};

struct PeakInfo
{
int pos;
int left_size;
int right_size;
float value;
};

PeakInfo peakInfo(int pos, int left_size, int right_size, float value)
{
PeakInfo output;
output.pos = pos;
output.left_size = left_size;
output.right_size = right_size;
output.value = value;
return output;
}

vector<PeakInfo> findPeaks(InputArray _src, int window_size)
{
Mat src = _src.getMat();

Mat slope_mat = src.clone();

// Transform initial matrix into 1channel, and 1 row matrix
Mat src2 = src.reshape(1, 1);

int size = window_size / 2;

Length up_hill, down_hill;
vector<PeakInfo> output;

int pre_state = 0;
int i = size;

while(i < src2.cols - size)
{
float cur_state = src2.at<float>(i + size) - src2.at<float>(i - size);

if(cur_state > 0)
cur_state = 2;
else if(cur_state < 0)
cur_state = 1;
else cur_state = 0;

// In case you want to check how the slope looks like
slope_mat.at<float>(i) = cur_state;

if(pre_state == 0 && cur_state == 2)
up_hill.pos1 = i;
else if(pre_state == 2 && cur_state == 1)
{
up_hill.pos2 = i - 1;
down_hill.pos1 = i;
}

if((pre_state == 1 && cur_state == 2) || (pre_state == 1 && cur_state == 0))
{
down_hill.pos2 = i - 1;
int max_pos = up_hill.pos2;
if(src2.at<float>(up_hill.pos2) < src2.at<float>(down_hill.pos1))
max_pos = down_hill.pos1;

PeakInfo peak_info = peakInfo(max_pos, up_hill.size(), down_hill.size(), src2.at<float>(max_pos));

output ...
more

The LocalMaximum() have to be written like the getLocalMaximum()? thanks

( 2015-12-07 05:23:22 -0500 )edit

Could you please tell me precisely whats going on in findPeaks function?

( 2018-11-16 16:06:00 -0500 )edit

for the fun of it, you could try an analytical solution, like:

 Mat dx;  Sobel(hist,dx,CV_32F,1,0,1); // zeros of the 1st derivative in x are are extremums


but you'd still have to iterate it to check the neighbours for slope, so ...

let's keep it simple: a histogram is a 1d array, and for a a peak value , both neighbour elements have to be <= center.

for (int i=1; i<hist.total()-1; ++i)
{
float left  = hist.at<float>(i-1);
float cent  = hist.at<float>(i);
float right = hist.at<float>(i+1);
// we have to set a boundary condition for 'plateaus',
// so just decide to have the 'cutoff' on the left side
if ( left < cent  && right <= cent )
{
// peak !
}
// bonus track, get the valleys, too !
if ( left > cent  && right >= cent )
{
// valley !
}
}


you will still have to think of a way to handle the 1st and last element (which are lacking the neighbour).

more

in the case of 1st and last element I guess you just consider that the left and right values are zero, respectively.

( 2015-02-07 15:24:51 -0500 )edit

not nessecarily, unfortunately,

|            ___              |
|\          /      \       ___|
|  \      /         \__/      |
|    \__/                     |

( 2015-02-07 15:38:02 -0500 )edit