Ask Your Question
3

Find all peaks for Mat() in OpenCV C++

asked 2015-03-04 01:51:13 -0600

seidju gravatar image

updated 2015-10-27 01:08:15 -0600

I want to find all peaks for my image. I need it to divide my picture such way:

enter image description here

So, i already asked question, how to project all image to one of the axis, and now i need to find all maximums on this one-row image. Here's my part of the code:

void segment_plate (Mat input_image) {
    double minVal; 
    double maxVal; 
    Point minLoc; 
    Point maxLoc;
    Mat work_image = input_image;
    Mat output;
    //binarize image
    adaptiveThreshold(work_image,work_image, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, 10);  
    //project it to one axis
    reduce(work_image,output,0,CV_REDUCE_SUM,CV_32S);

    //find minimum and maximum falue for ALL image
    minMaxLoc(output, &minVal,&maxVal,&minLoc,&maxLoc);
    cout << "min val : " << minVal << endl;
    cout << "max val: " << maxVal << endl;

As you can see, i could find one maximum and one minimum for all picture, but i need to find local maximums. Thanks for any help!

EDIT Ok, so i made a mistake, i need to find peaks for this vector. I've used this code to find first peak:

int findPeakUtil(Mat arr, int low, int high, int n) {
// Find index of middle element
    int mid = low + (high - low)/2;  /* (low + high)/2 */

// Compare middle element with its neighbours (if neighbours exist)
    if ((mid == 0 || arr.at<int>(0,mid-1) <= arr.at<int>(0,mid)) &&
        (mid == n-1 || arr.at<int>(0,mid+1) <= arr.at<int>(0,mid)))
    return mid;

// If middle element is not peak and its left neighbor is greater than it
// then left half must have a peak element
    else if (mid > 0 && arr.at<int>(0,mid-1) > arr.at<int>(0,mid))
    return findPeakUtil(arr, low, (mid - 1), n);

// If middle element is not peak and its right neighbor is greater than it
// then right half must have a peak element
    else return findPeakUtil(arr, (mid + 1), high, n);
}

// A wrapper over recursive function findPeakUtil()
int findPeak(Mat arr, int n) {
   return findPeakUtil(arr, 0, n-1, n);
}

So now my code looks like:

void segment_plate (Mat input_image) {
    Mat work_image = input_image;
    Mat output;
    //binarize image
    adaptiveThreshold(work_image,work_image, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, 10);  
    //project it to one axis
    reduce(work_image,output,0,CV_REDUCE_SUM,CV_32S);
    int n = output.cols;
    printf("Index of a peak point is %d", findPeak(output, n));

But how can i find another peaks? The algorithm of peak finding i took from here.

edit retag flag offensive close merge delete

2 answers

Sort by ยป oldest newest most voted
7

answered 2015-03-04 05:07:50 -0600

theodore gravatar image

updated 2015-03-04 05:11:46 -0600

you can have a look here I guess this is what you are looking for. Plus look, to a newer implementation of mine below:

void non_maxima_suppression(const cv::Mat& src, cv::Mat& mask, const bool remove_plateaus)
{
    // find pixels that are equal to the local neighborhood not maximum (including 'plateaus')
    cv::dilate(src, mask, cv::Mat());
    cv::compare(src, mask, mask, cv::CMP_GE);

    // optionally filter out pixels that are equal to the local minimum ('plateaus')
    if (remove_plateaus) {
        cv::Mat non_plateau_mask;
        cv::erode(src, non_plateau_mask, cv::Mat());
        cv::compare(src, non_plateau_mask, non_plateau_mask, cv::CMP_GT);
        cv::bitwise_and(mask, non_plateau_mask, mask);
    }
}

// function that finds the peaks of a given hist image
void findHistPeaks(InputArray _src, OutputArray _idx, const float scale = 0.2, const Size& ksize = Size(9, 9), const bool remove_plateus = true)
{
    Mat hist = _src.getMat();
    // die if histogram image is not the correct type
    CV_Assert(hist.type() == CV_32F);

    // find the min and max values of the hist image
    double min_val, max_val;
    minMaxLoc(hist, &min_val, &max_val);

    Mat mask;
    GaussianBlur(hist, hist, ksize, 0); // smooth a bit in order to obtain better result
    non_maxima_suppression(hist, mask, remove_plateus); // extract local maxima

    vector<Point> maxima;   // output, locations of non-zero pixels
    cv::findNonZero(mask, maxima);

    for(vector<Point>::iterator it = maxima.begin(); it != maxima.end();)
    {
        Point pnt = *it;
        float pVal = hist.at<float>(/*pnt.x, */pnt.y - 1);
        float val = hist.at<float>(/*pnt.x, */pnt.y);
        float nVal = hist.at<float>(/*pnt.x, */pnt.y+1);
        // filter peaks
        if((val > max_val * scale))
            ++it;
        else
            it = maxima.erase(it);
    }

    Mat(maxima).copyTo(_idx);
}

Then you can call the function by either passing an std::vector<Point> or a cv::Mat as an output array. If you pass the scale attribute as 0 you will get every peak in the matrix, but if you don't want the small peaks then just leave it as it is or raise it to a higher value.

// vector<Point> peaks; or
Mat peaks;
findHistPeaks(b_hist, peaks);
edit flag offensive delete link more

Comments

Thanks a lot!

seidju gravatar imageseidju ( 2015-03-04 19:44:31 -0600 )edit
0

answered 2016-01-12 04:09:25 -0600

updated 2016-01-12 04:14:43 -0600

@theodore, could you take a look at my try at your code

I got the mask of the histogram

image description

But when I calculate and visualize the peaks, I get this result

image description

Any idea why I would get this odd locations as peaks?

Code I am using for it

Mat mask;
hist_copy.convertTo(hist_copy, CV_32FC1);

double min_val, max_val;
minMaxLoc(hist_copy, &min_val, &max_val);

non_maxima_suppression(hist_copy, mask, true); // extract local maxima

vector<Point> maxima;   // output, locations of non-zero pixels
cv::findNonZero(mask, maxima);

for(vector<Point>::iterator it = maxima.begin(); it != maxima.end();)
{
    Point pnt = *it;
    float pVal = hist_copy.at<float>(pnt.y - 1);
    float val = hist_copy.at<float>(pnt.y);
    float nVal = hist_copy.at<float>(pnt.y+1);
    // filter peaks
    if((val > max_val*0.2))
        ++it;
    else
        it = maxima.erase(it);
}

// Make a color hist
Mat hist_color(hist_copy.rows, hist_copy.cols, CV_32FC3);
Mat in_h[] = { hist_copy.clone(), hist_copy.clone(), hist_copy.clone() };
int from_to_h[] = { 0,0, 1,1, 2,2 };
mixChannels( in_h, 3, &hist_color, 1, from_to_h, 3 );
for(int i=0; i < maxima.size(); i++){
    drawMarker(hist_color, maxima[i], Scalar(0,0,255), MARKER_STAR, 10, 1);
}

imshow("peaks", hist_color); waitKey(0);
edit flag offensive delete link more

Comments

@StevenPuttemans I will check it later when I find some time ;-)

theodore gravatar imagetheodore ( 2016-01-12 04:33:18 -0600 )edit

Aha no instant idea? I am kind of running against a deadline here and stuck in the middle of it :D

StevenPuttemans gravatar imageStevenPuttemans ( 2016-01-12 04:34:24 -0600 )edit

A small explanation of that sketchy scale parameter could already give me some insight :D

StevenPuttemans gravatar imageStevenPuttemans ( 2016-01-12 04:35:38 -0600 )edit

did you try to use the findHistPeak() as it is? also I think a bit of smoothing would help, try different kernels. Try also to play a bit with the scale ;-).

