😏히스토그램

히스토그램

히스토그램 계산부터 정규화, 평탄화, CLAHE까지 모든 과정을 다룹니다.

히스토그램 기본 이론

히스토그램 개념

히스토그램은 도수 분포표를 그래프로 나타낸 것입니다. 이미지에서 히스토그램은 각 픽셀 값이 몇 개씩 있는지를 시각적으로 보여줍니다. 예를 들어, 픽셀 값 0부터 255까지 각각 몇 개씩 존재하는지 그래프로 표현한 것입니다.

이미지 히스토그램을 통해 다음을 파악할 수 있습니다:

  • 픽셀들의 색상 분포

  • 명암의 분포 특성

  • 이미지의 전체적인 밝기 특성

cv2.calcHist() 함수

OpenCV에서는 cv2.calcHist() 함수로 히스토그램을 계산합니다.

hist = cv2.calcHist(images, channels, mask, histSize, ranges)
매개변수 상세 설명
  • images: 입력 이미지, 리스트 형태로 전달 (예: [img])

  • channels: 분석할 채널 지정, 리스트 형태

* 1채널: [0]
* 2채널: [0, 1]
* 3채널: [0, 1, 2]

  • mask: 특정 영역만 계산할 마스크, 전체 영역이면 None

  • histSize: 히스토그램 구간(bin) 개수, 채널 수에 맞는 리스트

* 1채널: [256]
* 3채널: [256, 256, 256]

  • ranges: 픽셀 값 범위, RGB의 경우 [0, 256]

히스토그램 계산 실습

회색조 이미지 히스토그램

가장 기본적인 회색조 이미지의 히스토그램을 계산하고 시각화하는 예제입니다.

# 회색조 1채널 히스토그램
import cv2
import numpy as np
import matplotlib.pylab as plt

# 이미지를 그레이스케일로 읽기
img = cv2.imread('../img/mountain.jpg', cv2.IMREAD_GRAYSCALE)
cv2.imshow('img', img)

# 히스토그램 계산
hist = cv2.calcHist([img], [0], None, [256], [0, 256])

# 히스토그램 그리기
plt.plot(hist)
print("hist.shape:", hist.shape)  # (256, 1)
print("hist.sum():", hist.sum(), "img.shape:", img.shape)
plt.show()
핵심 포인트
  • cv2.calcHist()의 반환값은 shape이 (256, 1)인 배열

  • 히스토그램의 총합은 이미지의 전체 픽셀 수와 같음

  • plt.plot(hist)로 간단하게 그래프 표시 가능

컬러 이미지 히스토그램

RGB 3채널 이미지의 각 채널별 히스토그램을 계산하는 방법입니다.

# 색상 이미지 히스토그램
import cv2
import numpy as np
import matplotlib.pylab as plt

# 컬러 이미지 읽기
img = cv2.imread('../img/mountain.jpg')
cv2.imshow('img', img)

# 채널별로 분리하여 히스토그램 계산
channels = cv2.split(img)
colors = ('b', 'g', 'r')

for (ch, color) in zip(channels, colors):
    hist = cv2.calcHist([ch], [0], None, [256], [0, 256])
    plt.plot(hist, color=color)

plt.show()
핵심 포인트
  • cv2.split(img)로 BGR 각 채널을 분리

  • 각 채널별로 히스토그램을 계산하여 색상별 분포 확인

  • OpenCV는 BGR 순서를 사용함에 주의


정규화 (Normalization)

이미지의 픽셀 값 분포가 특정 영역에 몰려있을 때, 전체 범위로 확장하여 화질을 개선하는 기법입니다.

cv2.normalize() 함수

dst = cv2.normalize(src, dst, alpha, beta, type_flag)
매개변수 상세 설명
  • src: 입력 이미지

  • dst: 출력 이미지

  • alpha: 정규화 구간의 시작값

  • beta: 정규화 구간의 끝값

  • type_flag: 정규화 방식 선택

* cv2.NORM_MINMAX: 최솟값-최댓값 구간 정규화
* cv2.NORM_L1: 전체 합으로 나누기
* cv2.NORM_L2: 단위 벡터로 정규화
* cv2.NORM_INF: 최댓값으로 나누기

