728x90

2023-11-24 57th Class

Filter & Window

#️⃣ Sobel Filter

Pasted image 20231124101204.png

소벨 필터(Sobel Filter) 란 CNN의 커널에 해당하는 것으로 이미지의 특성을 추출하기 위한 행렬을 의미함
소벨 필터는 X 필터와, Y 필터 2종류가 있음

위의 예시를 통해 살펴보면

  • 소벨 필터는 홀수 x 홀수 형태의 행렬
  • 위의 예시에서는 3x3 행렬
  • 필터의 원소는 2, 1, 0, -1, -2로 구성 됨
  • 좌우 대칭 또는 상하 대칭
  • x 필터는 상하로 대칭이고, y 필터는 좌우로 대칭

#️⃣ Window

Pasted image 20231124101238.png

  • 윈도우는 데이터에서 필터 크기만큼을 추출한 것을 의미
  • 윈도우 사이즈는 홀수이기 때문에
  • 위와 같이 윈도우 내에서 세로방향 혹은 가로방향으로
  • 0에서 1로 혹은 1에서 0으로 값이 변화하는 양상을 띔

#️⃣ Sobel Filter의 동작 원리

  • Sobel Filter는 2차원 Correlation 연산을 수행함
  • 2D Correlation 연산: 원소끼리 곱한 후 더해주는 것

Sobel X 예시

Pasted image 20231124101629.png

  • window에 따라 correlation의 값이 달라짐
  • 비슷할 수록 높은 값이 나오고, 다를 수록 낮은 값이 출력됨
  • 반대 특성을 지나면 작은 음수, 자신과 상관 없는 경우에는 0 출력
  • Sobel X 필터는 수평방향으로 밝기 변화가 있을 때 반응하는 filter
  • edge detection

#️⃣ Sobel Filter

full code

import numpy as np  
import matplotlib.pyplot as plt  
from PIL import Image  
import os  
  
def check_pattern_image_w_nptile():  
    white_patch = 255 * np.ones(shape=(10, 10))  
    black_patch = np.zeros(shape=(10, 10))  
  
    img1 = np.hstack([white_patch, black_patch])  
    img2 = np.hstack([black_patch, white_patch])  
    img = np.vstack([img1, img2])  
  
    tile = np.tile(img, reps=[2, 2])  
    return tile  
  
  
def visualize(names, if_vmax=False, *args):  
    fig, axes = plt.subplots(ncols=3, figsize=(3 * len(args), 3))  
    for i, data in enumerate(args):  
        if not if_vmax:  
            axes[i].imshow(data, cmap='gray')  
        else:  
            axes[i].imshow(data, cmap='gray', vmax=255, vmin=0)  
        axes[i].set_title(names[i])  
        axes[i].tick_params(left=False, labelleft=False, bottom=False, labelbottom=False)  
  
    fig.tight_layout()  
    plt.show()  
  
  
def get_data(data=None):  
    if data is None:  
        data = check_pattern_image_w_nptile()  
  
    x_filter = np.array([  
        [-1, 0, 1],  
        [-2, 0, 2],  
        [-1, 0, 1]  
    ])  # 상하 대칭  
  
    y_filter = np.array([  
        [1, 2, 1],  
        [0, 0, 0],  
        [-1, -2, -1]  
    ])  # 좌우 대칭  
  
    return data, x_filter, y_filter  
  
  
def two_dim_correlation(data, filter_):  
    window_size = 3  
    height, width = data.shape  
    n_window_height = height - window_size + 1  
    n_window_width = width - window_size + 1  
  
    hadamard_product = lambda row, col: data[row:row + window_size, col:col + window_size] * filter_  
    extracted = np.array(  
        [[hadamard_product(row, col) for col in range(n_window_width)] for row in range(n_window_height)])  
    correlated = np.sum(extracted, axis=(2, 3))  
  
    return correlated  
  
  
