我知道可以在OpenCV中找到Mat
的平均值:
cv::mean(mat);
我想知道在OpenCV中是否还有一个选项可以找到没有极值的平均值(例如,只有10%到90%之间的值)。
答案 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.
为了简单起见,这里尾部和头部相同。