파이썬 강의/openCV

파이썬 openCV 21. 노이즈 제거 : 나가오-마츠야마 필터(Nagao-Matsuyama filter)

마리사라 2020. 12. 13. 11:17
반응형

파이썬 openCV 21번째 강의는 나가오-마츠야마 필터(Nagao-Matsuyama filter)입니다. 나가오-마츠야마 필터도 쿠와하라(Kuwahara) 필터처럼 이름이 잘 알려지지 않은 필터입니다. 이 필터 또한 국내에서는 잘 알려지지 않아서 해외 사이트를 많이 참고해서 썼습니다.


0. 나가오-마츠야마 필터(Nagao-Matsuyama filter)

나가오-마츠야마 필터(Nagao-Matsuyama filter)는 다른말로는 모서리 보존 스무딩 필터(Edge-Preserving Smoothing Filters)라고도 합니다. Kuwahara 필터와 마찬가지로 모서리를 보존하면서 스무딩을 진행하는 필터입니다. 이 역시 가우시안 잡음을 잡는 데 사용하는 필터로 사용됩니다.

 

Nagao-Matsuyama는 Kuwahara 필터와 비슷하면서 다릅니다. Kuwahara와 Nagao-Matsuyama는 둘 다 일정 영역의 밝기의 분산을 기준으로, 그 영역의 평균값이 출력 영상의 화소 값이 되는 것은 같습니다.

 

하지만 Kuwahara 필터가 5 x 5의 공간을 4개의 영역으로 나누는 것에 비해, Nagao-Matsuyama는 5 x 5의 공간을 기준으로 8개의 영역으로 나눕니다.

Nagao-Matsuyama 필터의 영역

그렇기에 Kuwahara 필터보다 연산량이 많아서 처리속도는 느리지만 Kuwahara 필터보다는 원본 영상에 가까운 영상을 얻을 수 있습니다.


1. openCV에서의 Nagao-Matsuyama

이제 코드를 확인해 보겠습니다.

 

