Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

You can smooth your histogram to catch only 2 major peaks

cv::Mat hist,hist_smoothed;
int smooth_size=5; //use odd value
calcHist(&grayImg, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);
GaussianBlur(hist, hist_smoothed, Size(smooth_size, smooth_size), 0, 0, BORDER_CONSTANT);

than you can use your loop

int number=0;
int size=0;
for (int i=1; i<256-1;++i)  {
    if(hist[i]>hist[i-1] && hist[i]>hist[i+1]) { 
        cout << "Peak Index: " <<i << " Peak Value: " <<hist[i] <<endl;
    }
}

in case you can play with smooth_size for better filtering.

See my post here

PS: please use '0101' button when you type some code

You can smooth your histogram to catch only 2 major peaks

cv::Mat hist,hist_smoothed;
int smooth_size=5; //use odd value
calcHist(&grayImg, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);
GaussianBlur(hist, hist_smoothed, Size(smooth_size, smooth_size), 0, 0, BORDER_CONSTANT);

than you can use your loop

int number=0;
int size=0;
for (int i=1; i<256-1;++i)  {
    if(hist[i]>hist[i-1] if( hist_smoothed[i]> hist_smoothed[i-1] && hist[i]>hist[i+1])  hist_smoothed[i]> hist_smoothed[i+1]) { 
        cout << "Peak Index: " <<i << " Peak Value: " <<hist[i] <<endl;
    }
}

in NOTE: you should know that hist_smoothed[i] < hist[i] because of smoothing.

In case you can play with smooth_size for better filtering.

See also my post here

PS: please use '0101' button when you type some code

You can smooth your histogram to catch only 2 major peaks

cv::Mat hist,hist_smoothed;
int smooth_size=5; //use odd value
calcHist(&grayImg, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);
int smooth_size=5; //use odd value
GaussianBlur(hist, hist_smoothed, Size(smooth_size, smooth_size), 0, 0, BORDER_CONSTANT);

than you can use your loop

int number=0;
int size=0;
for (int i=1; i<256-1;++i)  {
    if( hist_smoothed[i]> hist_smoothed[i-1] &&  hist_smoothed[i]> hist_smoothed[i+1]) { 
        cout << "Peak Index: " <<i << " Peak Value: " <<hist[i] <<endl;
    }
}

NOTE: you should know that hist_smoothed[i] < hist[i] because of smoothing.

In case you can play with smooth_size for better filtering.

See also my post here

PS: please use '0101' '1010' button when you type some code

You can smooth your histogram the histogram to catch only 2 major peaks with:

cv::Mat hist,hist_smoothed;
GaussianBlur(hist, histSmoothed, Size(11,11), 0, 0, BORDER_REPLICATE);

This operation removes noise and small variation over histogram... as is expected by GaussianBlur. You can play with the code below to analyse your histogram and make blur on it. Here is my result:

image description

All relevant peaks are kept according to smoothing size. Look at this example

image description

BTW, to use your algorithm for peak 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 it's width. Use tag peaks on this site to locate examples like this

This is the code for smoothing and plot.... enjoy !

#define CL_RED  Scalar(0,0,255)
#define CL_BLU  Scalar(255,0,0)
#define CL_YELLOW  Scalar(0,255,255)

struct histSerie {
    Mat hist;
    std::string Title;
    Scalar lineColor;
    int lineWidth;
    histSerie(std::string _Title = "", Mat _hist = Mat(),
        Scalar _lineColor = Scalar(255, 0, 0), int _lineWidth = 2) :
        Title(_Title), lineColor(_lineColor), lineWidth(_lineWidth)
    {_hist.copyTo(hist);}
};

int smoothSize = 11;
int borderType = BORDER_REPLICATE;
std::string winName = "Histograms";
Mat grayImg;

//helper function
void PlotHistSeries(const vector<histSerie> &histSeries, Mat &plotResult, Scalar bgColor = Scalar(0, 0, 0));

