使用OpenCV函数在C++中计算梯度方向

3

有人能帮我解决这个问题吗?

我正在尝试使用OpenCV中的Sobel算子计算x和y方向的梯度方向。我使用atan2函数计算弧度下的正切值,然后将其转换为角度,但我得到的所有角度都在0到90度之间。

我的期望是得到0到360度之间的角度。我使用的图像是灰度图像。以下是代码片段:

Mat PeripheralArea;
Mat grad_x, grad_y;  // this is the matrix for the gradients in x and y directions
int off_set_y = 0, off_set_x = 0;
int scale = 1, num_bins = 8, bin = 0;
int delta=-1 ;
int ddepth = CV_16S;

GaussianBlur(PeripheralArea, PeripheralArea, Size(3, 3), 0, 0, BORDER_DEFAULT);
Sobel(PeripheralArea, grad_y, ddepth, 0, 1,3,scale, delta, BORDER_DEFAULT);
Sobel(PeripheralArea, grad_x, ddepth, 1, 0,3, scale, delta, BORDER_DEFAULT);

for (int row_y1 = 0, row_y2 = 0; row_y1 < grad_y.rows / 5, row_y2 < grad_x.rows / 5; row_y1++, row_y2++) {
    for (int col_x1 = 0, col_x2 = 0; col_x1 < grad_y.cols / 5, col_x2 < grad_x.cols / 5; col_x1++, col_x2++) {
        gradient_direction_radians = (double) atan2((double) grad_y.at<uchar>(row_y1 + off_set_y, col_x1 + off_set_x), (double) grad_x.at<uchar>(row_y2 + off_set_y, col_x2 + off_set_x));
        gradient_direction_degrees = (int) (180 * gradient_direction_radians / 3.1415);
        gradient_direction_degrees = gradient_direction_degrees < 0
                                     ? gradient_direction_degrees+360
                                     : gradient_direction_degrees;

    }

}

请注意,off_set_xoff_set_y变量不参与计算,而是用于偏移至不同的正方形块,最终想要计算直方图特征向量。

1
请修改您的代码。它很难阅读。 - PiotrWolkowski
1
在未读取代码的情况下,你是否记得arctan函数始终产生介于-pi/2和pi/2之间的值(http://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Principal_values)? - mistapink
1
@mistapink 但是atan2()产生的值在-π和π之间,请参见http://www.cplusplus.com/reference/cmath/atan2/。 - Bull
1
@B... 嗯,我不知怎么地读成了 atan。一定是漏看了那两个字 :) - mistapink
2个回答

6
您已指定Sobel()的目标深度为CV_16S。然而,当您访问grad_xgrad_y时,使用了 .at<uchar>(),这意味着它们的元素是8位无符号量,实际上它们是16位有符号量。 您可以改用 .at<short>(),但在我看来,您的代码存在很多问题,其中最重要的问题之一是OpenCV正好有一个可满足您需求的函数。
请使用cv :: phase(),并将您的循环替换为以下内容:
    cv::Mat gradient_angle_degrees;
    bool angleInDegrees = true;

    cv::phase(grad_x, grad_y, gradient_angle_degrees, angleInDegrees);

0

当我使用C++进行一些边缘检测时,我解决了这个需求。

为了确定梯度的方向,我使用artan2()函数,这个标准API定义了它的+y和+x与我们通常遍历2D图像的方式相同。

下面是我的理解,用图表现出来。

///////////////////////////////
// Quadrants of image:
// 3(-dx,-dy) | 4(+dx,-dy)      [-pi,0]
// ------------------------->+x
// 2(-dx,+dy) | 1(+dx,+dy)      [0,pi]
//            v
//            +y
///////////////////////////////
// Definition of arctan2():
// -135(-dx,-dy) | -45(+dx,-dy)
// ------------------------->+x
//  135(-dx,+dy) | +45(+dx,+dy)
//               v
//               +y
///////////////////////////////

如何进行渐变:

