히스토그램

히스토그램은 도수분포표를 그래프로 나타낸 것이다. OpenCV에서는 영상의 픽셀 값 분포를 그래프의 형태로 표현한 것을 말한다.

OpenCV에서는 calcHist() 함수를 통해 히스토그램을 구할 수 있다. 예제코드를 살펴보자.

val images = listOf(src)
val channels = MatOfInt(0) //grayscale은 채널이 하나이므로 0
val mask:Mat = ... // 마스크
val hist = Mat() // 히스토그램 
val histSize = MatOfInt(256) // grayscale은 픽셀값이 0~255로 표현된다.
val ranges = MatOfFloat(0f, 255f) 
Imgproc.calcHist(
    images,// 입력 영상 배열
    channels,// 채널 갯수
    mask, 
    hist, 
    histSize, // 히스토그램 각 차원의 크기를 나타내는 리스트(빈(bin)의 개수)
    ranges // 히스토그램 각 차원의 최솟값과 최댓값으로 구성
)

계산된 히스토그램을 Android MPChart 라이브러리를 통해 막대그래프로 그리면 다음과 같다.

영상과 히스토그램의 관계

이미지의 밝기 또는 명암비에 따라 히스토그램 분포 양상이 달라진다. 

  • 히스토그램이 높은 값들에 전반적으로 분포 되어 있다면 해당 이미지는 밝은 이미지다.
  • 히스토그램이 낮은 값들에 전반적으로 분포 되어 있다면 해당 이미지는 어두운 이미지다.
  • 히스토그램이 전반적으로 고루 분포 되어 있다면 명암비가 높고 선명해 보일 수 있다.
  • 히스토그램이 전반적으로 좁게 분포 되어 있다면 명암비가 낮고 흐릿해 보일 수 있다.

히스토그램 스트레칭

레나 이미지는 위와 같은 히스토그램을 보여준다. 히스토그램의 막대가 시작되는 0부터 n 구간 그리고 막대가 끝나는 m부터 255 구간은 픽셀값의 분포가 없는 것을 알 수 있다. 이러한 히스토그램이 전 구간에 걸쳐 나타나도록 히스토그램을 아래와 같이 늘려(stretch) 명암비를 높일 수 있다.

OpenCV의 normalize 라는 함수를 통해 이미지 스트레칭을 시도해보자.

val src:Mat = ...
val dst:Mat = ...
Core.normalize(
    src, 
    dst, 
    0.0, // 최솟값
    255.0, // 최댓값
    Core.NORM_MINMAX // 정규화 타입
)

히스토그램 스트레칭 된 레나 이미지를 살펴보자

 

히스토그램 평활화

히스토그램 스트레칭은 명암비를 높여주지만 항상 이미지의 품질이 좋지만은 않다. 히스토그램 평활화를 통해 좀 더 나은 품질로 명암비를 올릴수 있다.

히스토그램 평활화란 히스토그램이 균일한 분포로 나타내도록 변경하는 명암비 향상 기법을 말한다.

누적 분포 함수를 통해 픽셀값이 촘촘히 분포 된 곳은 스트레칭을 많이 적용하고 그렇지 않은 곳에는 스트레칭을 적게 적용하는 기법이다. 자세한 내용은 위키를 참조

OpenCV에서는 equalizeHist()라는 함수를 제공하고 있다.

Imgproc.equalizeHist(src, dst)

원본 컬러 이미지와 평활화가 적용된 컬러 이미지의 명암비 및 히스토그램을 비교해보도록 하자.

컬러 이미지의 스트레칭 및 평활화

컬러 이미지의 경우 R,G,B 채널에 각각 정규화 또는 평활화를 적용하면 될 것 같지만, 그렇게 하면 색상값이 변경되어 이상해 보일 수 있다. 그렇기 때문에 컬러맵을 YCrCB 로 변경하여 밝기 성분에 대응하는 Y값만 연산을  진행한 후 다시 CrCb성분과 합성하면 된다.

val srcYCrCB = Mat()
Imgproc.cvtColor(src,srcYCrCB, Imgproc.COLOR_BGR2YCrCb)
// 채널 분리
val srcPlanes = ArrayList<Mat>()
Core.split(srcYCrCB, srcPlanes)
// Y 채널에 대해서만 히스토그램 평활화 수행
Imgproc.equalizeHist(srcPlanes[0], srcPlanes[0])
// 다시 채널 합치기
val dstYCrCb = Mat()
Core.merge(srcPlanes, dstYCrCb)
Imgproc.cvtColor(dstYCrCb, src, Imgproc.COLOR_YCrCb2BGR)

원본 컬러 이미지와 평활화가 적용된 컬러 이미지의 명암비 및 히스토그램 분포를 비교해보도록 하자.

Buy me a coffeeBuy me a coffee
카테고리: AndroidOpenCV

0개의 댓글

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.