Opencv 从sobel确定图像梯度方向?

Opencv 从sobel确定图像梯度方向?,opencv,image-processing,gradient,Opencv,Image Processing,Gradient,我试图使用openCV的Sobel方法的结果来确定图像的梯度方向 我理解这应该是一项非常简单的任务。我从许多参考资料和答案中复制了这些方法,但无论我做什么,结果方向总是在0-57度之间(我希望范围是0-360度) 我相信所有的深度都是正确的。我试着用16S数据和8U数据计算方向 我只是看不出我错在哪里?有人能看出我的错误吗 void getGradients(IplImage* original, cv::Mat* gradArray) { cv::Mat original_Mat(or

我试图使用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.";
            }               
    }
}
void getGradients(IplImage*原始,cv::Mat*梯度阵列)
{
cv::Mat original_Mat(原件,真实);
//将其转换为灰色
cv::cvtColor(原色垫、原色垫、cv\u RGB2GRAY);
//cv::模糊(原始垫,原始垫,cv::大小(7,7));
///生成梯度x和梯度y
cv::Mat grad_x=cv::Mat::zeros(原始->高度,原始->宽度,cv_16S);
cv::Mat grad_y=cv::Mat::zeros(原始->高度,原始->宽度,cv_16S);
cv::Mat abs_grad_x=cv::Mat::Zero(原始->高度,原始->宽度,cv_8U);
cv::Mat abs_grad_y=cv::Mat::Zero(原始->高度,原始->宽度,cv_8U);;
///梯度X
cv::Sobel(原始材料,梯度x,cv_16S,1,0,3);
cv::可转换比例(梯度x,绝对梯度x);
///梯度Y
cv::Sobel(原始材料,梯度,cv_16S,0,1,3);
cv::可转换比例(梯度、绝对梯度);
uchar*pixelX=绝对梯度x.数据;
uchar*像素=绝对梯度数据;
uchar*grad1=gradArray[0]。数据;
uchar*grad2=gradArray[1]。数据;
uchar*grad3=gradArray[2]。数据;
uchar*grad4=gradArray[3]。数据;
uchar*grad5=gradArray[4]。数据;
uchar*grad6=gradArray[5]。数据;
uchar*grad7=gradArray[6]。数据;
uchar*grad8=gradArray[7]。数据;
整数计数=0;
int min=999999;
int max=0;
对于(int i=0;imax){max=directionDEG;}
如果(方向度>=0&&directionDEG=45&&directionDEG=90&&directionDEG=135&&directionDEG=190&&directionDEG=225&&directionDEG=270&&directionDEG=315&&directionDEG=360)
{

cout您使用的是整数运算,因此弧度和度数的计算受到截断的严重影响

另外,
atan2
给出了
-PI
+PI
范围内的结果,因此,如果您想要0..360范围内的度值,则需要添加180度校正:

        double directionRAD = atan2(pixelY[i], pixelX[i]);
        int directionDEG = (int)(180.0 + directionRAD / M_PI * 180.0);
注意
directionRAD
使用的是
double
而不是
int


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

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

你可以使用索贝尔算子得到x导数
dx
和y导数
dy
。然后你可以使用公式来计算梯度的大小和方向。
G=sqrt(dx^2+dy^2),theta=arctan(dy/dx)
。你可以发现这只是转换笛卡尔坐标系(x,y)到极坐标(ρ,θ)

您的代码中存在错误,您将
dx
dy
的绝对值设置为绝对值,这使得方向始终位于笛卡尔坐标系的第一象限。您使用的函数
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 << " ";
const string imgname=“F:/OpenCV/square.jpg”;
Mat img=imread(imgname、CV\u LOAD\u IMAGE\u COLOR);
//1.将其转换为灰度值
席灰色;
CVT颜色(img、灰色、CV_bgr2灰色);
//2.模糊图像
模糊(灰色,灰色,大小(7,7));
//3.索贝尔
Mat grad_x,grad_y;
沙尔(灰色,渐变x,CV_32FC1,1,0);
Scharr(灰色,渐变,CV_32FC1,0,1);
//4.计算坡度大小和方向
垫的大小、方向;
bool usedegate=true;//使用度或rad
//方向的范围为[0,2pi)或[0,360)
cartToPolar(梯度x、梯度y、震级、方向、使用程度);
//测试,方向的直方图
向量cnt(8,0);/0-45,45-90,…,315-360
for(自动iter=方向.开始();iter!=方向.结束();++iter)
{
int idx=静态铸件(*iter)/45;
++cnt[idx];
}
垫状鳞片;
可转换标度(幅值、标度);
imshow(“震级”,按比例);
用于(自动v:cnt)

非常感谢Paul的建议。我按照你的建议更正了截断问题,但是没有太大的区别。我仍然得到0-90(或180-270,应用180度更正)的有限数量的渐变方向。我不再缩放这些值,因此使用Sobel操作给出的原始16秒值。我已经对整个图像进行了处理,但看不出哪里出错。你有什么想法吗?谢谢。你也删除了abs操作吗?如果是,那么我建议你将最新代码作为新问题发布。是的,我有。我会发布现在是一个新问题。谢谢。谢谢老ufo。我按照Paul的建议更正了整数除法问题,我只直接使用Sobel操作中的16S数据。但是我仍然得到0-90之间的有限方向范围。你能看到我的代码有什么其他错误吗?请在你的帖子中更新代码,这样我们就可以在当前的v中发现错误版本。