bool gradient(double*& magnitude, double*& orientation, double* src, int width, int height, string file) {
if (src == NULL)
    return false;
if (width <= 0 || height <= 0)
    return false;

double gradient_x_correlation[3*3] = {-0.5, 0.0, 0.5,
                                      -0.5, 0.0, 0.5,
                                      -0.5, 0.0, 0.5};
double gradient_y_correlation[3*3] = {-0.5,-0.5,-0.5,
                                       0.0, 0.0, 0.0,
                                       0.5, 0.5, 0.5};

double *Gx = NULL;
double *Gy = NULL;

this->correlation(Gx, src, gradient_x_correlation, width, height, 3);
this->correlation(Gy, src, gradient_y_correlation, width, height, 3);

if (Gx == NULL || Gy == NULL) 
    return false;

//magnitude
magnitude = new double[sizeof(double)*width*height];
if (magnitude == NULL)
    return false;
memset(magnitude, 0, sizeof(double)*width*height);
double gx = 0.0;
double gy = 0.0;
double gm = 0.0; 
for (int j=0; j<height; j++) {
    for (int i=0; i<width; i++) {
        gx = pow(Gx[i+j*width],2);
        gy = pow(Gy[i+j*width],2);
        gm = sqrt(pow(Gx[i+j*width],2)+pow(Gy[i+j*width],2));
        if (gm >= 255.0) {
            return false;
        }
        magnitude[i+j*width] = gm;
    }
}

//orientation
orientation = new double[sizeof(double)*width*height];
if (orientation == NULL)
    return false;
memset(orientation, 0, sizeof(double)*width*height);
double ori = 0.0;

double dtmp = 0.0;
double ori_normalized = 0.0;
for (int j=0; j<height; j++) {
    for (int i=0; i<width; i++) {
        gx = (Gx[i+j*width]);
        gy = (Gy[i+j*width]);
        ori = atan2(Gy[i+j*width], Gx[i+j*width])/PI*(180.0); //[-pi,+pi]
        if (gx >= 0 && gy >= 0) { //[Qudrant 1]:[0,90] to be [0,63] 
            if (ori < 0) {
                printf("[Err1QUA]ori:%.1f\n", ori);
                return false;
            }
            ori_normalized = (ori)*255.0/360.0; 
            if (ori != 0.0 && dtmp != ori) {
                printf("[Qudrant 1]orientation: %.1f to be %.1f(%d)\n", ori, ori_normalized, (uint8_t)ori_normalized);
                dtmp = ori;
            }
        }
        else if (gx >= 0 && gy < 0) { //[Qudrant 4]:[270,360) equal to [-90, 0) to be [191,255]
            if (ori > 0) {
                printf("[Err4QUA]orientation:%.1f\n", ori);
                return false;
            }
            ori_normalized = (360.0+ori)*255.0/360.0;
            if (ori != 0.0 && dtmp != ori) {
                printf("[Qudrant 4]orientation:%.1f to be %.1f(%d)\n", ori, ori_normalized, (uint8_t)ori_normalized);
                dtmp = ori;
            }
        }
        else if (gx < 0 && gy >= 0) { //[Qudrant 2]:(90,180] to be [64,127]
            if (ori < 0) {
                printf("[Err2QUA]orientation:%.1f\n", ori);
                return false;
            }
            ori_normalized = (ori)*255.0/360.0; 
            if (ori != 0.0 && dtmp != ori) {
                printf("[Qudrant 2]orientation: %.1f to be %.1f(%d)\n", ori, ori_normalized, (uint8_t)ori_normalized);
                dtmp = ori;
            }
        }
        else if (gx < 0 && gy < 0) { //[Qudrant 3]:(180,270) equal to (-180, -90) to be [128,190] 
            if (ori > 0) {
                printf("[Err3QUA]orientation:%.1f\n", ori);
                return false;
            }
            ori_normalized = (360.0+ori)*255.0/360.0; 
            if (ori != 0.0 && dtmp != ori) { 
                printf("[Qudrant 3]orientation:%.1f to be %.1f(%d)\n", ori, ori_normalized, (uint8_t)ori_normalized);
                dtmp = ori;
            }
        }
        else {
            printf("[EXCEPTION]orientation:%.1f\n", ori);
            return false;
        }
        orientation[i+j*width] = ori_normalized;
    }
}
return true;
}

如何进行交叉相关:

bool correlation(double*& dst, double* src, double* kernel, int width, int height, int window) {
if (src == NULL || kernel == NULL)
    return false;
if (width <= 0 || height <= 0 || width < window || height < window )
    return false;
dst = new double[sizeof(double)*width*height];
if (dst == NULL)
    return false;
memset(dst, 0, sizeof(double)*width*height);

int ii = 0;
int jj = 0;
int nn = 0;
int mm = 0;

double max = std::numeric_limits<double>::min();
double min = std::numeric_limits<double>::max();
double range = std::numeric_limits<double>::max();

for (int j=0; j<height; j++) {
    for (int i=0; i<width; i++) {
        for (int m=0; m<window; m++) {
            for (int n=0; n<window; n++) {
                ii = i+(n-window/2);
                jj = j+(m-window/2);
                nn = n;
                mm = m;
                if (ii >=0 && ii<width && jj>=0 && jj<height) {
                    dst[i+j*width] += src[ii+jj*width]*kernel[nn+mm*window];
                }
                else {
                    dst[i+j*width] += 0;
                }
            }
        }
        if (dst[i+j*width] > max) 
            max = dst[i+j*width];
        else if (dst[i+j*width] < min)
            min = dst[i+j*width];
    }
}

//normalize double matrix to be an uint8_t matrix
range = max - min;
double norm  = 0.0;
printf("correlated matrix max:%.1f, min:%.1f, range:%.1f\n", max, min, range);
for (int j=0; j<height; j++) {
    for (int i=0; i<width; i++) {
        norm = dst[i+j*width];
        norm = 255.0*norm/range;
        dst[i+j*width] = norm;
    }
}
return true;
}

对于我来说,我使用一张中空矩形的图片,你可以在我的样例上下载它。

我的示例图像中,中空矩形部分的梯度方向将顺时针从0到360度变化(象限1到2到3到4)。

以下是我的打印品,描述了方向的追踪:

[Qudrant 1]orientation: 45.0 to be 31.9(31)
[Qudrant 1]orientation: 90.0 to be 63.8(63)
[Qudrant 2]orientation: 135.0 to be 95.6(95)
[Qudrant 2]orientation: 180.0 to be 127.5(127)
[Qudrant 3]orientation:-135.0 to be 159.4(159)
[Qudrant 3]orientation:-116.6 to be 172.4(172)
[Qudrant 4]orientation:-90.0 to be 191.2(191)
[Qudrant 4]orientation:-63.4 to be 210.1(210)
[Qudrant 4]orientation:-45.0 to be 223.1(223)

我在GitHub上分享了更多关于数字图像处理的源代码 :)


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