1

I need to search array (MatND object) across the whole array to get where's 5% of MAX value. minMaxLoc function returns me MAX value but I don't know hot to search it by my self.

Any ideas?

1 Answer 1

3

If it is an uchar Mat

/**
 * @param : input image
 * @hist  : histogram
 * @nmin  : total minimum pixels number
 * @nmax  : total maximum pixels number
 * @channel : channel number
 *
 * ex : images with 1000 pixels, 50 equal to 5% of it
 */
std::pair<size_t, size_t> get_quantile_uchar(cv::Mat &input, cv::MatND &hist, size_t nmin, size_t nmax, int channel)
{
    int const hist_size = std::numeric_limits<uchar>::max() + 1;
    float const hranges[2] = {0, 255};
    float const *ranges[] = {hranges};

    //compute and cumulate the histogram
    cv::calcHist(&input, 1, &channel, cv::Mat(), hist, 1, &hist_size, ranges);
    auto *hist_ptr = hist.ptr<float>(0);
    for(size_t i = 1; i != hist_size; ++i){
        hist_ptr[i] += hist_ptr[i - 1];
    }

    // get the new min/max
    std::pair<size_t, size_t> min_max(0, hist_size - 1);
    while(min_max.first != (hist_size - 1) && hist_ptr[min_max.first] <= nmin){
        ++min_max.first; // the corresponding histogram value is the current cell position
    }

    while(min_max.second > 0 && hist_ptr[min_max.second] > nmax){
        --min_max.second; // the corresponding histogram value is the current cell position
    }

    if (min_max.second < hist_size - 2)
        ++min_max.second;

    return min_max;
}

Example, if there are an Mat(100 * 100) with value within 0~255, you could measure the top 5% percentile and lowest 3% percentile like this

auto const result = get_quantile(input, hist, input.total * 0.03, input.total * 0.95, 0); 

if it is not an uchar Mat, then you can sort the channel you want to measure first

/**
* @brief generic algorithm for other channel types except of uchar
* @param input   the input image
* @param output  the output image
* @param smin    total number of minimum pixels
* @param smax    total number maximum pixels
* @param channel the channel used to compute the histogram
*
* This algorithm only support uchar channel and float channel by now
*/
template<typename T>
std::pair<T, T> get_quantile(cv::Mat &input, size_t smin, size_t smax, int channel)
{
    std::vector<float> temp_input = copy_to_one_dim_array_ch<float>(input, channel);
    std::sort(std::begin(temp_input), std::end(temp_input));

    return std::pair<T, T>(temp_input[smin], temp_input[temp_input.size() - 1 - smax]);
}

The next problem is how to implement the function copy_to_one_dim_array_ch

/*
 * experimental version for cv::Mat, try to alleviate the problem
 * of code bloat.User should make sure the space of begin point to
 * have enough of spaces.
 */
template<typename T, typename InputIter>
void copy_to_one_dim_array_ch(cv::Mat const &src, InputIter begin, int channel)
{
    int const channel_number = src.channels();
    if(channel_number <= channel || channel < 0){
        throw std::out_of_range("channel value is invalid\n" + std::string(__FUNCTION__) +
                                "\n" + std::string(__FILE__));
    }

    for(int row = 0; row != src.rows; ++row){
        auto ptr = src.ptr<T>(row) + channel;
        for(int col = 0; col != src.cols; ++col){
            *begin = *ptr;
            ++begin;
            ptr += channel_number;
        }
    }
}

template<typename T>
std::vector<T> const copy_to_one_dim_array_ch(cv::Mat const &src, int channel)
{
    std::vector<T> result(src.total());
    copy_to_one_dim_array_ch<T>(src, std::begin(result), channel);

    return result;
}

Some features need c++11 support, and the function copy_to_one_dim_array_ch do not support nonbyte image

If you want to make it become easier to use, you could 1 : wrap those function in a class.
2 : apply full specialization on uchar Mat 3 : wrap the class in a function

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.