정규화 적용 예제

# 히스토그램 정규화
import cv2
import numpy as np
import matplotlib.pylab as plt

# 어두운 이미지 읽기
img = cv2.imread('../img/abnormal.jpg', cv2.IMREAD_GRAYSCALE)

# 수동 정규화 계산
img_f = img.astype(np.float32)
img_norm = ((img_f - img_f.min()) * 255 / (img_f.max() - img_f.min()))
img_norm = img_norm.astype(np.uint8)

# OpenCV API를 이용한 정규화
img_norm2 = cv2.normalize(img, None, 0, 255, cv2.NORM_MINMAX)

# 결과 비교
cv2.imshow('Before', img)
cv2.imshow('Manual', img_norm)
cv2.imshow('cv2.normalize()', img_norm2)

# 히스토그램 비교
hist = cv2.calcHist([img], [0], None, [256], [0, 255])
hist_norm = cv2.calcHist([img_norm], [0], None, [256], [0, 255])
hist_norm2 = cv2.calcHist([img_norm2], [0], None, [256], [0, 255])

hists = {'Before': hist, 'Manual': hist_norm, 'cv2.normalize()': hist_norm2}
for i, (k, v) in enumerate(hists.items()):
    plt.subplot(1, 3, i+1)
    plt.title(k)
    plt.plot(v)
plt.show()
정규화 효과
  • 중앙에 몰린 픽셀 값들이 전체 범위로 확산

  • 명암 대비 향상으로 이미지가 더 선명해짐


평탄화 (Histogram Equalization)

히스토그램을 균등하게 분포시켜 명암 대비를 극대화하는 기법입니다. 정규화보다 더 강력한 대비 개선 효과가 있습니다.

평탄화의 원리

히스토그램 평탄화는 누적 분포 함수(CDF)를 이용하여 픽셀 값을 재분배합니다. 이를 통해 모든 픽셀 값이 균등하게 분포하도록 만듭니다.

cv2.equalizeHist() 함수

dst = cv2.equalizeHist(src)
매개변수 설명
  • src: 입력 이미지 (8비트 1채널)

  • dst: 평탄화된 출력 이미지

회색조 이미지 평탄화

# 회색조 이미지 평탄화
import cv2
import numpy as np
import matplotlib.pylab as plt

# 어두운 이미지 읽기
img = cv2.imread('../img/yate.jpg', cv2.IMREAD_GRAYSCALE)
rows, cols = img.shape[:2]

# 수동 평탄화 구현
hist = cv2.calcHist([img], [0], None, [256], [0, 256])
cdf = hist.cumsum()  # 누적 히스토그램
cdf_m = np.ma.masked_equal(cdf, 0)  # 0값을 NaN으로 마스킹
cdf_m = (cdf_m - cdf_m.min()) / (rows * cols) * 255  # 평탄화 계산
cdf = np.ma.filled(cdf_m, 0).astype('uint8')  # NaN을 0으로 복원
img2 = cdf[img]  # 히스토그램을 이용한 픽셀 매핑

# OpenCV API를 이용한 평탄화
img3 = cv2.equalizeHist(img)

# 결과 비교
cv2.imshow('Before', img)
cv2.imshow('Manual', img2)
cv2.imshow('cv2.equalizeHist()', img3)

# 히스토그램 비교
hist2 = cv2.calcHist([img2], [0], None, [256], [0, 256])
hist3 = cv2.calcHist([img3], [0], None, [256], [0, 256])

hists = {'Before': hist, 'Manual': hist2, 'cv2.equalizeHist()': hist3}
for i, (k, v) in enumerate(hists.items()):
    plt.subplot(1, 3, i+1)
    plt.title(k)
    plt.plot(v)
plt.show()

컬러 이미지 평탄화

컬러 이미지에 평탄화를 적용할 때는 YUV 색공간을 활용하여 밝기 채널(Y)만 처리하는 것이 효과적입니다.

# 색상 이미지 평탄화
import numpy as np, cv2

# 컬러 이미지 읽기
img = cv2.imread('../img/yate.jpg')

# BGR을 YUV로 변환
img_yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)

