图像的梯度和幅值

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


发布了192 篇原创文章 · 获赞 14 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/u011559236/article/details/78682806
今日推荐