# 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

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

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.

not nessecarily, unfortunately,

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


Official site

GitHub

Wiki

Documentation