图像的梯度:梯度的方向是函数f(x,y)变化最快的方向,当图像中存在边缘时,一定有较大的梯度值,相反,当图像中有比较平滑的部分时,灰度值变化较小,则相应的梯度也较小,图像处理中把梯度的模简称为梯度,由图像梯度构成的图像成为梯度图像。
练习《学习OpenCV》第六章第五题:
创建一幅新图像,其中只有45度直线,背景为黑,直线为白。给出一系列中孔尺寸,我们将要得到图像的一阶x方向导数(dx)和一阶y方向导数(dy)。然后对这条直线采取以下测量方法。dx和dy图像组成了输入图像的梯度。位置(i,j)的大小是(i,j) = (dx*dx + dy*dy)再开方:且角度是arctan(dy(i,j)/dx(i,j))。扫描整幅图像并寻找幅值最大或者最大附近的位置。记录这些位置的角度。将角度求平均,记录为直线的测量角。
a:用3*3中孔的Sobel滤波器完成。
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
using namespace std;
using namespace cv;
#define PI 3.14159265358979323846
double find_point(IplImage * img1 , IplImage * img2 , CvPoint * p_point)
{
char * ptr1 = img1->imageData; //img1 的数据指针
char * ptr2 = img2->imageData; //img2 的数据指针
double Last_Amplitude = 0.0; //上一点幅度
double Current_Amplitude = 0.0; //当前幅度
double subtraction = 0.0; //两次最大幅值波动允许的范围
double radian = 0.0; //角度和
double rad = 0.0; //角度
double num = 0.0; //次数
bool enable = true;
if(ptr1!=NULL && ptr2!=NULL)
{
for(int i = 0;i<img1->height;i++) //对数据矩阵的行进行寻址
{
ptr1 = img1->imageData + i * img1->widthStep;
ptr2 = img2->imageData + i * img2->widthStep;
for(int j = 0;j<img1->width;j++) //对数据矩阵的列进行寻址
{
Current_Amplitude = sqrt(ptr1[j] * ptr1[j] + ptr2[j] * ptr2[j]);
subtraction = abs(Current_Amplitude - Last_Amplitude);
if (Current_Amplitude > Last_Amplitude)
{
Last_Amplitude = Current_Amplitude;
if (subtraction > 2) //大于2代表要统计新的幅值了
{
radian = 0;
num = 0;
}
p_point->x = j; //对于x和y的坐标系来说,x为列
p_point->y = i; //y为横
rad = atan((double)ptr2[j]/ptr1[j]);
radian += rad;
num++;
cout << " j: " << j << " i: " << i << endl;
cout << " 采集的最大幅值个数: " << num << " Last_Amplitude: " << Last_Amplitude << endl;
cout << " 弧度和: " << radian << endl;
enable = false; //标志位排除Current_Amplitude>Last_Amplitude,且差值小于2时的两次加和情况
}
else if(subtraction>0 && subtraction < 2 && enable) //判断幅值是否在正常的波动范围内
{
p_point->x = j;
p_point->y = i;
rad = atan((double)ptr2[j] / ptr1[j]);
radian += rad;
num++;
cout << " X: " << p_point->x << " Y: " << p_point->y << endl;
cout << " 采集的最大幅值次数: " << num << " Current_Amplitude: " << Current_Amplitude << endl;
cout << " 弧度和: " << radian << endl;
}
enable = true;
}
}
radian = radian / num;
return radian;
}
return 0;
}
void ShowImgData(IplImage * img)
{
for(int i = 0;i<img->height;i++)
{
char * ptr = img->imageData + i * img->widthStep;
for(int j = 0;j<img->width;j++)
{
int data = int(ptr[j]);
if(data!=0)
{
cout<<"("<<j<<","<<i<<") = "<<data<<endl;
}
}
}
}
int main(int argc, const char * argv[]) {
/*1、加载一幅灰度图像*/
const char filename[] = "/Users/linwang/Desktop/45line.png";
IplImage * Img = cvLoadImage(filename,CV_LOAD_IMAGE_GRAYSCALE);
/*2、缩小一倍*/
IplImage *out = cvCreateImage(cvSize(Img->width/2,Img->height/2), Img->depth, Img->nChannels);
cvResize(Img, out);
cvShowImage("1/2 Src", out);
/*3、Sobel分别在x方向求导数*/
IplImage *dst_x_3 = cvCloneImage(out);
cvSetZero(dst_x_3);
cvSobel(out, dst_x_3, 1, 0, CV_SCHARR);
ShowImgData(dst_x_3);
cvShowImage("Sobel_X_3", dst_x_3);
/*4、Sobel分别在y方向求导数*/
IplImage *dst_y_3 = cvCloneImage(out);
cvSetZero(dst_y_3);
cvSobel(out, dst_y_3, 0, 1, CV_SCHARR);
ShowImgData(dst_y_3);
cvShowImage("Sobel_Y_3", dst_y_3);
/*5、找到幅值像素的最大点*/
CvPoint Point(0,0); //幅值值为最大点
double Radian = 0.0; //弧度
Radian = find_point(dst_x_3, dst_y_3, &Point);
cout<<Radian<<endl;
Radian = (double)(Radian / PI)*180;
cout << " Sobel 3*3算子,图像中直线的角度: " << Radian << endl << endl;
cvWaitKey(0);
cvReleaseImage(&Img);
cvReleaseImage(&dst_x_3);
cvReleaseImage(&dst_y_3);
cvReleaseImage(&out);
return 0;
}