void onTrackBar(int, void*)
{
    Mat hist, histSmoothed, plotResult;
    vector<histSerie> histSeries;
    //enable this is you want to draw over srcImage
    //cvtColor(grayImg, plotResult, CV_GRAY2BGR);

    // Create the histogram
    int histSize = 256;
    float range[] = { 0, histSize };
    const float* histRange = { range };
    calcHist(&grayImg, 1, 0, cv::Mat(), Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);
int smooth_size=5; true, false);
    histSeries.push_back(histSerie("Histogram", hist, CL_RED, 1));

    // smooth the histogram
    if ((smoothSize % 2) == 0)
        smoothSize += 1;  //use odd value
    Size sz(smoothSize, smoothSize);
    GaussianBlur(hist, hist_smoothed, Size(smooth_size, smooth_size), histSmoothed, sz, 0, 0, BORDER_CONSTANT);

than you can use your loop

borderType);

    histSeries.push_back(histSerie("Histogram Smoothed w="+to_string(smoothSize),
        histSmoothed, CL_YELLOW, 1));

    PlotHistSeries(histSeries, plotResult);
    imshow(winName, plotResult);
}

int main(int argc, char* argv[])
{
    Mat src;
    src = imread("../img/grayscale2.jpg");
    if (!src.data) return -1;
    cvtColor(src, grayImg, CV_BGR2GRAY);
    imshow("Src image", grayImg);
    namedWindow(winName);
    createTrackbar("Smooth Size", winName, &smoothSize, 23, onTrackBar);
    createTrackbar("Border", winName, &borderType, BORDER_REPLICATE, onTrackBar);
    onTrackBar(0, 0);

    waitKey(0);
    return 0;
}
void PlotHistSeries(const vector<histSerie> &histSeries, Mat &plotResult, Scalar bgColor)
{
    //create the plot image
    int hist_w = 600; int hist_h = 400;
    if (plotResult.empty())
        plotResult = Mat(hist_h, hist_w, CV_8UC3, bgColor);
    else {
        hist_w = plotResult.cols; int hist_h = plotResult.rows;
    }

    //calculate y scale
    double maxOfMax = 0;
    int histSize = 0;
    for (int i=1; i<256-1;++i)  s = 0; s < histSeries.size(); s++) {
    if( hist_smoothed[i]> hist_smoothed[i-1] &&  hist_smoothed[i]> hist_smoothed[i+1]) { 
        cout << "Peak Index: " <<i << " Peak Value: " <<hist[i] <<endl;
    histSize = max(histSize, histSeries[s].hist.size().height);
        double minVal, maxVal;
        minMaxLoc(histSeries[s].hist, &minVal, &maxVal);
        maxOfMax = max(maxOfMax, maxVal);
    }
    double yScale = hist_h / maxOfMax;
    // Draw the histograms
    int bin_w = cvRound((double)hist_w / histSize);
    for (int s = 0; s < histSeries.size(); s++) {
        Mat data;
        histSeries[s].hist.convertTo(data, CV_64F, yScale, 0);
        Point pt1, pt2;

        for (int i = 1; i < histSize; i++) {
            pt1 = Point(bin_w*(i - 1), cvRound(hist_h - data.at<double>(i - 1)));
            pt2 = Point(bin_w*(i), cvRound(hist_h - data.at<double>(i)));
            line(plotResult, pt1, pt2, histSeries[s].lineColor, histSeries[s].lineWidth, 8, 0);
        }
        //the legend
        pt1.x = 10; pt2.x = pt1.x + 20;
        pt1.y = pt2.y = 20 * (s + 1);
        line(plotResult, pt1, pt2, histSeries[s].lineColor, histSeries[s].lineWidth, 8, 0);
        pt2.x += 5;
        cv::putText(plotResult, histSeries[s].Title, pt2, 
                    CV_FONT_HERSHEY_PLAIN, 1, histSeries[s].lineColor);
    }
}

NOTE: you should know that hist_smoothed[i] < hist[i] because of smoothing.

In case you can play with smooth_size for better filtering.

See also my post here

PS: please use '1010' button when you type some code

You can smooth the histogram to catch only major peaks with:

GaussianBlur(hist, histSmoothed, Size(11,11), 0, 0, BORDER_REPLICATE);

This operation removes noise and small variation over histogram... as is expected by GaussianBlur. You can play with the code below to analyse your histogram and make blur on it. Here is my result:

image description

All relevant peaks are kept according to smoothing size. Look at this example

image description

BTW, to use your algorithm for peak 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 it's width. Use tag peaks on this site to locate examples like this

This is the code for smoothing and plot.... enjoy !

