【问题标题】:Find average w/o extreme values查找没有极值的平均值
【发布时间】:2016-02-11 12:25:02
【问题描述】:

我知道有一个选项可以在 OpenCV 中找到 Mat 的平均值:

cv::mean(mat);

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

【问题讨论】:

    标签: c++ opencv matrix statistics average


    【解决方案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%。

    【讨论】:

    • cv::mean 也在零值中考虑。所以平均值会很低。
    • @Bschs 这就是为什么最后一行更正了规范化
    • @tobi303 这不适用于一般矩阵,其中值可以小于 0。但是,这种方法适用于图像,因为您有 tolerance,它可以'不是固定的,而是为每个图像重新计算。所以这不能回答这个问题,因为你错过了如何计算阈值。其余的都可以,Bschs错了:D
    • @Miki 我同意你关于容忍度的观点。实际上我误解了答案,并认为应该跳过与平均值相差一定比例的异常值(实际上仍然不是我的代码正在做的事情,但这可以很容易地修复)。另一方面,我不明白,为什么否定条目会是一个问题
    • @tobi303 我的错,我忽略了它。只是 tolerance 的东西:D
    【解决方案2】:

    不,没有 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;
    }
    

    【讨论】:

      【解决方案3】:

      我会简单地对 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.
      

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

      【讨论】:

        猜你喜欢
        • 2021-12-22
        • 2020-08-10
        • 2018-07-12
        • 1970-01-01
        • 2020-03-16
        • 2012-02-20
        • 2014-05-16
        相关资源
        最近更新 更多