找到没有极值的平均值

时间:2016-02-11 12:25:03

标签: c++ opencv matrix statistics average

我知道可以在OpenCV中找到Mat的平均值:

cv::mean(mat);

我想知道在OpenCV中是否还有一个选项可以找到没有极值的平均值(例如,只有10%到90%之间的值)。

3 个答案:

答案 0 :(得分:1)

我不知道OpenCV,但我怀疑它有一个准备好使用的功能。但是,一个天真的实现可能如下所示:

double m = cv::mean(mat);
Mat temp = mat;
... set all elements in temp to 0, where abs(temp[i][j] - m) > tolerance
... and count those elements in count
int N = mat.total(); // total number of elements
m = cv::sum(temp) / (N-count)
编辑:实际上这不是问题所要求的。但是,如果可以假设值的高斯分布,则可以基于标准偏差(必须计算)来估计tolerance的值,以排除数据的上/下10%。

答案 1 :(得分:0)

不,没有OpenCV功能可以做到这一点。但是,您可以实现自己的。

最棘手的部分是计算与百分比相对应的值。这可以容易地实现计算图像的累积直方图。

但是,为了使方法更通用,您无法知道矩阵中的哪些值,因此您需要依赖maps

请注意,如果您只使用CV_8U张图片,则可以优化知道您最多可以拥有256个不同的值。要实现此目的,您可以提示here

所以这是一种可能的实现,适用于Mat最多4个通道(cv::mean),并且不知道先验可能的不同值的数量。您可以检查它正确执行的示例矩阵初始化中的注释/退出部分:

#include <opencv2/opencv.hpp>
#include <vector>
#include <numeric>
#include <map>
#include <iostream>
using namespace cv;
using namespace std;

double robustMeanC1(const Mat1d& src, Vec2d bounds)
{
    // Compute histogram (with no predefined range)
    map<double, int> hist;
    for (int r = 0; r < src.rows; ++r)
    {
        for (int c = 0; c < src.cols; ++c)
        {
            double key = src(r,c);
            if (hist.count(key) == 0) {
                hist.insert(make_pair(key, 1));
            }
            else {
                hist[key]++;
            }
        }
    }

    // Get vectors from map
    vector<double> vals;
    vector<int> sums;
    vals.reserve(hist.size());
    sums.reserve(hist.size());
    for (auto kv : hist)
    {
        vals.push_back(kv.first);
        sums.push_back(kv.second);
    }

    // Compute cumulative histogram
    vector<int> cumhist(sums);
    for (int i=1; i<sums.size(); ++i)
    {
        cumhist[i] = cumhist[i - 1] + sums[i];
    }

    // Compute bounds
    int total = src.rows * src.cols;
    double low_bound = (total * bounds[0]) / 100.0;
    double upp_bound = (total * bounds[1]) / 100.0;
    int low_index = distance(cumhist.begin(), upper_bound(cumhist.begin(), cumhist.end(), low_bound));
    int upp_index = distance(cumhist.begin(), upper_bound(cumhist.begin(), cumhist.end(), upp_bound));

    if (low_index == upp_index) {upp_index++;}

    // Compute mean
    double mean = 0.0;
    int count = 0;
    for (int i = low_index; i < upp_index; ++i)
    {
        mean += vals[i] * sums[i];
        count += sums[i];
    }
    mean /= count;

    return mean;
}

Scalar robustMean(const Mat& src, Vec2d bounds) 
{
    Mat m;
    src.convertTo(m, CV_64F);

    Scalar res(0.0, 0.0, 0.0, 0.0);

    if (m.channels() == 1)
    {
        res[0] = robustMeanC1(m, bounds);
    } 
    else
    {
        vector<Mat1d> planes;
        split(m, planes);

        if (planes.size() > 4)
        {
            // Error, at most 4 channels
            return Scalar(0,0,0,0);
        }

        for (int i = 0; i < planes.size(); ++i)
        {
            res[i] = robustMeanC1(planes[i], bounds);
        }
    }
    return res;
}



int main()
{
    Mat1d m(10,10, 5.f);
    m(Range(0,1), Range::all()) = 2.0;
    //m(Range(1, 2), Range::all()) = 80.0;
    //randu(m, Scalar(0), Scalar(1));

    //Mat3b m = imread("path_to_image");

    Scalar rs = robustMean(m, Vec2d(10, 90));
    Scalar s = mean(m);

    cout << "Robust Mean: " << rs << endl;
    cout << "       Mean: " << s << endl;

    return 0;
}

答案 2 :(得分:0)

我只是对Mat元素进行排序,并采用截断向量的平均值

#include <algorithm>
#include <vector>
// c in [0,1] the portion of middvalues added to the mean
template<class _T> _T avg( std::vector< _T > & vec, double c )
{
    if ( c < 0.0 )
        c = 0.0;
    else if ( c > 1.0 )
        c = 1.0;
    const size_t len = (size_t)( c * (double)vec.size() );
    if ( len == 0 )
        return 0.0;
    std::vector< _T >::iterator beg = vec.begin();
    std::vector< _T >::iterator end = vec.end();
    if ( len < vec.size() )
    {    
        beg += ( vec.size() - len )/2;
        end = beg + len;
        std::nth_element( vec.begin(), beg, vec.end() );
        std::nth_element( beg, end, vec.end() );
    }
    double sum = 0.0, d = 0.0;
    for ( std::vector<_T>::iterator it = beg; it!=end; ++it, d+=1.0 )
        sum += *it;
    return sum/d;
}
// fill the vector and compute for each channel separately.

为了简单起见,这里尾部和头部相同。