파이썬 강의/openCV

파이썬 openCV 23. 노이즈 제거 : 최댓값(Maximum) / 최솟값(Minimum) 필터

마리사라 2020. 12. 15. 15:57
반응형

파이썬 openCV 23번째 강의는 최댓값(Maximum) / 최솟값(Minimum) 필터입니다. 이번 강의도 salt-and-pepper를 제거하는 필터입니다. 혹시나 salt-and-pepper가 무엇인지 모르시는 분들은 이전 강의를 참조하시면 좋을 것 같습니다.

 

2020/12/14 - [파이썬/openCV] - 파이썬 openCV 22. 노이즈 제거 : 메디안(median)과 하이브리드 메디안(hybrid median)

 

파이썬 openCV 22. 노이즈 제거 : 메디안(median)과 하이브리드 메디안(hybrid median)

파이썬 openCV 22번째 강의는 노이즈 제거의 방법인 median 필터와 hybrid median 필터입니다. 이전까지의 노이즈는 가우시안 노이즈를 제거하는 방법이었다면, 이번 강의와 다음 강의는 솔트 앤 페퍼

marisara.tistory.com


0. 최댓값(Maximum) / 최솟값(Minimum) 필터?

최댓값/최솟값 필터는 이름에서 알수 있듯이 일정 범위 내에서 최댓값 또는 최솟값을 출력 화소로 하는 필터입니다. 이 필터는 노이즈 제거 뿐 아니라 다른 곳에도 쓰이는 필터이므로 조금 알아두셨으면 합니다.

 

1 2 3
4 5 6
7 8 9

3 x 3 마스크를 예시로 들어 보겠습니다. 이때 최댓값 / 최솟값 필터를 통과하게 되면 출력 화소는 각각 9와 1이 되게 됩니다.

 

1 2 0
4 5 6
255 8 9

이번에는 salt-and-pepper가 첨가된 영상을 예시로 들어 보겠습니다. 위에서 3은 0(pepper)으로, 7은 255(salt)로 변하는 salt-and-pepper 노이즈가 첨가된 마스크입니다. 이제 이 마스크를 최댓값 / 최솟값 필터에 통과시키게 되면 출력 화소는 각각 255와 0이 되게 됩니다.

 

이렇게만 보면 salt로 변한 화소가 포함된 마스크는 최댓값 필터를 통과하면 255로, pepper로 변한 화소가 포함된 마스크는 최솟값 필터를 통과하면 0으로 변하니 극단적인 변화가 있을 것으로 예상할 수 있습니다.

 

100 120 140
70 180 220
150 30 40

하지만 일반적인 이미지는 1 ~ 10까지의 값이 아닌 0 ~ 255까지의 값을 갖습니다. 그래서 위와 같은 마스크를 예시로 보게 되면 최댓값 / 최솟값은 각각 220과 30으로, 255와 0과는 대략 30 정도의 차이밖에 나지 않습니다. 그래서 최댓값 필터를 통과한 영상은 밝게, 최솟값 필터를 통과한 영상은 어둡게 되겠지만 극단적인 변화는 일어나지 않습니다.


1. openCV에서의 최댓값(Maximum) / 최솟값(Minimum) 필터

img = cv2.imread('lenna.png')
height, width, channel = img.shape

우선 이미지를 불러와 줍니다.

 

noise = img.copy()
salt = int(height * width * 0.05)
for i in range(salt):
    row = int(np.random.randint(99999, size=1) % height)
    col = int(np.random.randint(99999, size=1) % width)
    ch = int(np.random.randint(99999, size=1) % channel)
    noise[row][col][ch] = 255 if np.random.randint(99999, size=1) % 2 == 1 else 0

그리고 salt-and-pepper 노이즈를 첨가합니다.

 

out = np.zeros((height + 1, width + 1, channel), dtype=np.float)
out[1:1 + height, 1:1 + width] = img.copy().astype(np.float)
temp, max_out, min_out = out.copy(), out.copy(), out.copy()
for i in range(height):
    for j in range(width):
        for k in range(channel):
            max_out[1 + i, 1 + j, k] = np.max(temp[i:i + 3, j:j + 3, k])
            min_out[1 + i, 1 + j, k] = np.min(temp[i:i + 3, j:j + 3, k])
max_out = max_out[1: 1 + height, 1: 1 + width].astype(np.uint8)
min_out = min_out[1: 1 + height, 1: 1 + width].astype(np.uint8)

이제부터 최댓값 최솟값 필터를 구현합니다.

 

최댓값 필터 영상과 최솟값 필터 영상을 만들기 위해 제로 패딩을 해주고, 중첩 for문을 통해 최댓값과 최솟값을 찾아 출력 영상에 집어 넣습니다. 이후 제로 패딩을 제거하고 다시 정수형으로 형 변환해주시면 끝입니다.

 

최댓값 최솟값 필터는 말 그대로 최댓값과 최솟값만 구하면 되기에 코드가 길지 않습니다.

 

 

이제 결과 영상입니다.

원본 영상
salt-and-pepper 적용 영상
최댓값 필터 통과 영상
최솟값 필터 통과 영상

두 필터 모두 노이즈는 확실하게 제거 되었지만, 앞서 설명한 대로 각각 밝아지고 어두워지는 경향을 보였습니다.


2. 마치며

최댓값 / 최솟값 필터는 간단하게 salt-and-pepper 노이즈를 제거할 수 있지만, 원본 영상과는 살짝 달라지는 특성이 있어서 실생활에서 노이즈를 제거하는 데 사용되지는 않습니다.

 

def max_min():
    img = cv2.imread('lenna.png')
    height, width, channel = img.shape

    noise = img.copy()
    salt = int(height * width * 0.05)
    for i in range(salt):
        row = int(np.random.randint(99999, size=1) % height)
        col = int(np.random.randint(99999, size=1) % width)
        ch = int(np.random.randint(99999, size=1) % channel)
        noise[row][col][ch] = 255 if np.random.randint(99999, size=1) % 2 == 1 else 0

    out = np.zeros((height + 1, width + 1, channel), dtype=np.float)
    out[1:1 + height, 1:1 + width] = img.copy().astype(np.float)
    temp, max_out, min_out = out.copy(), out.copy(), out.copy()
    for i in range(height):
        for j in range(width):
            for k in range(channel):
                max_out[1 + i, 1 + j, k] = np.max(temp[i:i + 3, j:j + 3, k])
                min_out[1 + i, 1 + j, k] = np.min(temp[i:i + 3, j:j + 3, k])
    max_out = max_out[1: 1 + height, 1: 1 + width].astype(np.uint8)
    min_out = min_out[1: 1 + height, 1: 1 + width].astype(np.uint8)

    cv2.imshow('original', img)
    cv2.imshow('salt', noise)
    cv2.imshow('max', max_out)
    cv2.imshow('min', min_out)
    cv2.waitKey(0)
반응형