반응형

히스토그램

이미지처리를 할때 픽셀의 분포를 파악하기위해 사용하는 방법이다. 도메인마다 특징이 있는 픽셀값이 존재할 수 있으며, 이런 특징을 잘 핸들링 해주면 보다 퀄리티 높은 이미지를 획득 할 수 있다. 명암비, 평활화, 역투영과 같은 방법은 모두 히스토그램을 활용한 방법이라고 할 수 있다.

 

히스토그램 구하기

cv2.calcHist(src, channels, mask, histSize, ranges, hist=None, accmulate=None)
  • src: 입력 이미지의 리스트. 리스트 형태로 받기 때문에, 여러장의 이미지에 대해서도 히스토그램을 구할 수 있다.
  • channels: 히스토그램을 구할 채널을 나타내는 리스트. GrayScale이라면 [0]을, BGR이라면 [0, 1, 2]가 된다.
  • mask: 마스크 이미지. 마스크의 ROI만 히스토그램을 구할때 사용.
  • histSize: 히스토그램 각 차원의 크기(bin)를 나타내는 리스트. 예를들어 [256]이라면 모든 GrayScale 픽셀값이 나타나고, [128]이라면 2개의 픽셀의 빈도가 더해진 형태가 된다.
  • range: 히스토그램의 최소값, 최대값의 범위. GrayScale이라면 [0, 256]이다. 맨 마지막 256값은 포함이 안되며 255까지 표현된다.
  •  

GrayScale 히스토그램 구하기

src = cv2.imread('lenna.bmp', cv2.IMREAD_GRAYSCALE)
hist = cv2.calcHist([src], [0], None, [256], [0, 256])
plt.plot(hist)
plt.show()

GrayScale에서 히스토그램.

 

컬러 이미지 히스토그램 구하기

src = cv2.imread('lenna.bmp', cv2.IMREAD_COLOR)
planes = cv2.split(src)
colors = ['b', 'g', 'r']

for (plane, c) in zip(planes, colors):
    hist = cv2.calcHist([plane], [0], None, [256], [0, 256])
    plt.plot(hist, color=c)
plt.show()

컬러 이미지에서 히스토그램은 각 채널의 성분의 분포를 따로 표현한다.

 

BGR 색공간에서 히스토그램을 구하는방법은 위와같이, cv2.split함수를 이용해 각각의 채널을 분리시키고 하나씩 그림을 그려주도록 한다.

 

명암비(Contrast)

명암비는 밝은곳과 어두운곳의 밝기 정도의 차이라고 할 수 있다. 예를들어 뿌옇게 찍힌 이미지라면 명암비를 높여주어 밝은곳은 더욱 밝게, 어두운곳은 더 어둡게 만든다. 그러면 이미지가 보다 선명해 보이는 효과를 가져오게 된다.

(좌)원본이미지 / (우) 명암비가 조절된 이미지

 

이 작업을 수행하기 위해서는 간단한 수식을 사용한다. 특정 값을 기준으로 해당 값보다 큰 값은 가중치를 더해주고 작은값은 가중치를 빼준다. 또한 0과 255를 벗어나는 값에 대해서는 0, 255로 포화(Saturate)시켜준다. 아래 함수에서 특정값은 128이 되고, 가중치는 $\alpha$가 된다.


$$
dst(x, y) = saturate(src(x, y) + (src(x, y)-128) * \alpha)
$$

0, 255를 벗어나는 값은 Saturate된다.

 

명암비조절

src = cv2.imread('lenna.bmp', cv2.IMREAD_GRAYSACLE)
alpha = 1.0
func = (1+alpha) * src - (alpha * 128)
dst = np.clip(func, 0, 255).astype(np.uint8)

 

스트레칭

위와같은 방법은 사용자가 직접 기준값과 가중치를 설정해야하는 반면, 이를 자동적으로 조절하는 방법도 있다. 아이디어는 간단한데, 바로 정규화를 해주면된다. 정규화를 통해 히스토그램의 분포를 넓게 펼쳐줌으로써 명암비를 높일 수 있다.

