8

Can anyone help me to find out the top 1% (or say top 100 pixels)brightest pixels with their locations of a gray image in opencv. because cvMinMaxLoc() gives only brightest pixel location.

Any help is greatly appreciated.

Jav_Rock
  • 22,059
  • 20
  • 123
  • 164
Dark Knight
  • 3,507
  • 8
  • 35
  • 44

5 Answers5

2

this is a simple yet unneficient/stupid way to do it:

for i=1:100
  get brightest pixel using cvMinMaxLoc 
  store location
  set it to a value of zero
end

if you don't mind about efficiency this should work.

you should also check cvInRangeS to find other pixels of similar values defining low and high thresholds.

dnul
  • 717
  • 6
  • 23
1

You need to calculate the brightness threshold from the histogram. Then you iterate through the pixels to get those positions that are bright enough to satisfy the threshold. The program below instead applies the threshold to the image and displays the result for demonstration purposes:

#!/usr/bin/env python3

import sys
import cv2
import matplotlib.pyplot as plt

if __name__ == '__main__':
    if len(sys.argv) != 2 or any(s in sys.argv for s in ['-h', '--help', '-?']):
        print('usage: {} <img>'.format(sys.argv[0]))
        exit()
    img = cv2.imread(sys.argv[1], cv2.IMREAD_GRAYSCALE)
    hi_percentage = 0.01 # we want we the hi_percentage brightest pixels
    # * histogram
    hist = cv2.calcHist([img], [0], None, [256], [0, 256]).flatten()
    # * find brightness threshold
    # here: highest thresh for including at least hi_percentage image pixels,
    #       maybe you want to modify it for lowest threshold with for including
    #       at most hi_percentage pixels
    total_count = img.shape[0] * img.shape[1]  # height * width
    target_count = hi_percentage * total_count # bright pixels we look for
    summed = 0
    for i in range(255, 0, -1):
        summed += int(hist[i])
        if target_count <= summed:
            hi_thresh = i
            break
    else:
        hi_thresh = 0
    # * apply threshold & display result for demonstration purposes:
    filtered_img = cv2.threshold(img, hi_thresh, 0, cv2.THRESH_TOZERO)[1]
    plt.subplot(121)
    plt.imshow(img, cmap='gray')
    plt.subplot(122)
    plt.imshow(filtered_img, cmap='gray')
    plt.axis('off')
    plt.tight_layout()
    plt.show()
Lars
  • 325
  • 4
  • 5
1

C++ version based upon some of the other ideas posted:

// filter the brightest n pixels from a grayscale img, return a new mat
cv::Mat filter_brightest( const cv::Mat& src, int n ) {

    CV_Assert( src.channels() == 1 );
    CV_Assert( src.type() == CV_8UC1 );

    cv::Mat result={};

    // simple histogram
    std::vector<int> histogram(256,0); 
    for(int i=0; i< int(src.rows*src.cols); ++i) 
        histogram[src.at<uchar>(i)]++;

    // find max threshold value (pixels from [0-max_threshold] will be removed)
    int max_threshold = (int)histogram.size() - 1;
    for ( ; max_threshold >= 0 && n > 0; --max_threshold ) {
        n -= histogram[max_threshold];
    }

    if ( max_threshold < 0 )  // nothing to do
        src.copyTo(result);
    else     
        cv::threshold(src, result, max_threshold, 0., cv::THRESH_TOZERO);

    return result;
}

Usage example: get top 1%

auto top1 = filter_brightest( img, int((img.rows*img.cols) * .01) );
Tom
  • 1,977
  • 1
  • 20
  • 19
0

Try using cvThreshold instead.

mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • @Mark:thresholding doesnt help,you need to find out brightest pixels and their location. – Dark Knight Sep 06 '10 at 04:40
  • How doesn't it help? Threshold it first to boost their intensity to max, and then use your cvMinMaxLoc to find where they are. – mpen Sep 06 '10 at 06:49
  • cvMinMaxLoc gives only one brightest pixel's location,hw can i find out other loc with same intensity? – Dark Knight Sep 06 '10 at 07:02
  • Oh.. my bad. I thought you meant that it gave all the pixels with the (same) brightest intensity. I don't know... what format are you hoping to get back? A list of x,y coordinates? Why not just use a binary threshold as I suggested, and then loop over the image however you please? Actually, if you're going to loop it, you can do the thresholding yourself at the same time. – mpen Sep 06 '10 at 07:06
  • yeah...finding (x,y)coordinates of all those brightest pixels...kind of sorting but with the locations... – Dark Knight Sep 06 '10 at 07:17
  • i'm pretty sure I just looped it in my project. – mpen Sep 06 '10 at 07:25
  • k..can you send me that part? ajith.srao@gmail.com – Dark Knight Sep 06 '10 at 07:32
  • do you really want me to send you a for loop? seriously? `for(int y=0;ythreshold){do_something_with_coords(x,y);}}}` – mpen Sep 06 '10 at 07:47
  • ha ha...:)i thought yo using some fn like minmaxLoc...anyway will see,ty – Dark Knight Sep 06 '10 at 08:02
0

Well the most logical way is to iterate the whole picture, then get the max and min value of the pixels. Then chose a threshold which will give you the desired percent(1% in your case). After that iterate again and save the i and j coordinates of each pixel above the given threshold. This way you'll iterate the matrix only two times instead of 100(or 1% of the pixels times) and choosing the brightest and deleting it.

OpenCV mats are multidimensional arrays. Gray image is two dimensional array with values from 0 to 255. You can iterate trough the matrix like this. for(int i=0;i < mat.height();i++) for(int j=0;j < mat.width();j++) mat[i][j];

Petar Velev
  • 2,305
  • 12
  • 24