【问题标题】:How to detect the intensity gradient direction如何检测强度梯度方向
【发布时间】:2016-11-25 00:22:25
【问题描述】:

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

例如拥有

看起来像

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

【问题讨论】:

  • 听起来就像平均梯度...
  • 我看到图片几乎是均匀的像素。你的意思是什么像素?
  • 请定义“垂直于像素值”。
  • 我也不清楚,你的平均表现是什么:选项1:如果你对整个图片进行变化,结果将只是一个方向(然后可以画成一条线) 选项 2:如果您对图片中的一条线进行平均,您将得到 N 个方向(对应于一条摆动的线,就像您画的那样)。选项 3:如果您对图片的一列进行平均 -> 模拟选项 2 让我们知道您想要什么

标签: c++ opencv


【解决方案1】:

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();

【讨论】:

  • 不错 - 干得好!我同意标准化 - 我主要是出于可视化目的这样做,因为原始问题上的几个 cmets 暗示人们看不到 OP 正在寻找什么。
【解决方案2】:

我不能轻易地告诉你如何使用 OpenCV 来做到这一点,但我可以告诉你方法并演示如何在命令行中使用 ImageMagick

首先,我认为您需要将图像转换为灰度并将其标准化为全范围的黑白 - 就像这样:

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

然后你需要使用 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

参见 Wikipedia here - 特别是这一行:

【讨论】:

    【解决方案3】:

    将图像转换为灰度,并根据灰度对其像素进行分类。对于分类,您可以使用 Otsu 方法或具有 2 个集群的 kmeans 之类的方法。再取形态梯度进行二元检测。

    这里是分类的像素和使用 Otsu 方法的边界。

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

    我使用下面的代码得到[0.983035, -0.183421] 共线向量。所以,[0.183421 0.983035] 与这个向量正交。

    在左图中,红线是最小二乘线,蓝线是与红线垂直的线。在右图中,红线是最小二乘线,绿线是使用上述 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;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-17
      • 1970-01-01
      • 1970-01-01
      • 2013-06-22
      • 2016-02-14
      • 2011-08-01
      • 1970-01-01
      相关资源
      最近更新 更多