如何检测强度梯度方向

时间:2016-11-25 00:22:25

标签: c++ opencv

具有灰度像素的正方形区域的垫子。如何创建一条直线,其方向被创建为与大多数像素值垂直的方向改变方向(平均梯度,整个Mat上的平均值,结果将只是一个方向(然后可以绘制为一条线))?

例如

enter image description here

看起来像

enter image description here

如何在OpenCV(python或C ++)中做这样的事情?

3 个答案:

答案 0 :(得分:7)

OpenCV实现看起来如下所示。它以与Mark Setchell的答案中所解释的方式类似的方式解决了这个问题,除了对图像进行标准化对结果方向没有任何影响。

Mat img = imread("img.png", IMREAD_GRAYSCALE);

// compute the image derivatives for both the x and y direction
Mat dx, dy;
Sobel(img, dx, CV_32F, 1, 0);
Sobel(img, dy, CV_32F, 0, 1);

Scalar average_dx = mean(dx);
Scalar average_dy = mean(dy);

double average_gradient = atan2(-average_dy[0], average_dx[0]);
cout << "average_gradient = " << average_gradient << endl;

显示生成的方向

Point center = Point(img.cols/2, img.rows/2);
Point direction = Point(cos(average_gradient) * 100, -sin(average_gradient) * 100);

Mat img_rgb = imread("img.png"); // read the image in colour
line(img_rgb, center, center + direction, Scalar(0,0,255));
imshow("image", img_rgb);
waitKey();

image direction

答案 1 :(得分:4)

我无法轻易告诉您如何使用 OpenCV 进行操作,但我可以告诉您方法,并在命令行中使用 ImageMagick 进行演示

首先,我认为您需要将图像转换为灰度并将其标准化为黑色到白色的全部范围 - 如下所示:

convert gradient.png -colorspace gray -normalize stage1.png

enter image description here

然后你需要使用Sobel滤波器计算图像的X梯度和Y梯度,然后在X梯度上采用Y梯度的反褐色:

convert stage1.png -define convolve:scale='50%!' -bias 50% \
  \( -clone 0 -morphology Convolve Sobel:0 \) \
  \( -clone 0 -morphology Convolve Sobel:90 \) \
  -fx '0.5+atan2(v-0.5,0.5-u)/pi/2' result.jpg

然后result.jpg中像素的平均值就是你的线的方向。

你可以看到卷积中用于X和Y梯度的系数如下:

convert xc: -define morphology:showkernel=1 -morphology Convolve Sobel:0 null:

Kernel "Sobel" of size 3x3+1+1 with values from -2 to 2
Forming a output range from -4 to 4 (Zero-Summing)
 0:         1         0        -1
 1:         2         0        -2
 2:         1         0        -1


convert xc: -define morphology:showkernel=1 -morphology Convolve Sobel:90 null:
Kernel "Sobel@90" of size 3x3+1+1 with values from -2 to 2
Forming a output range from -4 to 4 (Zero-Summing)
 0:         1         2         1
 1:         0         0         0
 2:        -1        -2        -1

参见维基百科here - 特别是这一行:

enter image description here

答案 2 :(得分:3)

将图像转换为灰度,并根据灰度级对其像素进行分类。对于分类,您可以使用Otsu方法或具有2个簇的kmeans。然后采用形态学梯度来检测doundary。

以下是使用Otsu方法的分类像素和边界。

otsu boundaty

现在找到边界图像的非零像素,并使用fitLine函数将2D线拟合到这些像素,该函数找到加权最小二乘线或使用this RANSAC实现。 fitLine给出与线共线的归一化矢量。使用此向量,您可以找到它的正交向量。

我使用下面的代码得到[0.983035, -0.183421]共线矢量。因此,[0.183421 0.983035]与此向量正交。

这里,在左图中,红线是最小方线,蓝线是红线的垂直线。在右图中,红线是最小方块线,绿色线是使用上述RANSAC库拟合的线。

lsq ransac

Mat im = imread("LP24W.png", 0);

Mat bw, gr;

threshold(im, bw, 0, 255, CV_THRESH_BINARY|CV_THRESH_OTSU);

Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
morphologyEx(bw, gr, CV_MOP_GRADIENT, kernel);

vector<vector<Point>> contours;
findContours(gr, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
vector<Point> points;
for (vector<Point>& cont: contours)
{
    points.insert(points.end(), cont.begin(), cont.end());
}
Vec4f line;
fitLine(points, line, CV_DIST_L2, 0, 0.01, 0.01);
cout << line << endl;