from skimage.exposure import cumulative_distribution
from skimage.io import imread
import matplotlib.pyplot as plt
import numpy as np
import cv2 as cv
def hist_matching(c, c_t, im): # 매핑하는 함수(원본영상의 ncdf, template의 ncdf, 원본영상)
b = np.interp(c, c_t, np.arange(256)) # 매핑
pix_replace = {i:b[i] for i in range(256)}
mp = np.arange(0, 256)
for (k, v) in pix_replace.items():
mp[k] = v # mp는 매핑 테이블 (LUV)
im = mp[im] # 매핑 테이블 사용하여 입력영상 반환
return im
def ncdf(img):
n, b = cumulative_distribution(img.ravel()) # 영상의 ncdf 반환
for i in range(b[0]):
n = np.insert(n, 0, 0) # b[0]의 앞부분의 자리의 n 값(ncdf)은 0을 채워줌
for i in range(b[-1] + 1, 256): # b(n-1) 뒷부분에는 1의 값을 채워줌
n = np.append(n, 1)
return n
def myHist(img): # r,g,b 히스토그램 반환하는 함수
if img.dtype == 'float64':
img = (img * 255).astype('uint8')
hist_list = []
for i in range(3):
hist = cv.calcHist([img], [i], None, [256], [0,256]) # r,g,b 채널별 히스토그램 반환
hist_list.append(hist/np.max(hist))
return hist_list
s_file = 'data/monarch.png'
t_file = 'data/forest-park-trail.jpg'
src = imread(s_file) # 원본 영상 (변형된 히스토그램이 적용될 영상)
temp = imread(t_file) # template 영상 (원하는 히스토그램을 갖고 있는 영상)
src = src/255
output = np.zeros(src.shape, dtype=np.uint8)
for i in range(3): # RGB 채널에 대해 loop 작업
if src.dtype == 'float64':
src = (src*255).astype('uint8')
src_cdf = ncdf(src[..., i]) # 원본의 cdf
temp_cdf = ncdf(temp[..., i]) # template의 cdf
output[..., i] = hist_matching(src_cdf, temp_cdf, src[..., i]) # 히스토그램 명세화된 영상 반환
output = output[...,:3]
cv.imwrite('data/result.png',output[...,::-1])
plt.figure('myHist()')
# output 영상 출력
plt.subplot(221)
plt.title('result_png')
plt.imshow(output)
# source 영상 히스토그램
source_r, source_g, source_b = myHist(src)
# output 영상 히스토그램
output_r, output_g, output_b = myHist(output)
# source, output 영상 히스토그램 출력
# r채널 히스토그램 출력
plt.subplot(222)
plt.title('r_channel_histogram')
plt.plot(source_r, label='source_r')
plt.plot(output_r, label='output_r')
plt.legend()
# g채널 히스토그램 출력
plt.subplot(223)
plt.title('g_channel_histogram')
plt.plot(source_g, label='source_g')
plt.plot(output_g, label='output_g')
plt.legend()
# b채널 히스토그램 출력
plt.subplot(224)
plt.title('b_channel_histogram')
plt.plot(source_b, label='source_b')
plt.plot(output_b, label='output_b')
plt.legend()
plt.show()
# 2) lenna.tif 영상의 경우 화소값이 150일 떄의 r,g,b 반환값을 소수 4자리까지의 부동소수로 보이시오.
img = imread('data/lenna.tif')
r, g, b = myHist(img)
print('r = %.4f, g = %.4f, b = %.4f' % (r[150],g[150],b[150]))
# 히스토그램 명세화
- 입력영상의 히스토그램을 원하는 히스토그램으로 변환
1. 원본 영상의 정규화 분포 함수 작성
2. 원하는 히스토그램의 정규화 분포 함수 작성
3. 화소값 t의 평활화 매핑 값Ot 산출
4. 희망 히스토그램의 정규화 분포 함수에서Ot와 가장 가까운 값 N을 계산
5. 역 매핑함수에서 Qot 결정(LUT 사용)
# ncdf(img) 함수
- 영상의 ncdf 반환
- cumulative_distribution() 함수 이용하여 ncdf 반환 받음
# cumulative_distribution() 함수
- 이 함수는 아래와 같이 구현되어있음
def cumulative_distribution(image, nbins=256) :
hist, bin_centers = histogram(image, nbins)
img_cdf = hist.cumsum()
img_cdf = img_cdf/float(img_cdf[-1]])
return img_cdf, bin_centers
- 누적 분포 함수를 구한 후 정규화시키는 작업을 한 후 반환함
- 이 함수를 사용하면 bin_centers[0]의 앞부분의 자리의 n값을 0으로 채우고 b[n-1] 뒷부분 은 1로 채워줘야 함
# hist_matching(c, c_t, im) 함수
- 원본영상의 ncdf, template의 ncdf, 원본 영상을 매개변수로 입력받아 매핑 테이블을 생성하여 이를 입력영상에 적용하여 영상 변환함
- np.interp(c, c_t, np.arange(256) 를 사용하여 매핑시킴
- 딕셔너리를 사용하여 매핑 테이블(mp) 생성
- 매핑 테이블을 사용하여 변환된 영상 반환
# myHist(img) 함수
- r, g, b 채널별 히스토그램 반환하는 함수
- for문과 cv.calHist() 함수를 사용하여 히스토그램을 구하고 정규화를 시켜 반환함
- r, g, b 반환 값은 각각 최댓값에 의해 정규화된 0~1 분포도를 가짐
- 3채널 영상의 uint8과 float64 모두 지원하기 위하여 float64일 경우 255를 곱한 후 uint8 로 변환한 후 히스토그램을 얻음
‘lenna.tif’ 영상의 경우 화소 값이150일 때 r = 0.1877, g = 0.4219, b = 0.2338 임
# 출력 영상 반환
- 0으로 초기화한 원본 영상과 동일한 형, 형태의 배열을 생성
- ncdf 함수와, 매핑 함수를 사용하여 각 채널별 히스토그램 명세화된 영상을 반환받고 이를 합침
- 출력 영상을 'result.png' 로 저장함
- 출력 영상을 서브플롯의 첫 번째 창에 출력함
# 원본과 출력 영상의r, g, b별 히스토그램 출력
- myHist() 함수를 사용하여 반환받은 원본과 출력 영상 각각의 r, g, b 채널 별 히스토그램을 출력함
- r, g, b 채널별 히스토그램을 각각 서브플롯의 2,3,4 번째 창에 출력함
- 원본, 출력 영상의 범례를 표시함
- 소스에서 원본, 템플레이트 영상은 각각 'monarch.png', 'forest-parkptrail.jpg' 로 입력 받음
# 필요한 모듈 import
from skimage.exposure import cumulative_distribution
from skimage.io import imread
matplotlib.pyplot, numpy, import cv2 as cv
# 실행 결과
3채널 영상. uint8과 floa64 제한 없이 출력
1. 3채널 영상(uint8)
2. 3채널 영상(float64)
# 느낀 점
- myHist() 함수를 구현하기 위하여 다양한 히스토그램 함수에 대하여 알게 되었음
- 히스토그램의 명세화 작업에 대해서 복잡하지만 공부하게 되는 시간이었음
- uint8과 float64에 제한이 없게 하기 위하여 억지로 지정한 것이 아닌가 하는 생각이 들어 올바른 방법이 궁금함
'전공 공부 > 영상처리' 카테고리의 다른 글
Chroma Key Editor (0) | 2020.12.29 |
---|---|
UnsharpMasking 커널 기반 처리(나만의 생각..) (0) | 2020.12.29 |
UnsharpMasking 트랙 바로 시그마, 스케일 조정 (0) | 2020.12.29 |
히스토그램 스트레칭 함수 구현 (0) | 2020.12.29 |
시그모이드 그래프 출력 (0) | 2020.12.21 |