我试图使用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.";
}
}
}
答案 0 :(得分:6)
您正在使用整数运算,因此您对弧度和度数的计算会因截断而受到严重影响。
此外,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上的响应更容易修复这样的简单错误。
答案 1 :(得分:5)
您可以使用Sobel运算符获取x-derivative dx
和y-derivative dy
。然后,您可以使用公式计算渐变的大小和方向。 G=sqrt(dx^2+dy^2), theta=arctan(dy/dx)
。你可以发现这只是将descartes坐标系(x,y)转换为极坐标(rho,theta)!
您的代码中出现了一些错误,您将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 << " ";
答案 2 :(得分:2)
你获取渐变的绝对值,它将所有角度都映射到[-180; 180]至[0; 90]。你也使用整数除法。