img = cv2.imread('lenna.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
height, width = gray.shape

img_noise = np.zeros_like(gray)
for i in range(height):
    for j in range(width):
        make_noise = np.random.normal()
        set_noise = 10 * make_noise
        img_noise[i][j] = gray[i][j] + set_noise

out = np.zeros((height + 4, width + 4), dtype=np.float)
out[2: 2 + height, 2: 2 + width] = img_noise.copy().astype(np.float)

이 부분까지는 이전 강의인 Kuwahara 필터와 중복되는 부분입니다. 이미지를 불러오고, 그레이 스케일로 만든 후, 노이즈를 생성하고, 제로 패딩을 하는 부분입니다.

 

for i in range(height):
    for j in range(width):
        point1 = np.ravel(out[i + 1:i + 4, j:j + 2])
        point1 = np.append(point1, out[i + 2, j + 2])

        point2 = np.ravel(out[i + 3:i + 5, j + 1:j + 4])
        point2 = np.append(point2, out[i + 2, j + 2])

        point3 = np.ravel(out[i + 1:i + 4, j + 3:j + 5])
        point3 = np.append(point3, out[i + 2, j + 2])

        point4 = np.ravel(out[i:i + 2, j + 1:j + 4])
        point4 = np.append(point4, out[i + 2, j + 2])

        point5 = np.ravel(out[i:i + 2, j:j + 2])
        point5 = np.append(point5, out[i + 2, j + 1])
        point5 = np.append(point5, out[i + 1, j + 2])
        point5 = np.append(point5, out[i + 2, j + 2])

        point6 = np.ravel(out[i + 3:i + 5, j:j + 2])
        point6 = np.append(point6, out[i + 2, j + 1])
        point6 = np.append(point6, out[i + 3, j + 2])
        point6 = np.append(point6, out[i + 2, j + 2])

        point7 = np.ravel(out[i + 3:i + 5, j + 3:j + 5])
        point7 = np.append(point7, out[i + 2, j + 3])
        point7 = np.append(point7, out[i + 3, j + 2])
        point7 = np.append(point7, out[i + 2, j + 2])

        point8 = np.ravel(out[i:i + 2, j + 3:j + 5])
        point8 = np.append(point8, out[i + 1, j + 2])
        point8 = np.append(point8, out[i + 2, j + 3])
        point8 = np.append(point8, out[i + 2, j + 2])

        if min(np.var(point1), np.var(point2), np.var(point3), np.var(point4), np.var(point5), np.var(point6), np.var(point7), np.var(point8)) == np.var(point1):
            out[2 + i, 2 + j] = np.mean(point1)
        elif min(np.var(point1), np.var(point2), np.var(point3), np.var(point4), np.var(point5), np.var(point6), np.var(point7), np.var(point8)) == np.var(point2):
            out[2 + i, 2 + j] = np.mean(point2)
        elif min(np.var(point1), np.var(point2), np.var(point3), np.var(point4), np.var(point5), np.var(point6), np.var(point7), np.var(point8)) == np.var(point3):
            out[2 + i, 2 + j] = np.mean(point3)
        elif min(np.var(point1), np.var(point2), np.var(point3), np.var(point4), np.var(point5), np.var(point6), np.var(point7), np.var(point8)) == np.var(point4):
            out[2 + i, 2 + j] = np.mean(point4)
        elif min(np.var(point1), np.var(point2), np.var(point3), np.var(point4), np.var(point5), np.var(point6), np.var(point7), np.var(point8)) == np.var(point5):
            out[2 + i, 2 + j] = np.mean(point5)
        elif min(np.var(point1), np.var(point2), np.var(point3), np.var(point4), np.var(point5), np.var(point6), np.var(point7), np.var(point8)) == np.var(point6):
            out[2 + i, 2 + j] = np.mean(point6)
        elif min(np.var(point1), np.var(point2), np.var(point3), np.var(point4), np.var(point5), np.var(point6), np.var(point7), np.var(point8)) == np.var(point7):
            out[2 + i, 2 + j] = np.mean(point7)
        elif min(np.var(point1), np.var(point2), np.var(point3), np.var(point4), np.var(point5), np.var(point6), np.var(point7), np.var(point8)) == np.var(point8):
            out[2 + i, 2 + j] = np.mean(point8)

이제 중첩 for문입니다. 각각의 포인트에 계산할 화소를 담아 줍니다. 포인트에 담는 부분은 제가 임의로 만든 부분이라 코드를 더 줄이실 수 있으면 변형해서 쓰셔도 됩니다.

 

이후 Kuwahara 필터와 마찬가지로 각각의 분산의 최솟값이 해당하는 영역의 평균값을 출력 화소에 저장하게 됩니다. 이때 Nagao-Matsuyama 필터는 Kuwahara 필터의 연산의 두배 가량 되므로, 시간이 매우 오래 걸립니다. 저는 결과가 나올 때까지 약 5분의 시간이 걸렸으므로, 인내를 가지고 기다리셔야 합니다.

 

out = out[2:2 + height, 2:2 + width].astype(np.uint8)

마지막으로 제로 패딩 된 부분을 없애고, 정수형으로 변환하면 끝입니다.

 

 

이제 결과 영상을 확인해보겠습니다.

원본 영상

 

노이즈 영상
Nagao-Matsuyama 필터 영상

 

결과는 Kuwahara와 비슷하지만 조금 더 원본 영상에 가까운 영상이 나왔습니다.


2. 마치며

Nagao-Matsuyama 필터는 단순히 노이즈를 제거하는 작업뿐 아니라 영상 분할의 전처리 과정에도 적합한 필터입니다. 이는 지금은 다루지 않을 예정이며, 나중에 영상 분할을 설명하게 될 때 언급하도록 하겠습니다.

 

def nagao():
    img = cv2.imread('lenna.png')
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    height, width = gray.shape

    img_noise = np.zeros_like(gray)
    for i in range(height):
        for j in range(width):
            make_noise = np.random.normal()
            set_noise = 10 * make_noise
            img_noise[i][j] = gray[i][j] + set_noise

    out = np.zeros((height + 4, width + 4), dtype=np.float)
    out[2: 2 + height, 2: 2 + width] = img_noise.copy().astype(np.float)

    for i in range(height):
        for j in range(width):
            point1 = np.ravel(out[i + 1:i + 4, j:j + 2])
            point1 = np.append(point1, out[i + 2, j + 2])

            point2 = np.ravel(out[i + 3:i + 5, j + 1:j + 4])
            point2 = np.append(point2, out[i + 2, j + 2])

            point3 = np.ravel(out[i + 1:i + 4, j + 3:j + 5])
            point3 = np.append(point3, out[i + 2, j + 2])

            point4 = np.ravel(out[i:i + 2, j + 1:j + 4])
            point4 = np.append(point4, out[i + 2, j + 2])

            point5 = np.ravel(out[i:i + 2, j:j + 2])
            point5 = np.append(point5, out[i + 2, j + 1])
            point5 = np.append(point5, out[i + 1, j + 2])
            point5 = np.append(point5, out[i + 2, j + 2])

            point6 = np.ravel(out[i + 3:i + 5, j:j + 2])
            point6 = np.append(point6, out[i + 2, j + 1])
            point6 = np.append(point6, out[i + 3, j + 2])
            point6 = np.append(point6, out[i + 2, j + 2])

            point7 = np.ravel(out[i + 3:i + 5, j + 3:j + 5])
            point7 = np.append(point7, out[i + 2, j + 3])
            point7 = np.append(point7, out[i + 3, j + 2])
            point7 = np.append(point7, out[i + 2, j + 2])

            point8 = np.ravel(out[i:i + 2, j + 3:j + 5])
            point8 = np.append(point8, out[i + 1, j + 2])
            point8 = np.append(point8, out[i + 2, j + 3])
            point8 = np.append(point8, out[i + 2, j + 2])

            if min(np.var(point1), np.var(point2), np.var(point3), np.var(point4), np.var(point5), np.var(point6), np.var(point7), np.var(point8)) == np.var(point1):
                out[2 + i, 2 + j] = np.mean(point1)
            elif min(np.var(point1), np.var(point2), np.var(point3), np.var(point4), np.var(point5), np.var(point6), np.var(point7), np.var(point8)) == np.var(point2):
                out[2 + i, 2 + j] = np.mean(point2)
            elif min(np.var(point1), np.var(point2), np.var(point3), np.var(point4), np.var(point5), np.var(point6), np.var(point7), np.var(point8)) == np.var(point3):
                out[2 + i, 2 + j] = np.mean(point3)
            elif min(np.var(point1), np.var(point2), np.var(point3), np.var(point4), np.var(point5), np.var(point6), np.var(point7), np.var(point8)) == np.var(point4):
                out[2 + i, 2 + j] = np.mean(point4)
            elif min(np.var(point1), np.var(point2), np.var(point3), np.var(point4), np.var(point5), np.var(point6), np.var(point7), np.var(point8)) == np.var(point5):
                out[2 + i, 2 + j] = np.mean(point5)
            elif min(np.var(point1), np.var(point2), np.var(point3), np.var(point4), np.var(point5), np.var(point6), np.var(point7), np.var(point8)) == np.var(point6):
                out[2 + i, 2 + j] = np.mean(point6)
            elif min(np.var(point1), np.var(point2), np.var(point3), np.var(point4), np.var(point5), np.var(point6), np.var(point7), np.var(point8)) == np.var(point7):
                out[2 + i, 2 + j] = np.mean(point7)
            elif min(np.var(point1), np.var(point2), np.var(point3), np.var(point4), np.var(point5), np.var(point6), np.var(point7), np.var(point8)) == np.var(point8):
                out[2 + i, 2 + j] = np.mean(point8)

    out = out[2:2 + height, 2:2 + width].astype(np.uint8)
    cv2.imshow('original', gray)
    cv2.imshow('noise', img_noise)
    cv2.imshow('out', out)
    cv2.waitKey(0)
반응형