デジタル画像処理（空間フィルタリング）

## *空間フィルタリング*
領域に基づく濃淡変換（area to pixel operation)とのこと。
フィルタを各ピクセルに当てはめて、積和演算を実行して画像の平滑化（ノイズ除去）、エッジ検出を行う。
イメージは[この動画](https://vimeo.com/12774628)のように高速でフィルタをあてはめ、フィルタから得られた計算値を格画素に入力する。（畳み込みとも呼ばれる）

平滑化（画素値の凹凸を減少させる）

### *使用する画像*

![1_av_fil_before.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/04326a15-092b-ba15-51bf-b672ce74bd5e.jpeg)

使ったライブラリ

```python:
import numpy as np
from numpy.lib.stride_tricks import as_strided
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import axes3d
import matplotlib.gridspec as gridspec
import seaborn as sns
import math
import cv2
```

### *平均化フィルタ*
領域内の画素値の平均を注目画素の値とする。

```python:
av_filter = np.array([[ 1/25,1/25,1/25,1/25,1/25,],
                      [ 1/25,1/25,1/25,1/25,1/25,],
                      [ 1/25,1/25,1/25,1/25,1/25,],
                      [ 1/25,1/25,1/25,1/25,1/25,],
                      [ 1/25,1/25,1/25,1/25,1/25,]], np.float32)

# ddepth = -1, means destination image has depth same as input image
dst1 = cv2.filter2D(image_1, -1, av_filter)
cv2.imwrite('2_av_fil.jpg', dst1)
```
#### ↓全体的に平滑化さてている。
![2_av_fil.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/5ea96ac2-9f63-2b3f-859e-70de6b7f093a.jpeg)

### *加重平均フィルタ*
*重みつき平均化*とも呼ばれ注目画素を中心に、外にいくにしたがって重みが減っているような分布

```python:
add_av_fil = np.array([[1, 4, 6, 4, 1],
       [4, 16, 24, 16, 4],
       [6, 24, 36, 24, 6],
       [4, 16, 24, 16, 4],
       [1, 4, 6, 4, 1]], np.float32)/256

dst1 = cv2.filter2D(res, -1, add_av_fil)
cv2.imwrite('3_add_av_fil.jpg', dst1)
```
#### ↓比較的、エッジを保存しつつ平滑化さてている。

![3_add_av_fil.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/d6477a70-2e9b-e67e-2dc7-06710e8eb8a7.jpeg)

### *ガウシアンフィルタ*
ガウス分布に従って重みをつけている。

$$
    h\left(x, y\right) = \frac{1}{2\pi\sigma^2}{\exp{\left(-\frac{x^2+y^2}{2\sigma^2}\right)}}\\
$$

```python:
def gausian(X, y, siguma):
#     exp(-(x2 + y2)/2σ2)
    h2 = math.e**(-1*((X**2) + (y**2))/(2*(siguma**2)))
#     2πσ2
    h3 = 2*math.pi*(siguma**2)
    h = h2/h3
    return h

sns.set_style('ticks')

fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111, projection='3d')

x = y = np.arange(-3,3,0.1)
X,Y = np.meshgrid(x,y)

ax.plot_surface(X,Y, gausian(X,Y,1),cmap='coolwarm')
plt.show()
plt.savefig("gausian_fig.jpg")
```
#### ↓こういうイメージ
![gausian_fig.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/126cc8d4-a6f8-0631-d434-2194cae25e95.jpeg)

手作り

```python:
def gausian_kernel(size):
    if size%2==0:
        print('kernel size should be odd')
        return
    sigma = (size-1)/2
    
#     [0,size]→[-sigma, sigma] にずらす
    x = y = np.arange(0,size) - sigma
    X,Y = np.meshgrid(x,y) 
    print(sigma)
    
    mat = gausian(X,Y,sigma)
    
    # 総和が1になるように
    kernel = mat / np.sum(mat)
    return mat

g_fil_hand = gausian_kernel(3)

#フィルタ形状
#[[0.05854983 0.09653235 0.05854983]
#[0.09653235 0.15915494 0.09653235]
#[0.05854983 0.09653235 0.05854983]]

dst1 = cv2.filter2D(res, -1, g_fil_hand)
cv2.imwrite('4_g_hand_fil.jpg', dst1)
```

![4_g_hand_fil.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/e32025b0-a397-de21-829c-ed2fb6fdcb96.jpeg)

#### opencv による

```python:
sigma = 2
blur = cv2.GaussianBlur(res, (0, 0), sigmaX = 3, sigmaY = 3)
cv2.imwrite('5_g_cv_fil.jpg', blur)
```

![5_g_cv_fil.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/438bed84-bd8a-80dd-b277-c0b55bcaacbb.jpeg)


### *特定方向の平滑化*
以下のようなフィルタを用いることで、特定方向にのみ平滑化が行われ、
横にぶれたような絵になる。

```python:
flat_d_fil = np.zeros([15, 15])

for i in range(15):
    flat_d_fil[i, i] = 1/15

dst1 = cv2.filter2D(res, -1, flat_d_fil)
cv2.imwrite('6_shaky_fil.jpg', dst1)
```

![6_shaky_fil.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/7b5c8ddd-9b25-379d-567f-e8119afbdfa7.jpeg)

### *微分フィルタ*

#### 縦エッジ用

```python:
diff_col = np.array([[0, 0, 0],
        [1, 0, -1],
        [0, 0, 0]])

dst1 = cv2.filter2D(res, -1, 8*diff_col)
cv2.imwrite('7_diff_col.jpg', dst1)
```
![7_diff_col.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/d3458c74-c914-18dd-020f-22e935621724.jpeg)

#### 横エッジ用

```python:
diff_row = np.array([[0, -1, 0],
        [0, 0, 0],
        [0, 1, 0]])

dst1 = cv2.filter2D(res, -1, 8*diff_row)
cv2.imwrite('8_diff_row.jpg', dst1)
```

![8_diff_row.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/c3341053-9438-ac51-ac15-a804a8c9d7d8.jpeg)


### *プリューウィット　フィルタ*
微分フィルタと平均フィルタの組み合わせ
横方向の画素値の勾配を拾う　＋　縦方向の画素値の勾配を平滑化

```python:
pulu_fil = np.array([[1, 0, -1],
        [1, 0, -1],
        [1, 0, -1]])

dst1 = cv2.filter2D(res, -1, 2*pulu_fil)
cv2.imwrite('9_pulu_fil.jpg', dst1)
```

![9_pulu_fil.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/ee82b8d3-abde-2026-1511-b5dc9cedd21c.jpeg)

### *ソーベル　フィルタ*
微分フィルタと加重平均フィルタの組み合わせ 比較的にプリューウィットに比べ、滑らかなぼかし具合を実現する。 横方向の画素値の勾配を拾う　＋　縦方向の画素値の勾配を平滑化


```python:
sobel_fil = np.array([[1, 0, -1],
        [2, 0, -2],
        [1, 0, -1]])

dst1 = cv2.filter2D(res, -1, sobel_fil)
cv2.imwrite('10_sobel_fil.jpg', dst1)
```

![10_sobel_fil.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/7f4815a0-66a2-45aa-1b24-387cb773e2e1.jpeg)

### *2次微分フィルタ*
微分フィルタを二重にした感じ

```python:
todiff_fil = np.array([[0, 0, 0],
                      [1, -2, 1],
                      [0, 0, 0]])

dst1 = cv2.filter2D(res, -1, 2*todiff_fil)
cv2.imwrite('11_to_diff_fil.jpg', dst1)
```

### *ラプラシアン　フィルタ*

二次微分の縦＋横をした感じ。

```python:
lap_fil = np.array([[0, 1, 0],
        [1, -4, 1],
        [0, 1, 0]])

dst1 = cv2.filter2D(res,ddepth=cv2.CV_16S,kernel=lap_fil)
cv2.imwrite('12_lap_hand_fil.jpg', dst1+100)

plt.plot(ds[400])
plt.savefig("lap_fig_1.jpg")
```

![12_lap_fil.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/c573e110-0f5f-e759-c530-af5833323b7c.jpeg)

![lap_fig_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/4ec0516b-9d99-83bd-0f27-e0ede65a92fe.jpeg)

#### opencvで

```python:
ddepth = cv2.CV_16S
kernel_size = 3
dst = cv2.Laplacian(res, ddepth, ksize=kernel_size)
cv2.imwrite('13_lap_cv_fil.jpg', dst)

plt.plot(dst[400]+100)
plt.savefig("lap_fig_2.jpg")
```

![13_lap_cv_fil.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/7b85d5ef-7986-cdd4-70f5-4defcbb0baec.jpeg)


### *Log　フィルタ（ラプラシアン　ガウシアン）*

$$
    h\left(x, y\right) = \frac{x^2+y^2-2\sigma^2}{2\pi\sigma^6}{\exp{\left(-\frac{x^2+y^2}{2\sigma^2}\right)}}\\
$$

手作り

```python:
def Log_fil(X, y, siguma):
    
#     X2+y2-2σ2
    h1 = ((X**2) + (y**2) - (2*(siguma**2))) 
    
#     exp(-(x2 + y2)/2σ2)
    h2 = math.e**(-1*((X**2) + (y**2))/(2*(siguma**2)))
    
#     2πσ6
    h3 = 2*math.pi*(siguma**6)

    h = h1*h2/h3
    return h

sns.set_style('ticks')

fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111, projection='3d')

x = y = np.arange(-3,3,0.1)
X,Y = np.meshgrid(x,y)

ax.plot_surface(X,Y,Log_fil(X,Y,1),cmap='coolwarm')
plt.savefig("log_fig.jpg")
plt.show()
```

![log_fig.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/2065a8b6-8880-1a63-0f25-43ac81ce9983.jpeg)

```python:
def L_of_g_kernel(size, sigma):
    if size%2==0:
        print('kernel size should be odd')
        return
    ad_size = (size-1)/2
    
    # [0,size]→[-sigma, sigma] にずらす
    x = y = np.arange(0,size) - ad_size
    X,Y = np.meshgrid(x,y) 
    print(sigma)
    
    mat = Log_fil(X,Y,sigma)
    print(mat)
    
    # 総和が0になるように
    kernel = mat - np.sum(mat)/size**2
    
    return kernel

log_fil =  L_of_g_kernel(5, 1)

#フィルタ形状
#[[ 0.01749015  0.0391927   0.04307856  0.0391927   0.01749015]
# [ 0.0391927   0.         -0.09653235  0.          0.0391927 ]
# [ 0.04307856 -0.09653235 -0.31830989 -0.09653235  0.04307856]
# [ 0.0391927   0.         -0.09653235  0.          0.0391927 ]
# [ 0.01749015  0.0391927   0.04307856  0.0391927   0.01749015]]
```

```python:
dst1 = cv2.filter2D(res,ddepth=cv2.CV_16S,kernel=log_fil)
cv2.imwrite('14_log_fil_hand.jpg', dst1*20+200)
plt.savefig("log_fill_1.jpg")
plt.plot(dst1[400]*20+200)
```

![14_log_fil_hand.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/fb00e012-47ec-3c3d-c12a-0d7a4e0732bf.jpeg)

![log_fill_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/d836b367-46b8-f16f-9fce-e8c537d5add3.jpeg)

opencvによる

```python:
ddepth = cv2.CV_16S
kernel_size = 5

# Apply Gaussian Blur
blur = cv2.GaussianBlur(res,(5, 5), sigmaX = 1, sigmaY = 1)

# Apply Laplacian operator in some higher datatype
dst = cv2.Laplacian(blur, ddepth, ksize=kernel_size)
cv2.imwrite('15_log_fil_cv.jpg', dst+200)

plt.plot(dst[400])
plt.savefig("log_fill_2.jpg")
```

![15_log_fil_cv.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/935fac74-6a0b-1e4a-286d-a71f8e379980.jpeg)

![log_fill_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/757bc748-d428-a730-74b4-1afbccc23cd2.jpeg)

0交差の抽出

```python:
def Zero_crossing(image, thresh):
    z_c_image = np.zeros(image.shape)
    
    # For each pixel, count the number of positive
    # and negative pixels in the neighborhood
    
    for i in range(1, image.shape[0] - 1):
        for j in range(1, image.shape[1] - 1):
            negative_count = 0
            positive_count = 0
            neighbour = [image[i+1, j-1],image[i+1, j],image[i+1, j+1],image[i, j-1],image[i, j+1],image[i-1, j-1],image[i-1, j],image[i-1, j+1]]
            d = max(neighbour)
            e = min(neighbour)
            for h in neighbour:
                if h>0:
                    positive_count += 1
                elif h<0:
                    negative_count += 1
 
 
            # If both negative and positive values exist in 
            # the pixel neighborhood, then that pixel is a 
            # potential zero crossing
            
            z_c = ((negative_count > 0) and (positive_count > 0) and (d-e > thresh))
            
            # Change the pixel value with the maximum neighborhood
            # difference with the pixel
 
            if z_c:
                z_c_image[i, j] = 200

 
    return z_c_image

dst2 = Zero_crossing(dst1, 10)
cv2.imwrite('16_log_fil_zero.jpg', dst2)
```

![16_log_fil_zero.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/eb43f8d2-c4f2-6198-7029-84b91dbdccba.jpeg)


### *Cannyによる検出*
#### 1,平滑化
ガウシアンフィルタによる平滑化
#### 2, 縦微分、横微分フィルタによる処理
sobelフィルタを使用している。
#### 3,勾配量と勾配の方向の計算

$$
M(x,y) = \sqrt{G_x^2+G_y^2}
$$
$$
\Theta = atan2 \left(G_y/G_x \right)
$$

#### 4, 閾値処理
誤検出分を取り除くために一定の範囲のみ検出するようにする。

```python:
class Canny_hand:
    def __init__(self, image, sigma, h_thresh, l_thresh):
        self.image = image.astype(np.float32)#データ型の指定
        self.h, self.w = image.shape
        self.sigma = sigma
        self.h_thresh = h_thresh
        self.l_thresh = l_thresh
        self.sobelx = np.array([[-1, 0, 1],
                          [-2, 0, 2],
                          [-1, 0, 1]])

        self.sobely = np.array([[-1, -2, -1],
                          [0,  0, 0],
                          [1, 2, 1]])

    def do(self,):
        blur = cv2.GaussianBlur(self.image,(0, 0),  sigmaX = self.sigma, sigmaY = self.sigma)
        Gx = cv2.filter2D(blur, -1, self.sobelx)
        Gy = cv2.filter2D(blur, -1, self.sobely)
        G_value = np.sqrt(np.square(Gy) + np.square(Gx)) 
        G_angle = np.arctan2(Gy, Gx)
        G_angle_set = self.set_angle(G_angle)
        G_max_group = self.extract_max(G_value, G_angle_set)
        result = self.thresh_hold(G_max_group, min=self.l_thresh, max=self.h_thresh)

        return result

    def set_angle(self, G_angle):
        pai = math.pi
        
        G_angle[np.where((G_angle >= -pai/8) & (G_angle < pai/8))] = 0
        G_angle[np.where((G_angle >= pai/8) & (G_angle < 3*pai/8))] = 45
        G_angle[np.where((G_angle >= 3*pai/8) & (G_angle < 5*pai/8))] = 90
        G_angle[np.where((G_angle >= 5*pai/8) & (G_angle < 7*pai/8))] = 135
        G_angle[np.where((G_angle >= 7*pai/8) & (G_angle < -7*pai/8))] = 180
        G_angle[np.where((G_angle >= -7*pai/8) & (G_angle < -5*pai/8))] = 225
        G_angle[np.where((G_angle >= -5*pai/8) & (G_angle < -3*pai/8))] = 270
        G_angle[np.where((G_angle >= -3*pai/8) & (G_angle < -pai/8))] = 315

        return G_angle

    def extract_max(self, G_value, G_angle_set):
        result = G_value.copy()

        for y in range(1, self.h - 1):
            for x in range(1, self.w - 1):

                if G_angle_set[y][x] == 0:
                    if (G_value[y][x] < G_value[y][x+1]):
                        result[y][x] = 0
                elif G_angle_set[y][x] == 45:
                    if (G_value[y][x] < G_value[y+1][x+1]):
                        result[y][x] = 0
                elif G_angle_set[y][x] == 90:
                    if (G_value[y][x] < G_value[y+1][x]):
                        result[y][x] = 0
                elif G_angle_set[y][x] == 135:
                    if (G_value[y][x] < G_value[y+1][x-1]):
                        result[y][x] = 0
                elif G_angle_set[y][x] == 180:
                    if (G_value[y][x] < G_value[y][x-1]):
                        result[y][x] = 0
                elif G_angle_set[y][x] == 225:
                    if (G_value[y][x] < G_value[y-1][x-1]):
                        result[y][x] = 0
                elif G_angle_set[y][x] == 270:
                    if (G_value[y][x] < G_value[y-1][x]):
                        result[y][x] = 0
                elif G_angle_set[y][x] == 315:
                    if (G_value[y][x] < G_value[y-1][x+1]):
                        result[y][x] = 0

        return result

    def thresh_hold(self, in_put, min=75, max=150, d=1):

        for y in range(0, self.h):
            for x in range(0, self.w):

                if in_put[y][x] >= max:
                    in_put[y][x] = 255

                elif in_put[y][x] < min:
                    in_put[y][x] = 0

                else:
                    if np.max(in_put[y-d:y+d+1, x-d:x+d+1]) >= max:
                        in_put[y][x] = 255
                    else:
                        in_put[y][x] = 0

        return in_put

canny = Canny_hand(res, 0.3,100, 200 )
can_do = canny.do()
cv2.imwrite('17_canny_hand.jpg', can_do)
```

![17_canny_hand.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/70ee434b-3a8d-2c21-3948-9a148cc13b22.jpeg)

#### opencvによる

```python:
edges = cv2.Canny(res,0.3,  100, 200)
cv2.imwrite('17_canny.jpg', dst2)
```

![17_canny.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/374eefd6-a26a-fc5f-1eaa-4fb90df22318.jpeg)


### *鮮鋭化*

$$
元の画像　＋　（元の画像　- 平滑化後の画像）×定数
$$

```python:
# Apply Gaussian Blur
blur = cv2.GaussianBlur(res,(0, 0), sigmaX = 3, sigmaY = 3)
cv2.imwrite('shap_blur.jpg', blur)

diff = res - blur
cv2.imwrite('18_shape_diff.jpg', diff)

shapen = res + diff*3
cv2.imwrite('19_shape_shapen.jpg', shapen)
```

![shap_blur.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/620dad8b-8c80-00e2-03d8-37f5e1fb6b23.jpeg)
![18_shape_diff.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/ee6318b5-2045-1bab-0081-532a18f8d696.jpeg)
![19_shape_shapen.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/8293289d-3166-b5a7-5937-a0deaa6fe409.jpeg)


### *局所平均化*
比較的エッジを残したまま平均化できる。
例として、以下の９パターンを計算する。
この中で最も分散が少ない物を選ぶ。


![aria_ave.jpg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/fb4c4466-7877-2ed8-8d2a-4e3fb7f807d4.png)


```python:
def vari(data):
    count = len(data)
    av = sum(data)/count
    void = []
    total = 0
    for d in data:
        diff = (d - av)**2
        total += diff
    pvariance = total/count

    return pvariance


def selective_ave(image):
    av_image = np.zeros(image.shape)
    
    for i in range(2, image.shape[0] - 2):
        for j in range(2, image.shape[1] - 2):

            center_neighbour = [image[i+1, j-1],image[i+1, j],image[i+1, j+1],image[i, j-1],
                image[i, j+1],image[i-1, j-1],image[i-1, j],image[i-1, j+1]]

            l_u_neighbour = [image[i-2, j-2],image[i-2, j-1],image[i-1, j-2],image[i-1, j-1],
                image[i-1, j],image[i, j-1],image[i, j]]
            
            up_neighbour = [image[i-2, j-1],image[i-2, j],image[i-2, j+1],
                image[i-1, j-1],image[i-1, j],image[i-1, j+1],image[i, j]]

            r_u_neighbour = [image[i+2, j+2],image[i+2, j+1],image[i+1, j+2],image[i+1, j+1],
                image[i+1, j],image[i, j+1],image[i, j]]

            right_neighbour = [image[i-1, j+2],image[i, j+2],image[i+1, j+2],
                image[i-1, j+1],image[i, j+1],image[i+1, j+1],image[i, j]]
            
            left_neighbour = [image[i-1, j-2],image[i, j-2],image[i+1, j-2],
                image[i-1, j-1],image[i, j-1],image[i+1, j-1],image[i, j]]

            l_d_neighbour = [image[i+2, j-2],image[i+2, j-1],image[i+1, j-2],
                image[i+1, j-1],image[i+1, j],image[i, j-1],image[i, j]]

            down_neighbour = [image[i+2, j-1],image[i+2, j],image[i+2, j+1],
                image[i+1, j-1],image[i+1, j],image[i+1, j+1],image[i, j]]

            r_d_neighbour = [image[i+2, j+2],image[i+2, j+1],image[i+1, j+2],
                image[i+1, j+1],image[i+1, j],image[i, j+1],image[i, j]]
            
    
            nb_list = [center_neighbour, l_u_neighbour, up_neighbour,
                r_u_neighbour, right_neighbour, left_neighbour,
                l_d_neighbour, down_neighbour, r_d_neighbour]

            number = 0
            vari_max = 0

            for nb in nb_list:
                pre_vari = vari(nb)
                
                if vari_max < pre_vari:
                    vari_max = pre_vari
                    max_index = number
                
                number += 1
            
            result = statistics.mean(nb_list[max_index])

            av_image[i, j] = result
 
    return av_image

select_av =  selective_ave(res)
cv2.imwrite('20_select_av.jpg', select_av)
```
![20_select_av.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/93f13adf-d359-e989-9df4-51fb24a312e9.jpeg)


### *バイラテラルフィルタ（二つのガウス分布を使ったフィルタ)*
ガウスフィルタによるの平滑化の実施。
注目点との距離に応じて重みを分布させている（遠いほど軽くなる）
ここまでは通常のガウスフィルタと同じ。
ここでは、さらに、画素間の画素値の差にガウス分布の重みつけを採用している。
つまり（差が大きい（画素値が遠い）ほど、平滑化する重みが軽くなるので、エッジ部（画素値の差が大きい）は平滑化されにくくなり、
結果的にエッジが保存される。

```python:
bi = cv2.bilateralFilter(res,15, 20, 20)
cv2.imwrite('21_bilateral_fil.jpg', bi)
```

![21_bilateral_fil.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/31839a9d-6ee4-16b7-483e-7e45fc9a70d2.jpeg)


### *ノンローカルミーンフィルタ*
バイラテラルフィルタが画素値の差に対して重み付をしているのに対して、
ここでは、他の小領域（周囲の画像領域）との類似度の差に対して、重み付をしている。
つまり周辺に似たような画像が多いと平滑化が重くなり、似てない場合は平滑化が軽くなる。

```python:
dst = cv2.fastNlMeansDenoising(res, None, 10, 7, 21)
cv2.imwrite('22_non_l_m_fil.jpg', dst)
```

![22_non_l_m_fil.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/a2befb19-b883-b5e8-2c63-39ae8cd80f62.jpeg)


### *メディアンフィルタ*
フィルタ処理の際に、注目領域の中央値を出力としている。
中央値とは、M×N画素の画像に対して、画素値の小さい画素から数えて、（M×N＋１）/2
番目の画素の値のこと。
このフィルタはゴマを振りかけたようなスパイク状のノイズに有効である。

```python:
median = cv2.medianBlur(noicy_image, 3)
cv2.imwrite('24_median_fil.jpg', median)
```
ビフォー
![23_spike_noise.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/4de87fce-02dc-d12d-d46f-84fa61eb179e.jpeg)

アフター
![24_median_fil.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538540/e50240c9-13f9-26ce-066d-a6d4e07a5f05.jpeg)



参考にしたサイト
http://optie.hatenablog.com/entry/2018/03/21/185647
https://theailearner.com/2019/05/25/laplacian-of-gaussian-log/
https://algorithm.joho.info/programming/python/opencv-canny-edge-detecte-py/

以上