如何计算平均梯度方向和平均梯度强度/幅值。

3
在OpenCV中,如何计算Mat中的平均梯度强度和平均梯度方向?我通过谷歌找到了下面的方法,但在继续下一步之前,我想确认我的做法是否正确。请问这样做正确吗?
Mat img = imread('foo.png', CV_8UC); // read image as grayscale single channel

// Calculate the mean intensity and the std deviation
// Any errors here or am I doing this correctly?
Scalar sMean, sStdDev;
meanStdDev(src, sMean, sStdDev);
double mean = sMean[0];
double stddev = sStdDev[0];


// Calculate the average gradient magnitude/strength across the image
// Any errors here or am I doing this correctly?
Mat dX, dY, magnitude;
Sobel(src, dX, CV_32F, 1, 0, 1);
Sobel(src, dY, CV_32F, 0, 1, 1);
magnitude(dX, dY, magnitude);

Scalar sMMean, sMStdDev;
meanStdDev(magnitude, sMMean, sMStdDev);
double magnitudeMean = sMMean[0];
double magnitudeStdDev = sMStdDev[0];


// Calculate the average gradient direction across the image
// Any errors here or am I doing this correctly?
Scalar avgHorizDir = mean(dX);
Scalar avgVertDir = mean(dY);
double avgDir = atan2(-avgVertDir[0], avgHorizDir[0]);


float blurriness = cv::videostab::calcBlurriness(src); // low values = sharper. High values = blurry
1个回答

3
从技术上讲,这些都是获取两个平均值的正确方法。
计算平均方向的方法使用了加权定向统计学,这意味着梯度较弱的像素对平均值的影响较小。
然而,对于大多数图像来说,这个平均方向并不是很有意义,因为所有方向都存在边缘,这些边缘会相互抵消。
如果您的图像只有一条边缘,则此方法效果很好。
如果您的图像中有包含相反方向边缘的直线,则此方法将无法奏效。在这种情况下,您需要平均双倍角度(平均方向)。一种明显的方法是将每个像素的方向作为角度计算,将它们翻倍,然后使用定向统计学进行平均(即将其转换回向量并对其求平均)。将角度翻倍会导致相反方向被映射到相同的值,从而平均不会抵消它们。
另一种简单的平均取向的方法是取梯度场与自身的外积所得到的张量场的平均值,并确定对应于最大特征值的特征向量的方向。张量场的获得方法如下:
Mat Sxx = dX * dX;
Mat Syy = dY * dY;
Mat Sxy = dX * dY;

接下来应该对此进行平均处理:

Scalar mSxx = mean(sXX);
Scalar mSyy = mean(sYY);
Scalar mSxy = mean(sXY);

这些值组成一个2x2的实数对称矩阵:
|  mSxx   mSxy  |
|  mSxy   mSyy  |

确定它的特征分解相对来说比较简单,可以通过解析方法完成。我现在手头没有方程式,所以我会留给读者来练习。 :)


“将角度加倍会导致相反的方向映射到相同的值,因此平均值无法消除这些影响。” — 你能解释为什么加倍角度可以防止它们被抵消吗? - theV0ID
@theV0ID 如果你有两个指向相反方向的单位向量,并将它们相加,你最终会得到一个零向量。如果你先将它们的角度加倍,它们现在都将指向同一方向(取任意两个向量,进行计算,并验证此语句!),并将它们相加将产生长度为2的向量。将角度加倍是将向量写成极坐标形式,并将角度值加倍。 - Cris Luengo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接