파이썬 강의/openCV

파이썬 openCV 5. 명암대비 스트레칭(streching)

마리사라 2020. 11. 16. 23:08
반응형

파이썬 openCV 5번째 강의는 명암대비 스트레칭입니다.

여러분들도 근육이 뭉쳤을때 스트레칭을 통해 근육을 풀어주곤 하죠? 이미지도 마찬가지로 스트레칭이 가능한데요. 이번 시간에 스트레칭중 명암대비 스트레칭 기법을 알려드리겠습니다.


0. 스트레칭?

사람은 스트레칭을 할 때 근육을 쭉 늘리면서 스트레칭을 하죠? 이미지의 스트레칭은 이미지의 명암(밝고 어두움)을 쭈욱 늘려서 명암의 분포를 넓게 만드는 작업을 말합니다. 스트레칭의 방법으로는 명암대비 스트레칭과 엔드-인 기법의 두가지 방식이 대표적이며, 이번 시간에는 명암대비 스트레칭에 대해서 알려드리려고 합니다.

 

스트레칭을 알기 전에 히스토그램을 먼저 설명을 하고 가야되는데요. 히스토그램은 원래 도수분포표를 그래프의 형태로 나타낸 것을 말합니다.

출처 : 위키백과

 

위 사진이 대표적인 히스토그램의 모습입니다. 히스토그램의 자세한 정보는 통계학 과목을 공부하다 보면 알 수 있으니 패스하고, 우리는 디지털 영상에서의 히스토그램을 살펴 보겠습니다.

 

 

 디지털 영상에서의 히스토그램은 특정한 값을 가진 화소가 영상 안에 몇 개나 있는지를 표시한 것입니다. 특정한 값은 그레이스케일(흑백)이라면 밝기에 해당하는 명도값을, 일반적인 컬러 영상이라면 각각의 RGB값을 히스토그램으로 나타 낼 수 있겠죠.

 

이런 히스토그램으로 나타냈을때 좋은점이 여러가지 있습니다. 그중 첫번째로 화소값이 어떻게 되어있는지가 한번에 보이점이 있죠. 명도값을 히스토그램으로 나타냈을때 0쪽에 대부분 있다면 어두운영상, 255쪽에 대부분 있다면 밝은 영상, 히스토그램이 전체적으로 왼쪽에 있다면 명암대비가 낮은 영상, 히스토그램이 전체적으로 고르게 분포해 있다면 명암대비가 높은 영상입니다.

 

여기서 명암대비 스트레칭이 나오는데요. 명암대비가 낮은 영상을 명암대비가 높은 영상으로 늘려주는것을 명암대비 스트레칭이라고 하는거죠.


1. openCV에서의 명암대비 스트레칭

이제 히스토그램이 무엇인지도 알았고, 명암대비 스트레칭이 무엇인지도 알았으니 직접 해봐야겠죠?

 

명암대비 스트레칭을 하는 방법은 각각의 화소에 아래의 공식을 적용하면 됩니다

new pixel = (old pixel - low) * 255 / high - low

 ■ old pixel은 원래 영상 화소의 명도값

 ■ new pixel은 결과 영상 화소의 명도값(= 결과값)

 ■ low는 히스토그램의 최저 명도값

 ■ high는 히스토그램의 최고 명도값

 

이걸 모든 화소에 전부 적용해야하니 우리는 중첩 for문을 사용해서 적용해 보도록 하겠습니다.

 

시작하기 전에 이번에는 히스토그램과 관련된 강의이기 때문에 먼저 히스토그램을 표시할 수 있는 모듈이 필요합니다. 파이썬에서는 matplotlib라는 모듈이 히스토그램을 표시할 수 있는 모듈인데요. 파이썬을 설치하실때 기본 파이썬이 아닌 Anaconda로 설치하신 분들은 같이 동봉되어 있을테니 패스하셔도 되지만 해당 모듈이 없으신분들은 파이썬 콘솔을 켜고

>pip install matplotlib

라고 입력하셔서 matplotlib를 다운로드 하시면 될거같네요!

 

자 이제 matplotlib를 받으셨으면 import해야겠죠?

import matplotlib.pyplot as plt

잘보시면 matplotlib 전체를 import한 것이 아니라 matplotlib에 있는 pyplot만 import한 것을 볼 수 있는데요. 이는 우리가 사용할 대부분의 기능이 pyplot에서 사용하기에 pyplot만 사용하도록 import한 것입니다. 물론 대부분의 사용자들이 쓰는 plt로 치환해주는건 센스!

 

