본문 바로가기

머신러닝,딥러닝/opencv

opencv 입문하기 7-1편 histogram stretching, equalization

 

1. 히스토그램

 

 

히스토그램은 값 또는 구간별 관측값의 갯수를 표현한 것입니다. 

이미지 영역에서 히스토그램은 '이미지를 구성하는 픽셀값 분포'에 대한 그래프 입니다.

 

X축은 픽셀값이고 범위는 0~255, Y축은 이미지에서 해당 픽셀값을 가진 픽셀의 개수입니다. 

갯수로 표현시 영상의 크기에 따라 빈도 경향이 달라지므로 전체 픽셀수로 나눈 정규화된 히스토그램Normalized histogram) 을 사용합니다. 

 

 

 

흑백 레나 이미지로 간단하게 히스토그램을 그려보면, 

 

처럼 나옵니다. 

 

"""Histogram"""
import os
import cv2
import matplotlib.pyplot as plt


src = cv2.imread(
    'lenna_2.jpg', cv2.IMREAD_GRAYSCALE)
hist = cv2.calcHist([src], [0], None, [256], [0, 256])

cv2.imshow('src', src)
cv2.moveWindow('src', 900, 200)
cv2.waitKey(1)

plt.plot(hist)
plt.show()

cv2.destroyAllWindows()

이 때, 사용하는 메서드는 cv2.calcHist 입니다. 

 

cv2.calcHist(images, channels, mask, histSize, ranges, hist=None, accumulate=None) -> hist

 

• images : 입력 영상 리스트
• channels : 히스토그래을 그릴 채널 리스트
• mask : 마스트 영상(전체의 히스토그램을 얻고 싶으면 None)
• histSize : 히스토그램의 bin 의 갯수
• ranges : 히스토그램 각 차원의 최솟값과 최대값(리스트 타입)
• accumulate: 누적히스토그램을 나타내고 싶으면 True (default: False)

• hist : 계산된 히스토그램

 

 

이제, 컬러 이미지에 적용해보겠습니다. 

 

 

"""color video Histogram"""

import cv2
import matplotlib.pyplot as plt

src = cv2.imread('lenna_2.jpg')

colors = ['b', 'g', 'r']

bgr_plans = cv2.split(src)

for (p, c) in zip(bgr_plans, colors):
    hist = cv2.calcHist([p], [0], None, [256], [0, 256])
    plt.plot(hist, color=c)

cv2.imshow('src', src)
cv2.moveWindow('src', 900, 200)

cv2.waitKey(1)

plt.show()

위 코드는 한 이미지의 값들을 BGR 를 나눠 받은 뒤 한 도화지에 그리는 원리입니다. 

 

각각의 BGR 를 각각 그려보겠습니다. 

 

 

우선 위와 같이 그린 것은 높이가 상대적입니다. 앞에서 그린 히스토그램에서는 B 의 높이가 제일 컸는데 지금 그린 히스토그램에서는 이를 알기가 어렵고, 단지 분포만을 확인하기가 용이합니다. 

 

 

import cv2
import numpy as np 

src = cv2.imread('lenna_2.jpg')

bgr_plans = cv2.split(src)

histSize = 256

histRange = (0,256)

accumulate = False

b_hist = cv2.calcHist(bgr_plans, [0], None, [histSize], histRange, accumulate=accumulate)
g_hist = cv2.calcHist(bgr_plans, [1], None, [histSize], histRange, accumulate=accumulate)
r_hist = cv2.calcHist(bgr_plans, [2], None, [histSize], histRange, accumulate=accumulate)

hist_w = 256*3
hist_h = 400
histImage = np.zeros((hist_h,hist_w,3), dtype=np.uint8)

cv2.normalize(b_hist,b_hist,alpha=0,beta=hist_h,norm_type=cv2.NORM_MINMAX)
cv2.normalize(g_hist,g_hist,alpha=0,beta=hist_h,norm_type=cv2.NORM_MINMAX)
cv2.normalize(r_hist,r_hist,alpha=0,beta=hist_h,norm_type=cv2.NORM_MINMAX)