분포가 양옆으로 잡아 당겨진듯한 히스토그램.

cv2.normalize(src, dst, alpha=None, beta=None, norm_type=None, dtype=None, mask=None)
  • alpha: 목표 정규화의 값. 또는 사용자 정의 최소값
  • beta: 사용자 정의 최대값
  • norm_type: 정규화 타입. NORM_INF, NORM_L1, NORM_L2, NORM_MINMAX

 

히스토그램 스트레칭 하기

src = cv2.imread('Hawkes.jpg', cv2.IMREAD_GRAYSCALE)
dst = cv2.normalize(src, None, 0, 255, cv2.NORM_MINMAX)

 

평활화

스트레칭은 분포의 특성을 고려하지 않고, 균등하게 퍼트리는 역할을 한다. 반면, 평활화는 누적분포를 활용해서 스트레칭을 진행한다. 이 방법을 이용하면 분포의 밀도가 높은곳은 더 넓게 퍼트리고, 밀도가 낮은곳은 조금 퍼트리는 성질을 가지고 있다.

스트레칭 방법과는 분포의 양상이 매우 다르다.

cv2.equalizeHist(src, dst=None)

 

히스토그램 평활화 하기

src = cv2.imread('Hawkes.jpg', cv2.IMREAD_GRAYSCALE)
dst = cv2.equalizeHist(src)

 

컬러 영상의 히스토그램 평활화 하기

BGR 색공간에서는 밝기성분을 표현할 수 없기 때문에 평활화를 할 수 없다. 따라서, 컬러 영상에서 히스토그램 평활화를 하기 위해서는 색공간을 변경해야한다. YCrCb 색공간에서 Y는 밝기성분만을 가지고 있으므로 이를 활용한다.

src = cv2.imread('lenna.bmp', cv2.IMREAD_COLOR)
src_ycrcb = cv2.cvtColor(src, cv2.COLOR_BGR2YCrCb)
y, cr, cb = cv2.split(src_ycrcb)
dst_y = cv2.equalizeHist(y)
dst_ycrcb = cv2.merge([dst_y, cr, cb])
dst = cv2.cvtColor(dst_ycrcb, cv2.COLOR_YCrCb2BGR)

컬러 이미지를 다룰때, YCrCb 색공간을 잘 활용해보자.

 

역투영

역투영이란 이미지내에서 특정 영역의 픽셀값의 분포를 통해 색상 영역을 검출하는것을 말한다. 이를 잘 활용하면 물체와 배경을 분리하거나, 특정 객체를 Segmentation할 수 있다. 단, 색상정보를 활용하기 때문에, 복잡한 색상이 뒤섞여 있는 경우에는 성능이 떨어진다.

cv2.calcBackProject(images, channles, hist, ranges, scale, dst=None)
  • images: 입력 영상의 리스트. 여러장의 이미지를 받을 수 있다.
  • channels: 역투영 계산에 사용할 채널 번호 리스트
  • hist: 입력 히스토그램
  • ranges: 히스토그램 차원의 최대, 최소로 구성된 리스트
  • scale: 출력 역투영 행렬에 추가적으로 곱해줄 값

 

히스토그램 역투영 하기

역투영을 하기 위해서는 우선 특정 영역의 Hist정보가 필요하기 때문에 cv2.selectROI를 사용하여 ROI정보를 가져온다. 복잡한 색공간에 컬러정보를 가지고 오는것은 YCrCb 색공간을 사용하는것이 좋기 때문에, 색공간을 변경하고 ROI 영역을 잘라내어 Hist연산을 진행한다.

src = cv2.imread('son.jpg', cv2.IMREAD_COLOR)
x, y, w, h = cv2.selectROI(src)
src_ycrcb = cv2.cvtColor(src, cv2.COLOR_BGR2YCrCb)
roi = src_ycrcb[y:y+h, x:x+w]
hist = cv2.calcHist([src], [1, 2], None, [128, 128], [0, 256, 0, 256])
backproj = cv2.calcBackProject([src_ycrcb], [1, 2], hist, [0, 256, 0, 256], 1)
dst = cv2.copyTo(src, backproj)

반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기