def sobel_filtering1():  
    data, x_filter, y_filter = get_data()  
    x_filtered = two_dim_correlation(data, x_filter)  
    y_filtered = two_dim_correlation(data, y_filter)  
  
    visualize(["data", "x_filtered", "y_filtered"], False, data, x_filtered, y_filtered)  
  
  
def sobel_filtering2(path):  
    img = Image.open(path)  
    new_path = path.replace(".jpg", "-gray.jpg")  
    img_gray = img.convert("L")  
    if not os.path.isfile(new_path):  
        img_gray.save(new_path)  
  
    img_array = np.array(img_gray)  
    data, x_filter, y_filter = get_data(img_array)  
    x_filtered = two_dim_correlation(data, x_filter)  
    y_filtered = two_dim_correlation(data, y_filter)  
  
    visualize(["data", "x_filtered", "y_filtered"], True, data, x_filtered, y_filtered)  
  
  
if __name__ == '__main__':  
    sobel_filtering1()  
    sobel_filtering2(path="data/winter-3317660_640.jpg")

[1] 체크 패턴 Sobel Filtering

231124-1 check_pattern.png

[2] 흑백 이미지 Sobel Filtering

231124-2 gray_img.png


CNN

#️⃣ Convolutional Layer

CNN은 Convolutional Layer와 fully connected layer로 이루어진 딥러닝 모델
CNN은 특징 추출의 단계와, 분류 단계 총 2단계를 통해 데이터를 분류함

  • feature extractor: 여러 convolutional layer를 거치는 단계
  • classifier: convolutional layer 이후에 최종 분류하는 단계

#️⃣ Padding

  • 초록색은 output image
  • 파란색은 input image
  • 점선은 padding

패딩은 이미지를 둘러싸는 것으로 0 혹은 1의 값으로 채우게 됨

#️⃣ Kernel=Filter

Convolution (합성곱) filter는 Kernel이라고 함
이 컨볼루셔널 필터는 데이터에서 그 크기만큼 밀고 가면서 특정 패턴을 추출해 이미지를 그림
앞서 sobel filtering에서 X filter는 세로선, Y filter는 가로선의 edge detection(테두리 검출)을 하는 것처럼
kernel이 대각선, 질감, 모양 등을 검출해서 이미지를 그림

Pasted image 20231124122312.png

커널 사이즈, 패딩 사이즈 = (3, 1)

커널사이즈가 3일때는 패딩이 1로 설정하면 원본 이미지의 사이즈가 유지됨

패딩 적용 후 특성추출한 결과 (H’=세로, W’=가로)

H’ = H + 2P - F + 1
W’ = W + 2P - F + 1

6x6 데이터인경우 원본을 유지하는 패딩사이즈를 구하려면
6 = 6 + 2p - 3 + 1
2p = 2
p = 1
따라서 패딩사이즈가 1인경우 원본사이즈가 유지됨

Example

padding size가 1, input image size가 (1080, 1920)의 Full HD image이고,
filter size가 (3, 3)일때 convolution의 output image size는?e

답: (1080, 1920)

#️⃣ Channel

(6, 6, 3), (3, 3, 3)
마지막이 Channel

Input image의 channel 수는 Kernel 의 channel 수와 동일

Pasted image 20231124123412.png

def conv_test():  
    input_data = np.arange(6*6*3).reshape((6, 6, 3))  
    # print(input_data)  
    filter_data = np.arange(3*3*3).reshape((3, 3, 3))  
    print(filter_data)
input_data = 
[[[  0   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]
  [ 69  70  71]]

 [[ 72  73  74]
  [ 75  76  77]
  [ 78  79  80]
  [ 81  82  83]
  [ 84  85  86]
  [ 87  88  89]]

 [[ 90  91  92]
  [ 93  94  95]
  [ 96  97  98]
  [ 99 100 101]
  [102 103 104]
  [105 106 107]]]

(6,6,3)

filter_data = 
[[[ 0  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]]]
  
(3,3,3)

