1. 이미지 히스토그램이란 무엇인가?
- 정의: 히스토그램은 이미지의 픽셀 강도 분포(색상 또는 그레이스케일)를 빈(bin) 단위로 집계한 그래프
- 빈의 의미: 예를 들어 256개의 빈을 사용하면, 각 픽셀 값(0~255)이 몇 번 등장했는지 세어 막대 그래프로 표현
2. cv2.calcHist 함수
cv2.calcHist(images, channels, mask, histSize, ranges)
| 파라미터 | 설명 |
| images | 히스토그램을 계산할 이미지 배열을 리스트 형태로 감싸서 전달 ( [image] ) |
| channels | 계산할 채널 인덱스 리스트. 그레이스케일: [0] 컬러(RGB): [0,1,2] |
| mask | 히스토그램 계산에 포함할 픽셀 영역을 지정하는 바이너리 마스크. 전체 영역 계산 시 None. |
| histSize | 각 채널별 빈 개수를 리스트로 지정. (예: [256], 컬러 3채널 모두 256개면 [256,256,256]) |
| ranges | 픽셀값 범위(비포함 상한 포함). 보통 [0,256] |
3. 히스토그램 정규화하기(L1 정규화 / 확률밀도 히스토그램)
# 1.
hist /= hist.sum ( )
# 2.
cv2.normalize(hist, hist_l1, alpha=1, beta=0, norm_type=cv2.NORM_L1)
- 백분율로 변환 >>> 모든 값을 합산하면 1이 되므로 히스토그램을 확률분포로 변환
- 픽셀 수 차이 보정 : 서로 다른 크기·조명·노출 이미지 간 분포 비교 가능
- 특징 벡터 단위화 : 머신러닝·딥러닝 입력으로 사용할 때, 피처 스케일을 맞춰 학습 안정성 향상
4. Grayscale Histogram
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
hist = cv2.calcHist( [image], [0], None, [256], [0, 256]

결과 해석 : 검은색, 흰색 픽셀은 매우 적고 중간값이 높음
5. Color Histogram
5-1. 1D Histogram : 이미지의 한가지 특징에 대한 픽셀 갯수 선 그래프 출력
# 채널별 이미지 분할
chans = cv2.split(image)
colors = ("b", "g", "r")
for (chan, color) in zip(chans, colors):
# 채널별로 히스토그램 생성 후 겹쳐서 시각화
hist = cv2.calcHist([chan], [0], None, [256], [0, 256])
plt.plot(hist, color=color)
plt.xlim([0, 256])

🔵 파란색(Blue) 채널
- 밝은영역(220~240)에 매우 높고 뾰족한 peak 존재 : 아주 밝은 파란색을 가진 픽셀이 압도적으로 많음
- 사진 속 하늘과 바다의 색조가 비교적 균일하고 매우 밝음
🟢 녹색(Green) 채널
- 중간~밝은 영역(100~200)에 넓게 분포하며 여러 개의 봉우리를 형성
- 사진 속 야자수 잎은 햇빛을 직접 받는 밝은 부분과 그늘진 어두운 부분이 공존
🔴 빨간색(Red) 채널
- 어두운 영역(0~50)에몇 개의 봉우리를 형성, 밝은 영역으로 갈수록 픽셀 수가 급격히 줄어듬
- 사진 속 야자수 나무 기둥과 해변의 모래 색상, 이미지 전체적으로 밝은 빨간색 요소는 거의 없기 때문에 갈수록 값이 매우 낮음
5-2. 2D Histogram : 채널간의 관계를 픽셀 밀도 맵으로 시각화
- 32*32개의 bin 사용
- X, Y축: 각 색상 채널의 강도
- 색상/밝기(Z축): 해당 (X, Y) 좌표의 색상 조합을 가진 픽셀의 수.
그래프에서 밝은 부분은 해당 색상 조합을 가진 픽셀이 이미지에 많다는 것을 의미
진한보라 : 픽셀 거의 없음, 연한노랑 : 픽셀 많음
# green and blue channels
fig = plt.figure(figsize=(15, 25))
ax = fig.add_subplot(131)
hist = cv2.calcHist([chans[1], chans[0]], [0, 1], None, [32, 32],
[0, 256, 0, 256])
p = ax.imshow(hist, interpolation="nearest")
ax.set_title("2D Color Histogram for G and B")
plt.colorbar(p)
# green and red channels
ax = fig.add_subplot(132)
hist = cv2.calcHist([chans[1], chans[2]], [0, 1], None, [32, 32],
[0, 256, 0, 256])
p = ax.imshow(hist, interpolation="nearest")
ax.set_title("2D Color Histogram for G and R")
plt.colorbar(p)
# blue and red channels
ax = fig.add_subplot(133)
hist = cv2.calcHist([chans[0], chans[2]], [0, 1], None, [32, 32],
[0, 256, 0, 256])
p = ax.imshow(hist, interpolation="nearest")
ax.set_title("2D Color Histogram for B and R")
plt.colorbar(p)

🔵🟢 Green & Blue
- 노랑부분이 green, blue 모두 높은 값에 존재 : 파란색과 녹색이 함께 많이 포함된 청록색 등의 색상을 주요하게 가지고 있음
- 밝은 점에서 원점 방향으로 대각선 분포 : 청록색 계열의 색상이 매우 넓은 영역 차지
🟢🔴 Green & Red
- 원점에서 우상단으로 진한 대각선 분포 : 회색조(Grayscale)에 가까운 색상(R, G, B 값이 모두 비슷한 경우) 또는 노란색/갈색 계열(R과 G는 높고 B는 낮은 경우)이 많다
- 원점근처에 노랑색상 집중됨 : 검은색 또는 매우 어두운 영역이 상당히 큰 비중을 차지 (예: 어두운 배경, 그림자)
🔵🔴 Blue & Red
- '순수한' 파란색 계열(높은 B, 낮은 R) 많이 분포
6. 마스크 적용
# 마스크 적용 예시
mask = np.zeros(gray.shape, dtype="uint8")
mask[100:300, 150:350] = 255
for (chan, color) in zip(chans, colors):
hist = cv2.calcHist([chan], [0], mask, [256], [0, 256])
plt.plot(hist, color=color)
plt.xlim([0, 256])


출처
OpenCV Image Histograms ( cv2.calcHist ) - PyImageSearch
In this tutorial, you will learn how to compute image histograms using OpenCV and the “cv2.calcHist” function.
pyimagesearch.com
'PyImageSearch' 카테고리의 다른 글
| OpenCV - Histogram Matching (0) | 2025.06.16 |
|---|---|
| OpenCV - Histogram Equalization (0) | 2025.06.16 |
| OpenCV - Edge detection (0) | 2025.06.14 |
| OpenCV - Image Gradients(엣지검출) (0) | 2025.06.13 |
| OpenCV - Thresholding(이진화) (0) | 2025.06.12 |