본문 바로가기
OpenCV/OpenCV-Chapter

CH03 Opencv-Python Histogram, Contrast

by kiimy 2021. 12. 18.
728x90

Histogram

- 영상의 픽셀 값 분포(= 픽셀의 개수 Counting)

예를 들어 그레이스케일 영상에서 각 그레이스케일 값에 해당하는 픽셀의 개수를 구하고,

이를 막대 그래프의 형태로 표현

bin = pixel 값 / hist = 개수

Histogram normalized(영상의 자동 명암비 조절)

Histogram Stretching
영상의 히스토그램이 그레이스케일 전 구간에서 걸쳐 나타나도록 변경하는 선형 변환 기법

- 단순히 픽셀 개수를 GrayScale 값이 몇개 있다 counting하게 되면 이 영상이 작은 영상일 때와

  큰 영상일때의 값을 비교하기가 애매(size를 normalized 할때도 있음)

  • 각 픽셀의 개수를 영상 전체 픽셀 개수로 나누어준 것
  • 해당 그레이스케일 값을 갖는 픽셀이 나타날 확률

영상과 히스토그램의 관계

cv2.calcHist(images, channels, mask, histSize, ranges, hist=None, 
			accumulate=None) -> hist
            
# cv2.calcHist(연산 이미지, 특정 채널, 마스크, 히스토그램 크기, 히스토그램 범위)
# Calculate
'''
연산이미지: 하나여도 무조건 [list] 해줘야함
histSize: 히스토그램 각 차원의 크기(빈(bin)의 개수)를 나타내는 리스트
ranges: 히스토그램 각 차원의 최솟값과 최댓값으로 구성된 리스트
accumulate: 기존의 hist 히스토그램에 누적하려면 True, 새로 만들려면 False
'''
hist = cv2.calcHist([src], [0], None, [256], [0,256])
plt.plot(hist) # 분포 그래프를 보여줘야함 plot()
plt.show()

#############################################################

# color 영상의 히스토그램
src1 = cv2.imread('ch03\\images\\lenna.bmp')

colors = ['b', 'g', 'r'] # plot color
bgr_planes = cv2.split(src1)

# BGR을 각각 보여줘야하니 for, zip 
for (p, c) in zip(bgr_planes, colors):
    hist_color = cv2.calcHist([p], [0], None, [256], [0,256])
    plt.plot(hist_color, color=c)

cv2.imshow('src1', src1)
plt.show()

# numpy histogram(opencv 그리기 함수 사용)
def getGrayHistImage(hist):
    imgHist = np.full((100, 256), 255, dtype=np.uint8)

    histMax = np.max(hist)
    for x in range(256):
        pt1 = (x, 100)
        pt2 = (x, 100 - int(hist[x, 0] * 100 / histMax))
        cv2.line(imgHist, pt1, pt2, 0)

    return imgHist

# histImg = getGrayHistImage(hist)
# cv2.imshow('histImg', histImg)
# cv2.waitKey()

Contrast 

  • 밝은 곳과 어두운 곳 사이에 드러나는 밝기 정도의 차이
  • 컨트라스트, 대비

'''
dst(x,y) = saturate(s*src(x,y))
s = scala
if s = 0.5
명암은 줄어드는데 어두워짐(pdf 그래프 참조)
if s = 2.0
전체적으로 흰 부분이 많아짐(pdf 그래프 참조)
'''

# dst(x,y) = saturate(src(x,y) + (src(x,y) - 128) * 알파)
# 이 연산을 사용해야지만 적절한 contrast를 반영할 수 있다.

alpha = 1.0 # 커질수록 contrast가 높아짐 
# np.clip(0, 255) = 0보다 작은건 0, 255보다 큰건 255
# 결과값이 실수이니 .astype(np.uint8) 변경

dst = np.clip((1+alpha)*src - 128*alpha, 0, 255).astype(np.uint8)
hist = cv2.calcHist([dst], [0], None, [256], [0,256])

scala를 그냥 곱한 것 / 명암비 조절 함수

==> cv2.normalize를 쓰면됨(Histogram Stretching)


정규화(normalized = histogram stretching)

물체가 분간이 잘 되냐 안되냐를 판단할 때

