画像処理100本ノック①

<a href="https://github.com/yoyoyo-yo/Gasyori100knock">画像処理100本ノック</a>に最近取り組み始めたので実装例を一部。今回はQ1-10の中からいくつかピックアップしました。

## Q6 減色処理
処理の式は以下

```math
val = \left\{
\begin{array}{ll}
32 & (0 \leq x < 64) \\
96 & (64 \leq x < 128) \\ 
96 & (128 \leq x < 192) \\ 
96 & (192 \leq x < 256) \\ 
\end{array}
\right.
```



```python:
def reduce(pix):
    if 0 <= pix < 64:
        return 32
    elif 64 <= pix < 128:
        return 96
    elif 128 <= pix <192:
        return 160
    else:
        return 224

def reduce_color(img):
    return np.vectorize(reduce)(img)
```



## Q7 Average Pooling

```python:
def average_pool(img, f=8):
    height, width, channel = img.shape
    n = int(width/f)
    out = np.empty(shape=(n, n, channel))
    
    print(out.shape)
    
    
    for h in range(n):
        for w in range(n):
            for c in range(channel):
                vert_start = h * f
                vert_end = vert_start + f
                horiz_start = w * f
                horiz_end = horiz_start + f
                
                val = np.sum(img[vert_start:vert_end, horiz_start:horiz_end, c]) / f**2
                
                out[h, w, c] = val
    
    return out.astype(np.int32)
```


## Q8: Max Pooling

```python:
def max_pool(img, f=8):
    height, width, channel = img.shape
    n = int(width/f)
    out = np.empty(shape=(n, n, channel))
    
    print(out.shape)
    
    
    for h in range(n):
        for w in range(n):
            for c in range(channel):
                vert_start = h * f
                vert_end = vert_start + f
                horiz_start = w * f
                horiz_end = horiz_start + f
                
                val = np.max(img[vert_start:vert_end, horiz_start:horiz_end, c])
                
                out[h, w, c] = val
    
    return out.astype(np.int32)
```


## Q9 ガウシアンフィルタ
やることは単純と思っいたら意外に添字のミスでてこずってしまった。。。カーネルの実装は解答例を参考にしました。

```python:
def gaussian_filter(img, size=3, sigma=1.3):
    height, width, channel = img.shape
    
    # padding
    pad = size//2
    img_pad = img.copy()
    img_pad = np.pad(img_pad, [(pad, pad), (pad, pad), (0, 0)], 'constant', constant_values=(0,0))
    
    # PREPARE KERNEL
    K = np.zeros(shape=(size, size), dtype=np.float32)
    for x in range(-pad, -pad+size):
        for y in range(-pad, -pad+size):
            K[y+pad, x+pad] = (1/((2 * np.pi * sigma * sigma)) * np.exp(-(x**2+y**2) / (2*(sigma**2))))
    K /= K.sum()
    
    tmp = img_pad.copy()
    
    for h in range(height):
        for w in range(width):
            for c in range(channel):
                tmp[h+pad, w+pad, c] = np.sum(K*img_pad[h:h+size,  w:w+size, c])
    
    tmp = tmp[pad:pad+height, pad:pad+width, :].astype(np.int32)
                
    return tmp
```

## Q10 メディアンフィルタ


```python:
def median_filter(img, size=3):
    height, width, channel = img.shape
    out = img.copy()
    
    # padding 
    pad = size//2
    out = img.copy()
    out = np.pad(out, [(pad, pad), (pad, pad), (0, 0)], 'constant', constant_values=(0,0))
    
    assert out.shape == (img.shape[0]+2*pad, img.shape[1]+2*pad, img.shape[2])
    
    # Apply filter
    tmp = out.copy()
    
    for h in range(height):
        for w in range(width):
            for c in range(channel):
                out[h+pad, w+pad, c] = np.median(tmp[h:h+size, w:w+size, c])
    
    out = out[pad:H, pad:W, :].astype(np.uint8)
    
    return out
```