#define CL_RED  Scalar(0,0,255)
#define CL_BLU  Scalar(255,0,0)
#define CL_YELLOW  Scalar(0,255,255)

struct histSerie {
    Mat hist;
    std::string Title;
    Scalar lineColor;
    int lineWidth;
    histSerie(std::string _Title = "", Mat _hist = Mat(),
        Scalar _lineColor = Scalar(255, 0, 0), int _lineWidth = 2) :
        Title(_Title), lineColor(_lineColor), lineWidth(_lineWidth)
    {_hist.copyTo(hist);}
};

int smoothSize = 11;
int borderType = BORDER_REPLICATE;
std::string winName = "Histograms";
Mat grayImg;

//helper function
void PlotHistSeries(const vector<histSerie> &histSeries, Mat &plotResult, Scalar bgColor = Scalar(0, 0, 0));

void onTrackBar(int, void*)
{
    Mat hist, histSmoothed, plotResult;
    vector<histSerie> histSeries;
    //enable this is if you want to draw over srcImage
    //cvtColor(grayImg, plotResult, CV_GRAY2BGR);

    // Create the histogram
    int histSize = 256;
    float range[] = { 0, histSize };
    const float* histRange = { range };
    calcHist(&grayImg, 1, 0, Mat(), hist, 1, &histSize, &histRange, true, false);
    histSeries.push_back(histSerie("Histogram", hist, CL_RED, 1));

    // 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, 1));

    PlotHistSeries(histSeries, plotResult);
    imshow(winName, plotResult);
}

int main(int argc, char* argv[])
{
    Mat src;
    src = imread("../img/grayscale2.jpg");
imread("grayscale.jpg");
    if (!src.data) return -1;
    cvtColor(src, grayImg, CV_BGR2GRAY);
    imshow("Src image", grayImg);
    namedWindow(winName);
    createTrackbar("Smooth Size", winName, &smoothSize, 23, onTrackBar);
    createTrackbar("Border", winName, &borderType, BORDER_REPLICATE, onTrackBar);
    onTrackBar(0, 0);

    waitKey(0);
    return 0;
}
void PlotHistSeries(const vector<histSerie> &histSeries, Mat &plotResult, Scalar bgColor)
{
    //create the plot image
    int hist_w = 600; int hist_h = 400;
    if (plotResult.empty())
        plotResult = Mat(hist_h, hist_w, CV_8UC3, bgColor);
    else {
        hist_w = plotResult.cols; int hist_h = plotResult.rows;
    }

    //calculate y scale
    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);
    }
    double yScale = hist_h / maxOfMax;
    // Draw the histograms
    int bin_w = cvRound((double)hist_w / histSize);
    for (int s = 0; s < histSeries.size(); s++) {
        Mat data;
        histSeries[s].hist.convertTo(data, CV_64F, yScale, 0);
        Point pt1, pt2;

        for (int i = 1; i < histSize; i++) {
            pt1 = Point(bin_w*(i - 1), cvRound(hist_h - data.at<double>(i - 1)));
            pt2 = Point(bin_w*(i), cvRound(hist_h - data.at<double>(i)));
            line(plotResult, pt1, pt2, histSeries[s].lineColor, histSeries[s].lineWidth, 8, 0);
        }
        //the legend
        pt1.x = 10; pt2.x = pt1.x + 20;
        pt1.y = pt2.y = 20 * (s + 1);
        line(plotResult, pt1, pt2, histSeries[s].lineColor, histSeries[s].lineWidth, 8, 0);
        pt2.x += 5;
        cv::putText(plotResult, histSeries[s].Title, pt2, 
                    CV_FONT_HERSHEY_PLAIN, 1, histSeries[s].lineColor);
    }
}

You can smooth the histogram to catch only major peaks with:peaks

GaussianBlur(hist, histSmoothed, Size(11,11), 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 and make blur on it. histogram. Here is my result:result smoothSize=9

image descriptionimage description]

All relevant peaks are kept according to smoothing smooth size. Look at You can reduce smalls localMax increasing smoothSize=17 or reduce the num of bins... this examplecase uses 16 bins

image descriptionimage description

BTW, to use your algorithm for peak 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 it's 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_BLU  Scalar(255,0,0)
CL_GREEN Scalar(0,255,0)
#define CL_YELLOW  Scalar(0,255,255)

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 = 2) 1) :
        Title(_Title), lineColor(_lineColor), lineWidth(_lineWidth)
    {_hist.copyTo(hist);}