contrast를 올리는 기법은 어떤 영상을 시스템에 입력으로 주기전에 전처리 과정으로서 사용

영상의 히스토그램이 그레이스케일 전 구간에서 걸쳐 나타나도록 변경하는 선형 변환 기법

특정 영역에 몰려 있는 경우 화질을 개선하기도 하고, 이미지 간의 연산 시
서로 조건이 다른 경우 같은 조건으로 만들기도 한다.
cv2.normalize(src, dst, alpha=None, beta=None, norm_type=None, dtype=None,
				mask=None) -> dst
'''
alpha: (노름 정규화인 경우) 목표 노름 값, 
		(원소 값 범위 정규화인 경우) 최솟값
beta: (원소 값 범위 정규화인 경우) 최댓값
'''
# gmin, gmax, _, _ = cv2.minMaxLoc(src)
# min, max를 구해서 구하기
gmin = np.min(src)
gmax = np.max(src)
# numpy사용하여 기울기, y 절편구하기( pdf 사진 참고)/ 실수형태이니 .astype
# dst = ((src - gmin) * 255. / (gmax - gmin)).astype(np.uint8)

# cv2.normalize(입력 배열, 결과 배열, alpha, beta, 정규화 기준)
# cv2.NORM_MINMAX을 통해, 정규화 기준을 최솟값이 alpha가 되고, 최댓값이 beta가 되게 변경
dst = cv2.normalize(src, None, 0, 255, cv2.NORM_MINMAX)
hist = cv2.calcHist([dst], [0], None, [256], [0,256])

cv2.imshow('src', src)
cv2.imshow('dst', dst)
plt.plot(hist)
plt.show()

평활화(Histogram equalization) = 균등화, 균일화, 평탄화

- 누적 분포함수(Gray만 받음)

이미지의 히스토그램이 특정영역에 너무 집중되어 있으면 contrast가 낮아 좋은 이미지라고 할 수 없다.
전체 영역에 골고루 분포가 되어 있을 때 좋은 이미지라고 할 수 있다.

=정규화는 분포가 한곳에 집중되어 있는 경우에는 효과적이지만 
  그 집중된 영역에서 멀리 떨어진 값이 있을 경우에는 효과가 없다.


  평탄화는 각각의 값이 전체 분포에 차지하는 비중에 따라 분포를 재분배하므로 명암 대비를 
  개선하는 데 효과적

# grayscale 영상의 히스토그램 평활화
# normailzed와 equalizeHist 차이
# normalized = contrast가 커짐
# equalizeHist = 밝은 부분과 어두운부분 명확해짐

src = cv2.imread('ch03\\images\\Hawkes.jpg', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

dst = cv2.equalizeHist(src)

cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()

##############################################################

# color 영상 히스토그램 평활화
# 분할해서 각각 진행
'''
BGR 색공간에서는 밝기성분을 표현할 수 없기 때문에 평활화를 할 수 없다. 
따라서, 컬러 영상에서 히스토그램 평활화를 하기 위해서는 색공간을 변경해야한다. 
YCrCb 색공간에서 Y는 밝기성분만을 가지고 있으므로 이를 활용한다.
'''

src_color = cv2.imread('ch03\\images\\field.bmp')

# Y: 밝기 정보
# Cr,Cb : red와 blue(밝기정보Y와 대비했을 때의 차이) = 색차정보
# Green? == red, blue를 합치면 green을 만들 수 있다
src_ycrcb = cv2.cvtColor(src_color, cv2.COLOR_BGR2YCrCb)
y, cr, cb = cv2.split(src_ycrcb)
planes = cv2.split(src_ycrcb)

# 밝기 성분에 대해서만 히스토그램 평활화 수행
dst_y = cv2.equalizeHist(y)

# 평활화 진행 후 merge(YCrCb ==> BGR 변경)
dst_ycrcb = cv2.merge([dst_y, cr, cb])
dst_final = cv2.cvtColor(dst_ycrcb, cv2.COLOR_YCrCb2BGR)

'''
*why?*
planes = cv2.split(src_ycrcb)
planes[0] = cv2.equalizeHist(planes[0])
TypeError: 'tuple' object does not support item assignment
'''
 

Y: 밟기 정보를 더 넓게 분포하게 되면서 선명해짐

 

728x90

댓글