我正在 openCV 中编写一些代码,并希望找到一个非常大的矩阵数组(单通道灰度,浮点)的中值。
我尝试了几种方法,例如对数组进行排序(使用 std::sort)和选择中间条目,但与 matlab 中的中值函数相比,它非常慢。准确地说,在 matlab 中需要 0.25 秒的时间在 openCV 中需要超过 19 秒。
我的输入图像最初是一个 12 位灰度图像,尺寸为 3840x2748(~10.5 兆像素),转换为浮点数(CV_32FC1),其中所有值现在都映射到范围 [0,1] 并且在代码中的某个点我通过调用请求中值:
double myMedianValue = medianMat(Input);
函数 medianMat 是:
double medianMat(cv::Mat Input){
Input = Input.reshape(0,1); // spread Input Mat to single row
std::vector<double> vecFromMat;
Input.copyTo(vecFromMat); // Copy Input Mat to vector vecFromMat
std::sort( vecFromMat.begin(), vecFromMat.end() ); // sort vecFromMat
if (vecFromMat.size()%2==0) {return (vecFromMat[vecFromMat.size()/2-1]+vecFromMat[vecFromMat.size()/2])/2;} // in case of even-numbered matrix
return vecFromMat[(vecFromMat.size()-1)/2]; // odd-number of elements in matrix
}
我对函数 medinaMat 本身以及各个部分进行了计时 - 正如预期的那样,瓶颈在于:
std::sort( vecFromMat.begin(), vecFromMat.end() ); // sort vecFromMat
这里有人有有效的解决方案吗?
谢谢!
编辑 我尝试使用 Adi Shavit 的答案中给出的 std::nth_element 。
函数 medianMat 现在读取为:
double medianMat(cv::Mat Input){
Input = Input.reshape(0,1); // spread Input Mat to single row
std::vector<double> vecFromMat;
Input.copyTo(vecFromMat); // Copy Input Mat to vector vecFromMat
std::nth_element(vecFromMat.begin(), vecFromMat.begin() + vecFromMat.size() / 2, vecFromMat.end());
return vecFromMat[vecFromMat.size() / 2];
}
运行时间已从超过 19 秒降低到 3.5 秒。在使用中值函数的 Matlab 中,这仍然远不及 0.25 秒……
原文由 CV_User 发布,翻译遵循 CC BY-SA 4.0 许可协议
好的。
我实际上在发布问题之前尝试过这个,并且由于一些愚蠢的错误我取消了它作为解决方案的资格……无论如何它是:
我基本上用 2^12 = 4096 个 bin 为我的原始输入创建值的直方图,计算 CDF 并将其标准化,使其从 0 映射到 1,并找到 CDF 中等于或大于 0.5 的最小索引。然后我将此索引除以 12^2,从而找到请求的中值。现在它在 0.11 秒内运行(这是在没有大量优化的调试模式下),不到 Matlab 所需时间的一半。
这是函数(在我的例子中,nVals = 4096 对应于 12 位值):