lineWidth(_lineWidth), 
        drawLocalMax(_drawLocalMax)  {_hist.copyTo(hist); }
};

int smoothSize = 11;
int borderType = BORDER_REPLICATE;
std::string winName = "Histograms";
Mat grayImg;

//helper function
void PlotHistSeries(const vector<histSerie> &histSeries, Mat &plotResult, &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 //Create the histogram
    int histSize = 256;
    float range[] = { 0, histSize 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", histSeries.push_back(histSerie("Histogram bins=" + to_string(histSize), 
            hist, CL_RED, 1));

    // smooth false, 2));

    //smooth the histogram
    if ((smoothSize % 2) == 0)
        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),
w=" + to_string(smoothSize),
        histSmoothed, CL_YELLOW, 1));

true));
    //Plot and show the result
    PlotHistSeries(histSeries, plotResult);
    imshow(winName, plotResult);
}

int main(int argc, char* argv[])
{
    Mat src;
    src = imread("grayscale.jpg");
    if (!src.data) return -1;
    cvtColor(src, grayImg, CV_BGR2GRAY);
    imshow("Src image", grayImg);
    namedWindow(winName);
    createTrackbar("Smooth Size", winName, &smoothSize, 23, onTrackBar);
    createTrackbar("Border", winName, &borderType, BORDER_REPLICATE, onTrackBar);
    onTrackBar(0, 0);

    waitKey(0);
    return 0;
}
void PlotHistSeries(const vector<histSerie> &histSeries, Mat &plotResult, Scalar bgColor)
{
    //create the plot image
    int hist_w = 600; int hist_h = 400;
    if (plotResult.empty())
        plotResult = Mat(hist_h, hist_w, CV_8UC3, bgColor);
    else {
        hist_w = plotResult.cols; int hist_h = plotResult.rows;
    }

    //calculate y scale
    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 = hist_h (double)plotResult.size().height / maxOfMax;
    double xScale = (double)plotResult.size().width / histSize;
    // Draw the all histograms
    int bin_w = cvRound((double)hist_w / histSize);
    for (int s = 0; s < histSeries.size(); s++) {
        Mat data;
        histSeries[s].hist.convertTo(data, CV_64F, yScale, 0);
data = histSeries[s].hist;
        Point pt1, pt2;

pt0, pt1;
        int x0, x1, y0, y1;
        float v0, v1, v2;
        // for each bins
        for (int i = 1; 0; i < histSize; i++) {
            v1 = data.at<float>(i);
            if (i == 0) v0 = v1;
            else v0 = data.at<float>(i-1);
            x0 = cvRound((i - 1) * xScale);
            x1 = cvRound(i * xScale);
            y0 = cvRound(plotResult.size().height - v0*yScale);
            y1 = cvRound(plotResult.size().height - v1*yScale);
            pt0 = Point(x0, y0);
            pt1 = Point(bin_w*(i Point(x1, y1);
            //draw localMax 1st
            if (histSeries[s].drawLocalMax)
            {
                // check max on border
                if (i < histSize - 1), cvRound(hist_h 1) v2 = data.at<float>(i + 1); 
                else v2 = v1 - data.at<double>(i 1;
                if (i == 0) v0 = v1 - 1)));
            pt2 = Point(bin_w*(i), cvRound(hist_h - data.at<double>(i)));
1;
                //if localmax
                if ((v1 > v0) && (v1 > v2))
                    circle(plotResult, pt1, 5, CL_GREEN, 2);
            }
            //hist segment
            line(plotResult, pt0, pt1, pt2, histSeries[s].lineColor, histSeries[s].lineWidth, 8, 0);
        }
        //the legend
        //legend
        pt0.x = 10; pt1.x = 10; pt2.x = pt1.x pt0.x + 20;
        pt0.y = pt1.y = pt2.y = 20 * (s + 1);
        line(plotResult, pt0, pt1, pt2, histSeries[s].lineColor, histSeries[s].lineWidth, 8, 0);
        pt2.x pt1.x += 5;
        cv::putText(plotResult, histSeries[s].Title, pt2, 
        pt1,
            CV_FONT_HERSHEY_PLAIN, 1, histSeries[s].lineColor);
    }
}