for i in range(0,histSize):

    cv2.line(histImage,(i,hist_h-int(np.round(b_hist[i]))),
    (i,hist_h - 0), (255,0,0), thickness=2)

    cv2.line(histImage,(i+256,hist_h-int(np.round(g_hist[i-1]))),
    (i+256,hist_h - 0), (0,255,0), thickness=2)

    cv2.line(histImage,(i+256*2,hist_h-int(np.round(r_hist[i-1]))),
    (i+256*2,hist_h - 0), (0,0,255), thickness=2)

cv2.imshow('Source image', src)
cv2.imshow('HIstogram', histImage)
cv2.waitKey()

 

2.히스토그램 스트레칭(Histogram Stretching)

 

명암비(Contrast) 는 완전한 흰색과 완전환 검정색의 밝기(휘도) 차이입니다.

 

 

출처: https://en.wikipedia.org/wiki/Contrast_(vision)

적당한 명암비를 갖는게 이미지를 더 보기 좋게 만듭니다. 

 

이를 위해 opencv 에서는 histogram stretching 이란걸 해야하는데요. 뒤에서 다룰 '평활화'처럼 이미지를 더 보기 좋게 조정해주는 것입니다. 특정 밝기 영역에 영상픽셀의 밝기값이 집중되어 있는 것을 퍼트려 가시도를 좋게한다고 보면 됩니다. 

예를 들어 픽셀값이 100~200 인 grayscale 영상을 0 ~ 255로 변환하는 것입니다.

 

 

Histogram stretching(히스토그램 스트레칭)


• 영상의 최솟값을 0, 최댓값을 255 로 영상을 변경
• 히스토그램이 0 부터 255 까지 고르게 펴지는 효과
• 소수점 계산으로 인해 중간에 픽셀이 없는 값 존재
• 0 ~ 255 가 아닌 20 ~ 240 같이 새로운 구간 적용 가능

 

즉,  영상 내 픽셀의 최소, 최대값의 비율을 이용하여 고정된 비율로 영상을 낮은 밝기와 높은 밝기로 당겨주는 처리

입니다.

 

 

Histogram stretching 변경 함수

구간의 길이를 Max – Min 에서 newMax – newMin 으로 줄이고,시작을 Min 에서 newMin 으로 변경

 

위 수식을 구현한다고 보시면 됩니다.

 

 

cv2.normalize(src, dst, alpha=None, beta=None, norm_type=None, dtype=None, mask=None)

 

• src : 입력 영상
• dst : 결과 영상
• alpha
• Normed 영상의 최솟값(norm_type = NORM_MINMAX 경우)
• Normed 영상의 Norm 값(norm_type = NORM_L1/L2 경우)
• beta : Normed 영상의 최댓값(norm_type = NORM_MINMAX 경우)
• dtype : 결과 영상의 타입
• mask : 마스크 영상

 

위 메서드를 이용해서 stretching 을 해보겠습니다. 

 

 

dst 이미지가 스트레칭을 적용한 이미지인데 자세히 보시면 좀 더 명암비가 선명해짐을 확인할 수 있습니다. 

위에 그래프에서는 차이를 보여주고 싶었는데 normalize 한 것과 안한 히스토그램이 일치하네요 ㅠㅠ 

stretching 또한 일종의 scaling 이므로 분포 자체는 달라지지 않아서 처럼 보입니다 :)

 

 

# normalized histogram(히스토그램 스트레칭)


import cv2
import matplotlib.pyplot as plt

src = cv2.imread('lenna_2.jpg', cv2.IMREAD_GRAYSCALE)

dst = cv2.normalize(src, None, 0, 255, cv2.NORM_MINMAX)