theodore gravatar imagetheodore ( 2016-01-12 04:48:44 -0600 )edit

Hmm I am running a different approach now first :D will keep you posted on the result.

StevenPuttemans gravatar imageStevenPuttemans ( 2016-01-12 04:52:41 -0600 )edit

I think that GaussianBlur smooths peaks values. Despite of InputArray is defined to be const calling hist = _src.getMat();constructs a matrix header for the array (without copying data).... than 'GaussianBlur(hist, hist, ' will change (reduce) your source hist values.

pklab gravatar imagepklab ( 2016-01-13 12:15:17 -0600 )edit

Ok I'm OT because you are looking a reason for odd peaks. BTW your markers are all at same height ... should marker point to be Point(maxima[i].y, hist_not_smoothed.at<float>(maxima[i].y)) ?

pklab gravatar imagepklab ( 2016-01-13 13:41:51 -0600 )edit

@StevenPuttemans I am sorry but I did not have the time to check on your example. I am quite busy this period. In any case did you have any update.

theodore gravatar imagetheodore ( 2016-01-15 05:00:31 -0600 )edit

We took another approach at solving my situation, will edit it once I have the time :)

StevenPuttemans gravatar imageStevenPuttemans ( 2016-01-15 05:55:46 -0600 )edit

@StevenPuttemans do you have maybe in the meantime your solution for the peak finder problem? :)

aguila gravatar imageaguila ( 2016-08-10 09:43:14 -0600 )edit

Question Tools

3 followers

Stats

Asked: 2015-03-04 01:51:13 -0600

Seen: 23,409 times

Last updated: Jan 12 '16