圖像的梯度:梯度的方向是函數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;
}