히스토그램
이미지처리를 할때 픽셀의 분포를 파악하기위해 사용하는 방법이다. 도메인마다 특징이 있는 픽셀값이 존재할 수 있으며, 이런 특징을 잘 핸들링 해주면 보다 퀄리티 높은 이미지를 획득 할 수 있다. 명암비, 평활화, 역투영과 같은 방법은 모두 히스토그램을 활용한 방법이라고 할 수 있다.
히스토그램 구하기
cv2.calcHist(src, channels, mask, histSize, ranges, hist=None, accmulate=None)
src
: 입력 이미지의 리스트. 리스트 형태로 받기 때문에, 여러장의 이미지에 대해서도 히스토그램을 구할 수 있다.channels
: 히스토그램을 구할 채널을 나타내는 리스트. GrayScale이라면 [0]을, BGR이라면 [0, 1, 2]가 된다.mask
: 마스크 이미지. 마스크의 ROI만 히스토그램을 구할때 사용.histSize
: 히스토그램 각 차원의 크기(bin)를 나타내는 리스트. 예를들어 [256]이라면 모든 GrayScale 픽셀값이 나타나고, [128]이라면 2개의 픽셀의 빈도가 더해진 형태가 된다.range
: 히스토그램의 최소값, 최대값의 범위. GrayScale이라면 [0, 256]이다. 맨 마지막 256값은 포함이 안되며 255까지 표현된다.
GrayScale 히스토그램 구하기
src = cv2.imread('lenna.bmp', cv2.IMREAD_GRAYSCALE)
hist = cv2.calcHist([src], [0], None, [256], [0, 256])
plt.plot(hist)
plt.show()
컬러 이미지 히스토그램 구하기
src = cv2.imread('lenna.bmp', cv2.IMREAD_COLOR)
planes = cv2.split(src)
colors = ['b', 'g', 'r']
for (plane, c) in zip(planes, colors):
hist = cv2.calcHist([plane], [0], None, [256], [0, 256])
plt.plot(hist, color=c)
plt.show()
BGR 색공간에서 히스토그램을 구하는방법은 위와같이, cv2.split
함수를 이용해 각각의 채널을 분리시키고 하나씩 그림을 그려주도록 한다.
명암비(Contrast)
명암비는 밝은곳과 어두운곳의 밝기 정도의 차이라고 할 수 있다. 예를들어 뿌옇게 찍힌 이미지라면 명암비를 높여주어 밝은곳은 더욱 밝게, 어두운곳은 더 어둡게 만든다. 그러면 이미지가 보다 선명해 보이는 효과를 가져오게 된다.
이 작업을 수행하기 위해서는 간단한 수식을 사용한다. 특정 값을 기준으로 해당 값보다 큰 값은 가중치를 더해주고 작은값은 가중치를 빼준다. 또한 0과 255를 벗어나는 값에 대해서는 0, 255로 포화(Saturate)시켜준다. 아래 함수에서 특정값은 128이 되고, 가중치는 $\alpha$가 된다.
$$
dst(x, y) = saturate(src(x, y) + (src(x, y)-128) * \alpha)
$$
명암비조절
src = cv2.imread('lenna.bmp', cv2.IMREAD_GRAYSACLE)
alpha = 1.0
func = (1+alpha) * src - (alpha * 128)
dst = np.clip(func, 0, 255).astype(np.uint8)
스트레칭
위와같은 방법은 사용자가 직접 기준값과 가중치를 설정해야하는 반면, 이를 자동적으로 조절하는 방법도 있다. 아이디어는 간단한데, 바로 정규화를 해주면된다. 정규화를 통해 히스토그램의 분포를 넓게 펼쳐줌으로써 명암비를 높일 수 있다.
cv2.normalize(src, dst, alpha=None, beta=None, norm_type=None, dtype=None, mask=None)
alpha
: 목표 정규화의 값. 또는 사용자 정의 최소값beta
: 사용자 정의 최대값norm_type
: 정규화 타입. NORM_INF, NORM_L1, NORM_L2, NORM_MINMAX
히스토그램 스트레칭 하기
src = cv2.imread('Hawkes.jpg', cv2.IMREAD_GRAYSCALE)
dst = cv2.normalize(src, None, 0, 255, cv2.NORM_MINMAX)
평활화
스트레칭은 분포의 특성을 고려하지 않고, 균등하게 퍼트리는 역할을 한다. 반면, 평활화는 누적분포를 활용해서 스트레칭을 진행한다. 이 방법을 이용하면 분포의 밀도가 높은곳은 더 넓게 퍼트리고, 밀도가 낮은곳은 조금 퍼트리는 성질을 가지고 있다.
cv2.equalizeHist(src, dst=None)
히스토그램 평활화 하기
src = cv2.imread('Hawkes.jpg', cv2.IMREAD_GRAYSCALE)
dst = cv2.equalizeHist(src)
컬러 영상의 히스토그램 평활화 하기
BGR 색공간에서는 밝기성분을 표현할 수 없기 때문에 평활화를 할 수 없다. 따라서, 컬러 영상에서 히스토그램 평활화를 하기 위해서는 색공간을 변경해야한다. YCrCb 색공간에서 Y는 밝기성분만을 가지고 있으므로 이를 활용한다.
src = cv2.imread('lenna.bmp', cv2.IMREAD_COLOR)
src_ycrcb = cv2.cvtColor(src, cv2.COLOR_BGR2YCrCb)
y, cr, cb = cv2.split(src_ycrcb)
dst_y = cv2.equalizeHist(y)
dst_ycrcb = cv2.merge([dst_y, cr, cb])
dst = cv2.cvtColor(dst_ycrcb, cv2.COLOR_YCrCb2BGR)
역투영
역투영이란 이미지내에서 특정 영역의 픽셀값의 분포를 통해 색상 영역을 검출하는것을 말한다. 이를 잘 활용하면 물체와 배경을 분리하거나, 특정 객체를 Segmentation할 수 있다. 단, 색상정보를 활용하기 때문에, 복잡한 색상이 뒤섞여 있는 경우에는 성능이 떨어진다.
cv2.calcBackProject(images, channles, hist, ranges, scale, dst=None)
images
: 입력 영상의 리스트. 여러장의 이미지를 받을 수 있다.channels
: 역투영 계산에 사용할 채널 번호 리스트hist
: 입력 히스토그램ranges
: 히스토그램 차원의 최대, 최소로 구성된 리스트scale
: 출력 역투영 행렬에 추가적으로 곱해줄 값
히스토그램 역투영 하기
역투영을 하기 위해서는 우선 특정 영역의 Hist정보가 필요하기 때문에 cv2.selectROI
를 사용하여 ROI정보를 가져온다. 복잡한 색공간에 컬러정보를 가지고 오는것은 YCrCb 색공간을 사용하는것이 좋기 때문에, 색공간을 변경하고 ROI 영역을 잘라내어 Hist연산을 진행한다.
src = cv2.imread('son.jpg', cv2.IMREAD_COLOR)
x, y, w, h = cv2.selectROI(src)
src_ycrcb = cv2.cvtColor(src, cv2.COLOR_BGR2YCrCb)
roi = src_ycrcb[y:y+h, x:x+w]
hist = cv2.calcHist([src], [1, 2], None, [128, 128], [0, 256, 0, 256])
backproj = cv2.calcBackProject([src_ycrcb], [1, 2], hist, [0, 256, 0, 256], 1)
dst = cv2.copyTo(src, backproj)
최근댓글