4장. 채널 처리와 히스토그램 분석
마스크로 일부만 세기
교실 전체의 시험 점수 분포 말고 "앞줄 학생들만"의 분포가 궁금할 때가 있습니다. 출석부에서 앞줄 학생 이름에만 형광펜을 칠해 두고, 칠한 학생만 세면 됩니다. 마스크가 그 형광펜입니다. 그림과 같은 크기의 판에 셀 곳만 흰색(255)으로 칠해 cv2.calcHist 의 셋째 인자로 줍니다.
그림의 네 단계를 따라 마스크를 만들고 씁니다. 여기서 출석부가 그림이고, 형광펜을 칠한 이름이 마스크의 흰 칸입니다. 3장의 cv2.inRange 가 돌려주는 흑백 판도 같은 꼴이라 그대로 마스크로 쓸 수 있습니다.
가운데 그림이 마스크입니다. 해 둘레의 가로 70칸, 세로 70칸만 흰색이므로 4,900칸만 셉니다. 오른쪽은 세는 곳만 남겨 보여 준 모습입니다.
마스크의 약속
| 항목 | 약속 |
|---|---|
| 크기 | 그림과 세로·가로가 같아야 한다 |
| 자료형 | uint8 |
| 채널 | 하나(흑백 판) |
| 값 | 0 이 아닌 칸(보통 255)만 센다 |
짧은 예시
import cv2
import numpy as np
img = cv2.imread("ch04_scenes/sunset.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 그림과 같은 크기의 검은 판을 만들고, 해 둘레(세로 105~174, 가로 165~234)만 흰색으로
mask = np.zeros(gray.shape, dtype=np.uint8)
mask[105:175, 165:235] = 255
whole = cv2.calcHist([gray], [0], None, [256], [0, 256])
part = cv2.calcHist([gray], [0], mask, [256], [0, 256]) # 셋째 인자에 마스크
print(int(whole.sum()), int(part.sum())) # 결과: 76800 4900
# cv2.mean 도 mask 를 받는다: 흰 칸만의 평균
print(round(cv2.mean(gray, mask=mask)[0], 1), round(cv2.mean(gray)[0], 1))주의할 점
- 마스크의 크기가 그림과 다르면 오류가 납니다.
np.zeros(gray.shape, ...)처럼 그림의 모양을 그대로 씁니다. - 마스크로 센 막대는 전체보다 훨씬 낮습니다. 전체와 일부를 한 그래프에서 견주려면 막대를 픽셀 수로 나눠 비율로 바꿉니다.