圖像的梯度和幅值

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章