Filter는 뉴런, filter의 각각의 값들은 weight에 해당함
output channel 개수는 filter의 개수와 같음

#️⃣ Pooling

Pooling은 이미지의 사이즈를 줄여주는 것으로, Max Pooling과 Avg Pooling이 있음

Max Pooling은 pooling kernel에서 가장 큰 값이 추출되고,
Average Pooling은 pooling kernel의 값들의 평균 값이 추출됨

#️⃣ Stride

Stride는 다음 window를 extract할 때, 몇 칸을 뛸 지 결정하는 값 = 보폭

수식내부의 [] 처럼 생긴 것은 floor임 -> round down해서 정수를 만들어줌

  • H: height
  • W: weight
  • P: padding
  • F: filter
  • S: Stride

Input: or
Output: or

#️⃣ CNN 레이어 계산

[1]

  • input의 채널 수 = kernel의 채널 수
  • kernel의 채널 수 = output의 채널 수
  • 첫 번째 conv layer의 커널 수 = n1
  • output channel 수 = n1개
  • 두 번째 conv layer의 Input 채널 수 = n1개
  • 두 번째 conv layer의 Kernel의 채널 수 = n1개
  • 두 번째 conv layer의 Kernel의 개수 = n2개
  • 두 번째 conv layer의 output 채널 수 = n2개

[2]

Num conv1 pool1 conv2 pool2 conv3(=fc1) fc3 fc3
input 32x32x1 28x28x6 14x14x6 10x10x16 5x5x16 120 84
filter 5x5, 6개 2x2 5x5x6, 16개 2x2 5x5x16, 120개 - -
padding 0 0 0 0 0 - -
stride 1 2 1 2 1 - -
output 28x28x6 14x14x6 10x10x16 5x5x16 1x1x120 84 10
  • 1번째 레이어 커널의 채널수 : 1개
  • 1번째 레이어의 out 채널: 6개
  • 풀링 후 크기 절반이 됨
  • 2번째 레이어 채널 수: 6개
  • 2번째 레이어의 커널의 채널수: 6개
  • 2번째 레이어의 커널의 개수: 16개
  • 2번째 레이어의 out 채널: 16개

CNN 시각화 참고 사이트
adamharley.com/nn_vis/cnn/3d.html
https://adamharley.com/nn_vis/cnn/2d.html


Convolutional Neural Networks

#️⃣ nn.Conv2d in Pytorch

Pytorch의 Conv layer를 위한 텐서의 shape 표기 순서는 아래와 같음

(B, C, H, W)

  • B: Batch size
  • C: Channel
  • H: Height
  • W: Width

H’ = (H +2P -F + 1) / S
W’ = (W + 2P -F + 1) / S

code

def run_conv1():  
    H, W = 100, 150  
    input_tensor = torch.randn(size=(1, H, W))  
  
    conv = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3)  
    output_tensor = conv(input_tensor)  
    print(output_tensor.shape)
    
'''
torch.Size([1, 98, 148])
'''

input_tensor = (1, 100, 150)
output_tensor = (1, 98, 148) -> Height (100 +0 -3 +1) /1 , Width (150 +0 -3 +1) /1

(kernel_size=3, padding=1)은 원본을 유지하는 세트임!! 꼭 암기

input이 (1, 100, 150)이라면

(1)
conv = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, padding=1)
output_tensor = conv(input_tensor)

output_tensor shape: (1, 100, 150)

(2)
conv = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, padding=1, stride=2)
output_tensor = conv(input_tensor)

output_tensor shape: (1, 50, 75)

(3)
conv = nn.Conv2d(in_channels=1, out_channels=10, kernel_size=3, padding=1, stride=2)
output_tensor = conv(input_tensor)

output_tensor shape: (10, 50, 75)

(4)
conv = nn.Conv2d(in_channels=1, out_channels=20, kernel_size=3, padding=1, stride=2)
output_tensor = conv(input_tensor)

output_tensor shape: (20, 50, 75)

반응형