# Y 채널(밝기)에 평탄화 적용
img_yuv[:, :, 0] = cv2.equalizeHist(img_yuv[:, :, 0])

# YUV를 BGR로 변환
img2 = cv2.cvtColor(img_yuv, cv2.COLOR_YUV2BGR)

cv2.imshow('Before', img)
cv2.imshow('After', img2)
cv2.waitKey()
cv2.destroyAllWindows()
핵심 포인트
  • YUV 색공간에서 Y는 밝기, U와 V는 색상 정보

  • 밝기 채널만 평탄화하면 색상 왜곡 없이 대비 개선 가능

  • BGR 각 채널을 개별적으로 평탄화하면 색상이 왜곡됨


CLAHE (Contrast Limited Adaptive Histogram Equalization)

일반적인 평탄화의 문제점을 해결한 고급 기법입니다. 지역적 적응형 평탄화로 과도한 밝기 날림 현상을 방지합니다.

CLAHE의 특징

  • 적응형: 이미지를 작은 영역으로 나누어 각각 평탄화

  • 대비 제한: 지정된 임계값을 넘는 픽셀은 균등 배분

  • 노이즈 감소: 극단적인 값으로 인한 노이즈 방지

cv2.createCLAHE() 함수

clahe = cv2.createCLAHE(clipLimit, tileGridSize)
result = clahe.apply(src)
매개변수 설명
  • clipLimit: 대비 제한 임계값 (기본값: 40.0)

  • tileGridSize: 영역 분할 크기 (기본값: 8x8)

CLAHE 적용 예제

# CLAHE 적용
import cv2
import numpy as np

# 밝은 부분이 있는 이미지 읽기
img = cv2.imread('../img/bright.jpg')
img_yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)

# 일반 평탄화 적용
img_eq = img_yuv.copy()
img_eq[:, :, 0] = cv2.equalizeHist(img_eq[:, :, 0])
img_eq = cv2.cvtColor(img_eq, cv2.COLOR_YUV2BGR)

# CLAHE 적용
img_clahe = img_yuv.copy()
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
img_clahe[:, :, 0] = clahe.apply(img_clahe[:, :, 0])
img_clahe = cv2.cvtColor(img_clahe, cv2.COLOR_YUV2BGR)

# 결과 비교
cv2.imshow('Before', img)
cv2.imshow('CLAHE', img_clahe)
cv2.imshow('equalizeHist', img_eq)
cv2.waitKey()
cv2.destroyAllWindows()

CLAHE 핵심 사용법

# CLAHE 기본 사용 패턴
clahe = cv2.createCLAHE()  # 기본 설정으로 CLAHE 객체 생성
result = clahe.apply(img)  # 이미지에 CLAHE 적용
매개변수 조정 가이드
  • clipLimit을 낮추면 (예: 2.0) 더 자연스러운 결과

  • clipLimit을 높이면 (예: 8.0) 더 강한 대비 효과

  • tileGridSize를 작게 하면 더 세밀한 지역 적응

  • tileGridSize를 크게 하면 더 부드러운 전환


기법별 비교 및 활용 가이드

사용 시기별 권장사항

정규화 사용 권장 상황
  • 픽셀 값이 좁은 범위에 집중된 경우

  • 전체적으로 어둡거나 밝은 이미지

  • 간단한 대비 개선이 필요한 경우

평탄화 사용:

  • 강한 명암 대비 개선이 필요한 경우

  • 히스토그램이 불균등하게 분포된 경우

  • 흑백 이미지 처리

CLAHE 사용:

  • 밝은 부분과 어두운 부분이 모두 중요한 경우

  • 일반 평탄화로 세부사항이 손실되는 경우

  • 의료 영상, 보안 카메라 영상 등 디테일이 중요한 분야

처리 결과 특성

기법별 특성 비교

기법

장점

단점

적용 분야

정규화

간단하고 빠름, 자연스러운 결과

제한적인 개선 효과

일반적인 이미지 보정

평탄화

강력한 대비 개선

밝은 부분 날림, 색상 왜곡

어두운 이미지, 흑백 사진

CLAHE

균형잡힌 결과, 세부사항 보존

복잡한 설정, 처리 시간

전문적인 이미지 분석


참고 자료