【问题标题】:Determining image gradient direction from sobel?从sobel确定图像梯度方向?
【发布时间】:2013-11-15 17:25:29
【问题描述】:

我正在尝试使用 openCV 的 Sobel 方法的结果来确定图像梯度方向。

我知道这应该是一个非常简单的任务。我从这里的许多资源和答案中复制了这些方法,但无论我做什么,结果方向总是在 0 到 57 度之间(我希望范围是 0 到 360 度)。

我相信所有的深度都是正确的。我试过用 16S 数据和 8U 数据计算方向。

我就是看不出哪里出错了?谁能发现我的错误?

void getGradients(IplImage* original, cv::Mat* gradArray)
{
    cv::Mat original_Mat(original, true);

    // Convert it to gray
    cv::cvtColor( original_Mat, original_Mat, CV_RGB2GRAY );
    //cv::blur(original_Mat, original_Mat, cv::Size(7,7));

    /// Generate grad_x and grad_y
    cv::Mat grad_x = cv::Mat::zeros(original->height, original->width, CV_16S); 
    cv::Mat grad_y = cv::Mat::zeros(original->height, original->width, CV_16S);

    cv::Mat abs_grad_x = cv::Mat::zeros(original->height, original->width, CV_8U);
    cv::Mat abs_grad_y = cv::Mat::zeros(original->height, original->width, CV_8U);;

    /// Gradient X
    cv::Sobel(original_Mat, grad_x, CV_16S, 1, 0, 3);
    cv::convertScaleAbs( grad_x, abs_grad_x );

    /// Gradient Y
    cv::Sobel(original_Mat, grad_y, CV_16S, 0, 1, 3);
    cv::convertScaleAbs( grad_y, abs_grad_y );

    uchar* pixelX = abs_grad_x.data;
    uchar* pixelY = abs_grad_y.data;
    uchar* grad1 = gradArray[0].data;
    uchar* grad2 = gradArray[1].data;
    uchar* grad3 = gradArray[2].data;
    uchar* grad4 = gradArray[3].data;
    uchar* grad5 = gradArray[4].data;
    uchar* grad6 = gradArray[5].data;
    uchar* grad7 = gradArray[6].data;
    uchar* grad8 = gradArray[7].data;
    int count = 0;
    int min = 999999;
    int max = 0;

    for(int i = 0; i < grad_x.rows * grad_x.cols; i++) 
    {
            int directionRAD = atan2(pixelY[i], pixelX[i]);
            int directionDEG = directionRAD / PI * 180;

            if(directionDEG < min){min = directionDEG;}
            if(directionDEG > max){max = directionDEG;}

            if(directionDEG >= 0 && directionDEG <= 45)         { grad1[i] = 255; count++;}         
            if(directionDEG >= 45 && directionDEG <= 90)        { grad2[i] = 255; count++;}         
            if(directionDEG >= 90 && directionDEG <= 135)       { grad3[i] = 255; count++;}         
            if(directionDEG >= 135 && directionDEG <= 190)      { grad4[i] = 255; count++;}         
            if(directionDEG >= 190 && directionDEG <= 225)      { grad5[i] = 255; count++;}         
            if(directionDEG >= 225 && directionDEG <= 270)      { grad6[i] = 255; count++;}     
            if(directionDEG >= 270 && directionDEG <= 315)      { grad7[i] = 255; count++;}
            if(directionDEG >= 315 && directionDEG <= 360)      { grad8[i] = 255; count++;}

            if(directionDEG < 0 || directionDEG > 360)
            {
                cout<<"Weird gradient direction given in method: getGradients.";
            }               
    }
}

【问题讨论】:

    标签: opencv image-processing gradient


    【解决方案1】:

    您正在使用整数算术,因此您对弧度和度数的计算因截断而受到严重影响。

    另外atan2 给出的结果在-PI+PI 的范围内,所以如果你想要一个在0..360 范围内的度数,你需要添加一个180 度校正:

            double directionRAD = atan2(pixelY[i], pixelX[i]);
            int directionDEG = (int)(180.0 + directionRAD / M_PI * 180.0);
    

    注意double 的使用,而不是int 用于directionRAD

    专业提示:学习使用调试器来单步调试代码,在执行过程中检查变量 - 这将使修复此类简单错误比等待 StackOverflow 上的响应更容易。

    【讨论】:

    • 感谢保罗的建议。我按照您的建议更正了截断问题,但是并没有太大区别。我仍然得到有限数量的渐变方向 0 - 90(或 180 - 270 应用了 180 度校正)。我不再缩放这些值,因此使用 Sobel 操作给出的原始 16S 值。我已经逐步完成了整个图像的过程,但看不出哪里出了问题。你有什么想法?谢谢。
    • 你也去掉了abs操作吗?如果是这样,那么我建议您将最新代码作为新问题发布。
    • 是的,我做到了。我现在将发布一个新问题。谢谢。
    【解决方案2】:

    您可以使用 Sobel 运算符获得 x 导数 dx 和 y 导数 dy。然后可以使用公式计算梯度的大小和方向。 G=sqrt(dx^2+dy^2), theta=arctan(dy/dx)。你会发现这只是将笛卡尔坐标系(x,y)转换为极坐标(rho,theta)!

    您的代码中有问题,您将dxdy 设为绝对值,这使得方向始终位于笛卡尔坐标系的第一象限。而你使用的函数convertScaleAbs将结果转换为8位,导致截断错误。

    我有一个演示,可以部分根据您的代码计算幅度。

        const string imgname = "F:/OpenCV/square.jpg";
        Mat img = imread(imgname, CV_LOAD_IMAGE_COLOR);
    
        // 1. convert it to gray value
        Mat gray;
        cvtColor(img, gray, CV_BGR2GRAY);
        // 2. blur the image
        blur(gray, gray, Size(7, 7));
        // 3. sobel
        Mat grad_x, grad_y;
        Scharr(gray, grad_x, CV_32FC1, 1, 0);
        Scharr(gray, grad_y, CV_32FC1, 0, 1);
        // 4. calculate gradient magnitude and direction
        Mat magnitude, direction;
        bool useDegree = true;    // use degree or rad
        // the range of the direction is [0,2pi) or [0, 360)
        cartToPolar(grad_x, grad_y, magnitude, direction, useDegree);
    
        // test, the histogram of the directions
        vector<int> cnt(8, 0);   // 0-45, 45-90, ..., 315-360
    
        for(auto iter = direction.begin<float>(); iter != direction.end<float>(); ++iter)
        {
            int idx = static_cast<int>(*iter) / 45;
            ++cnt[idx];
        }
    
        Mat scaled;
        convertScaleAbs(magnitude, scaled);
        imshow("magnitude", scaled);
        for(auto v : cnt)
            cout << v << " ";
    

    【讨论】:

      【解决方案3】:

      您获取渐变的绝对值,它映射从 [-180; 的所有角度; 180] 到 [0;90]。你也使用整数除法。

      【讨论】:

      • 感谢老飞碟。我按照 Paul 的建议纠正了整数除法问题,并且我只使用了直接来自 Sobel 操作的 16S 数据。然而,我仍然得到 0 到 90 的有限方向范围。你能看出我的代码还有什么问题吗?
      • 请更新您帖子中的代码,以便我们找到当前版本中的错误。
      猜你喜欢
      • 1970-01-01
      • 2011-08-01
      • 2020-09-12
      • 2014-04-18
      • 2017-12-10
      • 2015-01-21
      • 2013-11-08
      • 2013-06-22
      相关资源
      最近更新 更多