import cv2import numpy as np# 이미지 읽기img = cv2.imread('./source_images/original1.jpg')# HSV 색공간으로 변환hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)# ROI 설정 (관심 영역)x, y, w, h = 120, 300, 180, 180hsv_roi = hsv_img[y:y+h, x:x+w]# 히스토그램 계산 (H, S 채널 사용)channels = [0, 1] # H=0, S=1ranges = [0, 180, 0, 256] # H: [0,180), S: [0,256)histSize = [180, 256] # H, S 각각 bin 개수img_hist = cv2.calcHist([hsv_img], channels, None, histSize, ranges)roi_hist = cv2.calcHist([hsv_roi], channels, None, histSize, ranges)# 히스토그램 정규화 (0~255)cv2.normalize(img_hist, img_hist, 0, 255, cv2.NORM_MINMAX)cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)
2. 확률 맵 생성
원본 이미지와 ROI의 히스토그램 비율로 픽셀별 확률 맵 생성.
ROI/원본 이미지 값이 크면, 그 색(H, S)이 ROI일 가능성이 높음.
Source
Backprojection Map
# 히스토그램 비율 R = M / I 계산R = roi_hist / (img_hist + 1) # 분모가 0이 되지 않도록 +1# ROI HSV 채널 분리h, s, v = cv2.split(hsv_img)# Backprojection 계산# h 값과 s 값에 해당하는 R 값을 추출B = R[h.flatten(), s.flatten()] # 1D 배열# 값이 1보다 크면 1로 제한B = np.minimum(B, 1)# 이미지 크기로 reshapeB = B.reshape(hsv_img.shape[:2])# 0~255 정규화B = cv2.normalize(B, None, 0, 255, cv2.NORM_MINMAX)B = B.astype(np.uint8)
OpenCV의 calcBackProject() 함수를 이용한 방법
B = cv2.calcBackProject([hsv_img], [0,1], roi_hist, [0,180,0,256], 1)
3. 영역 추출 및 후처리
Backprojection 결과는 노이즈가 있을 수 있음 → 필터링을 이용해 노이즈 제거 및 스무딩.
이진화를 통해 Backprojection 결과에서 임계값을 넘는 영역에 대한 마스크 생성.
원본 이미지에서 마스크에 해당하는 영역만 추출.
Filtered Mask
Result
# 원형 커널로 컨볼루션 → 주변 픽셀 보정kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))cv2.filter2D(B, -1, kernel, B)# 임계값 적용 (마스크 생성)_, mask = cv2.threshold(B, 50, 255, 0)# 원본 영상에서 mask 영역 추출result = cv2.bitwise_and(img, img, mask=mask)