이제 명암대비 스트레칭을 시작해볼까요?

 

img = cv2.imread("lenna.png")

늘 그렇듯 먼저 이미지를 불러와 주시구요.

 

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

명암대비 스트레칭은 그레이스케일(흑백)으로 하기에 흑백이미지로 변환해줍니다.

 

out = gray.copy()

저장할 변수도 만들어줍니다.

 

height, width = gray.shape

저번 강의처럼 이미지의 크기를 불러와줍니다.

 

중첩 for문으로 명암대비 스트레칭 공식을 적용해야 하는데요. 해당 공식을 적용하기 전에 high값과 low값을 알아야합니다

high = gray.max()
low = gray.min()

max는 파이썬 자체에서 제공하는 최댓값을 찾아주는 코드입니다. min은 반대로 최솟값을 찾아주는 코드이지요. 그레이스케일로 된 이미지의 값들은 각각 화소의 명도를 나타냅니다. 그래서 max와 min으로 쉽게 최댓값과 최솟값을 알 수 있는거죠.

 

for i in range(width):
	for j in range(height):
    		out[i][j] = (gray[i][j] - low) * 255 / (high - low)

위와 같은 중첩 for문을 사용해서 gray의 각각의 화소에 명암대비 스트레칭 공식을 적용하고 결과 값을 out에 저장합니다.

 

 

cv2.imshow("original", gray)
cv2.imshow("streching", out)

마지막으로 출력을 해주면 명암대비 스트레칭 작업은 끝입니다.

 

하지만 우리는 스트레칭이 잘됬는지 직접 봐야겠죠? 그때 이제 plt가 사용됩니다

 

plt.figure()

우선 plt.figure()라는 코드를 사용했습니다. 이건 히스토그램보기 전에 틀을 만들었다고 보시면 됩니다.

 

plt.subplot(1, 2, 1)

이제 plt에서 subplot(큰 틀 안에 작은 틀)을 만들어줍니다. 이번거는 1행 2열을 가지는 plt에서 1번째 칸에 subplot를 만들어준다는 뜻입니다

 

plt.hist(gray.ravel(), 256, [0, 256])

앞에서 부터 설명하겠습니다. hist는 히스토그램을 만드는 pyplot의 명령어입니다. hist는 hist(배열, x축의 갯수, y축의 값의 범위) 입니다. 이때 배열은 1차원 배열을 넣어주기 위해 ravel()을 사용하여 1차원 배열화 시켰습니다

 

plt.subplot(1, 2, 2)
plt.hist(out.ravel(), 256, [0, 256])

같은 방식으로 결과영상인 out도 히스토그램화 시켜서 1, 2의 자리에 넣어주겠습니다.

 

plt.show()

이제 결과를 보여주면!

 

 

원본영상
스트레칭한 영상

이 둘의 차이를 느끼실 수 있나요? 눈이 좋으신분들은 바로 느끼셨을테지만 크게 달라진건 없어보입니다. 그렇다면 이걸 히스토그램으로 본다면 어떻게 될까요?

 

왼쪽 : 원본 / 오른쪽 : 스트레칭

왼쪽에 비해서 오른쪽의 히스토그램이 좀더 넓게 분포되어있는것을 볼 수 있습니다. 이제 차이가 느껴지시나요?


2. 마치며

이번에는 전에 쓰지않던 matplotlib를 사용해서 설명이 조금 장황했을수도 있겠네요. 이 다음 강의인 엔드-인 기법 강의에서도 matplotlib를 사용할 예정이고, 여타 히스토그램과 관련된 강의를 쓴다면 그때도 matplotlib를 사용 할 예정이니 꼭 잊지마세요~

 

 

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('lenna.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
out = gray.copy()
height, width = gray.shape
high = gray.max()
low = gray.min()
for i in range(width):
    for j in range(height):
        out[i][j] = ((gray[i][j] - low) * 255 / (high - low))

cv2.imshow("original", gray)
cv2.imshow('streching', out)

plt.figure()
plt.subplot(1, 2, 1)
plt.hist(gray.ravel(), 256, [0, 256])
plt.subplot(1, 2, 2)
plt.hist(out.ravel(), 256, [0, 256])
plt.show()

cv2.waitKey(0)
반응형