Peter's codebook A blog full of codes

求影像梯度 -- ZeroJudge b436

難得這次暑假很長,

試著實作出求影像梯度的方法。

影像的梯度就是像素間的變化值,

最簡單的方式是由右方像素減自己、下方像素減自己,

就可以得出基本的影像梯度(看起來像影像邊緣)。

另外一個方法是,使用 Sobel operator 做卷積(常用來作邊緣偵測),

再把 x 方向、 y 方向卷積出來的結果 / 8.0 ,就可以得到整張影像的梯度。

至於為什麼要 / 8.0 ,仔細觀察 Sobel operator 的陣列就可以明白:

X 向 Sobel operator:

Y 向 Sobel operator:

X 方向卷積最多使像素值為 255 的 4 倍,

Y 方向卷積最多使像素值為 255 的 4 倍。

因此考慮 X, Y 方向得到的卷積值,需要除 8

最後輸出

其中, 代表 X 方向, Sobel operator 對像素點 做卷積。其餘以此類推。

算完梯度以後,照 ZJ 題意,四捨五入輸出。

NOTE: 不要連 alpha 也一起做梯度,不然整張圖的可讀性會很差

ZJ 題目傳送門

簡單梯度

較上面複雜的梯度

結果

Before:

img1

After:

img2

Before:

img3

After:

img4

主要程式碼

Gradient.h :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef __INCLUDE_GRADIENT_HEADER
#define __INCLUDE_GRADIENT_HEADER
#include "utils.h"
#include <cmath>

namespace ImageProcess 
{
    class Gradient {
        private:
            Image simg;
            Image timg;
            Pix getGradient(int x, int y);
        public:
            Gradient(const Image &img);
            Image* run(void);
            static const double xMask[3][3], yMask[3][3];
            ~Gradient();
    };
};

#endif

Gradient.cpp :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#ifndef __INCLUDE_GRADIENT_CPP
#define __INCLUDE_GRADIENT_CPP
#include "utils.h"
#include "Gradient.h"
#include <cmath>

namespace ImageProcess
{
    const double Gradient::xMask[3][3] = {
        {-1, 0, 1},
        {-2, 0, 2},
        {-1, 0, 1}
    };
    const double Gradient::yMask[3][3] = {
        {-1, -2, -1},
        { 0,  0,  0},
        { 1,  2,  1}
    };
    Pix Gradient::getGradient(int x, int y) {
        PixDouble sx(0,0,0,0);
        PixDouble sy(0,0,0,0);
        PixDouble O(0,0,0,0);
        for (int i=0; i<3; ++i) {
            for (int j=0; j<3; ++j) {
                PixDouble t(simg.getPixel(x+j-1,y+i-1));
                sx = sx + (t*xMask[i][j]);
                sy = sy + (t*yMask[i][j]);
            }
        }
        sx = sx - O; // 取絕對值,這裡 sx - O 代表 abs(sx - O)
        sy = sy - O; // 取絕對值,這裏 sy - O 代表 abs(sy - O)
        Pix res((sx+sy)/8.0, 24);
        res.alpha = simg.getPixel(x,y).alpha;
        return res;
    }
    Gradient::Gradient(const Image &img) : simg(img.img_W+2, img.img_H+2), timg(img.img_W, img.img_H) {
        /* paddle borders */
        for (int i=0; i<img.img_H; ++i) {
            simg.setPixel(0, i+1, img.getPixel(0, i));
            simg.setPixel(simg.img_W-1, i+1, img.getPixel(img.img_W-1, i));
            for (int j=0; j<img.img_W; ++j) {
                simg.setPixel(j+1, i+1, img.getPixel(j,i));
            }
        }
        for (int i=0; i<img.img_W; ++i) {
            simg.setPixel(i+1, 0, img.getPixel(i, 0));
            simg.setPixel(i+1, simg.img_H-1, img.getPixel(i, img.img_H-1));
        }

        /*paddle corners*/
        simg.setPixel(0,0, img.getPixel(0,0));
        simg.setPixel(0,simg.img_H-1, img.getPixel(0,img.img_H-1));
        simg.setPixel(simg.img_W-1,simg.img_H-1, img.getPixel(img.img_W-1,img.img_H-1));
        simg.setPixel(simg.img_W-1,0, img.getPixel(img.img_W-1,0));
    }
    Image* Gradient::run(void) {
        for (int i=1; i<simg.img_H-1; ++i) {
            for (int j=1; j<simg.img_W-1; ++j) {
                Pix res(getGradient(j,i));
                timg.setPixel(j-1,i-1,res);
            }
        }
        return new Image(this->timg);
    }
    Gradient::~Gradient() {}
};

#endif

完整程式碼:GitHub

ZJ 上 AC 程式碼:GitHub

comments powered by Disqus