hist1 = cv2.calcHist([src], [0], None, [256], [0, 256])
hist2 = cv2.calcHist([dst], [0], None, [256], [0, 256])


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

cv2.waitKey(1)

plt.plot(hist1, label="original")
plt.plot(hist2, label="normalized")
plt.legend(loc=2)

plt.show()

 

3. Histogram Equalization(히스토그램 평활화) 

- 히스토그램이 영상 전체에서 균일한 분포가 되도록 변경하는 기법

 

이미지의 밝기가 알맞지 않은 경우 히스토그램을 전체 영역에 고루 퍼져 있도록 바꾸어주면 이미지가 개선되는데 이를 '히스토그램 평활화' 라고 합니다. 

 

 

 https://en.wikipedia.org/wiki/Histogram_equalization

 

히스토그램 평활화 방법


1. 히스토그램 구하기
2. 히스토그램에서 전체 픽셀수로 나눈 정규화된 히스토그램 구하기
3. 정규화된 히스토그램에서 누적 확률 부포함수 구하기
4. 3의 결과에 기존 영상에서의 최댓값을 곱하기
5. 반올림하기

 

 

예시

 

메서드 

 

dst = cv2.equalizeHist(src)

 

• src : 입력 영상
• dst : grayscale 영상 결과

 

 

# 흑백 영상에 대한 평활화 

 

 

# 원본 영상과 어두운 영상을 모두 histigral equalizaton 한 결과

import cv2
import matplotlib.pyplot as plt

src1 = cv2.imread(
    'histogram equalization1.png', cv2.IMREAD_GRAYSCALE)

src2 = cv2.imread(
    'histogram equalization2.png', cv2.IMREAD_GRAYSCALE)

dst1 = cv2.equalizeHist(src1)
dst2 = cv2.equalizeHist(src2)

plt.subplot(221), plt.axis('off'), plt.imshow(src1, 'gray'), plt.title('src1')
plt.subplot(222), plt.axis('off'), plt.imshow(src2, 'gray'), plt.title('src2')
plt.subplot(223), plt.axis('off'), plt.imshow(dst1, 'gray'), plt.title('Histogram Equalization 1')
plt.subplot(224), plt.axis('off'), plt.imshow(dst2, 'gray'), plt.title('Histogram Equalization 2')

plt.show()

 

 

# 컬러 영상에 대한 평활화 

 

 

 

 

# 컬러 영상의 히스토그램 평활화

import numpy as np
import cv2

src = cv2.imread('sky.jpg')

src_ycrcb = cv2.cvtColor(src, cv2.COLOR_BGR2YCrCb)
ycrcb_planes = cv2.split(src_ycrcb)

# 밝기 성분(Y)에 대해서만 히스토그램 평활화 수행
ycrcb_planes[0] = cv2.equalizeHist(ycrcb_planes[0])

dst_ycrcb = cv2.merge(ycrcb_planes)
dst = cv2.cvtColor(dst_ycrcb, cv2.COLOR_YCrCb2BGR)

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

cv2.destroyAllWindows()

 

 

 

 

 

 

 

 

 

참고 

 

https://webnautes.tistory.com/1274

 

OpenCV Python 강좌 - 히스토그램(Histogram)

이미지에서 히스토그램을 구하는 방법과 응용으로 Histogram Equalization, CLAHE을 설명합니다. 다음 OpenCV Python 튜토리얼을 참고하여 강좌를 비정기적로 포스팅하고 있습니다. https://docs.opencv.org/4.0.0..

webnautes.tistory.com

https://m.blog.naver.com/PostView.nhn?blogId=stepingbaby&logNo=16754096&proxyReferer=https:%2F%2Fwww.google.com%2F

 

명암대비 스트레칭(Contrast Stretching)

특정 밝기 영역에 영상픽셀의 밝기값이 집중되어 있으면 영상의 가시도가 좋지 않다. 전체적으로 너무 어두...

blog.naver.com