๐ ๊ณต๋ถํ๋ ์ง์ง์ํ์นด๋ ์ฒ์์ด์ง?
[v0.12]์์์ฒ๋ฆฌ_ํ์คํ ๊ทธ๋จ(Histogram) ๋ณธ๋ฌธ
[v0.12]์์์ฒ๋ฆฌ_ํ์คํ ๊ทธ๋จ(Histogram)
์ง์ง์ํ์นด 2022. 1. 5. 00:35220104 ์์ฑ
<๋ณธ ๋ธ๋ก๊ทธ๋ ๊ทํ์ด ์์ฌ๋์ ๋ธ๋ก๊ทธ๋ฅผ ์ฐธ๊ณ ํด์ ๊ณต๋ถํ๋ฉฐ ์์ฑํ์์ต๋๋ค>
OpenCV - 10. ํ์คํ ๊ทธ๋จ๊ณผ ์ ๊ทํ(Normalize), ํํํ(Equalization), CLAHE
์ด๋ฒ ํฌ์คํ ์์๋ ํ์คํ ๊ทธ๋จ์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค. ์ด๋ฒ ํฌ์คํ ์ญ์ 'ํ์ด์ฌ์ผ๋ก ๋ง๋๋ OpenCV ํ๋ก์ ํธ(์ด์ธ์ฐ ์ )'๋ฅผ ์ ๋ฆฌํ ๊ฒ์์ ๋ฐํ๋๋ค. ์ฝ๋: github.com/BaekKyunShin/OpenCV_Project_Python/tr
bkshin.tistory.com
1. ํ์คํ ๊ทธ๋จ
: ๋์ ๋ถํฌํ๋ฅผ ๊ทธ๋ํ๋ก ๋ํ๋ธ ๊ฒ
: ๋ฌด์์ด ๋ช ๊ฐ ์๋์ง ๊ฐ์๋ฅผ ์ธ์ด ๋์ ๊ฒ์ ๊ทธ๋ํ
cv2.calHist(img, channel, mask, histSize, ranges)
: ํ์คํ ๊ทธ๋จ์ ๊ตฌํ
- img : ์ด๋ฏธ์ง ์์, [img]์ฒ๋ผ ๋ฆฌ์คํธ๋ก ๊ฐ์ธ์ ์ ๋ฌ
- channel : ๋ถ์ ์ฒ๋ฆฌํ ์ฑ๋, ๋ฆฌ์คํธ๋ก ๊ฐ์ธ์ ์ ๋ฌ
- 1 ์ฑ๋: [0], 2 ์ฑ๋: [0, 1], 3 ์ฑ๋: [0, 1, 2]
- mask : ๋ง์คํฌ์ ์ง์ ํ ํฝ์
๋ง ํ์คํ ๊ทธ๋จ ๊ณ์ฐ, None์ด๋ฉด ์ ์ฒด ์์ญ
- histSize : ๊ณ๊ธ(Bin)์ ๊ฐ์, ์ฑ๋ ๊ฐ์์ ๋ง๊ฒ ๋ฆฌ์คํธ๋ก ํํ
- 1 ์ฑ๋: [256], 2 ์ฑ๋: [256, 256], 3 ์ฑ๋: [256, 256, 256]
- ranges : ๊ฐ ํฝ์ ์ด ๊ฐ์ง ์ ์๋ ๊ฐ์ ๋ฒ์, RGB์ธ ๊ฒฝ์ฐ [0, 256]
# ํ์์กฐ 1์ฑ๋ ํ์คํ ๊ทธ๋จ
import cv2
import numpy as np
import matplotlib.pylab as plt
#--โ ์ด๋ฏธ์ง ๊ทธ๋ ์ด ์ค์ผ์ผ๋ก ์ฝ๊ธฐ ๋ฐ ์ถ๋ ฅ
img = cv2.imread('img/tree_gray.jpg', cv2.IMREAD_GRAYSCALE)
cv2.imshow('img', img)
#--โก ํ์คํ ๊ทธ๋จ ๊ณ์ฐ ๋ฐ ๊ทธ๋ฆฌ๊ธฐ
hist = cv2.calcHist([img], [0], None, [256], [0,256])
plt.plot(hist)
print("hist.shape:", hist.shape) #--โข ํ์คํ ๊ทธ๋จ์ shape (256,1)
print("hist.sum():", hist.sum(), "img.shape:",img.shape) #--โฃ ํ์คํ ๊ทธ๋จ ์ด ํฉ๊ณ์ ์ด๋ฏธ์ง์ ํฌ๊ธฐ
plt.show()
# ์์ ์ด๋ฏธ์ง ํ์คํ ๊ทธ๋จ
import cv2
import numpy as np
import matplotlib.pylab as plt
#--โ ์ด๋ฏธ์ง ์ฝ๊ธฐ ๋ฐ ์ถ๋ ฅ
img = cv2.imread('img/sunflower.jpg')
cv2.imshow('img', img)
#--โก ํ์คํ ๊ทธ๋จ ๊ณ์ฐ ๋ฐ ๊ทธ๋ฆฌ๊ธฐ
channels = cv2.split(img)
colors = ('b', 'g', 'r')
for (ch, color) in zip (channels, colors):
hist = cv2.calcHist([ch], [0], None, [256], [0, 256])
plt.plot(hist, color = color)
plt.show()
- img๋ ์์ ์ด๋ฏธ์ง์ด๋ฏ๋ก 3 ์ฑ๋ ์ด๋ฏธ์ง
- cv2.split(img)์ ํด์ฃผ๋ฉด ๋นจ๊ฐ, ํ๋, ์ด๋ก์ ๊ฐ 1 ์ฑ๋ ์ด๋ฏธ์ง๋ก ๋๋์ด์ 3์ฑ๋์ ๊ฐ์ง๋ค
- ํ๋ ์์ญ์ด ๋ง์์ ํ๋์ ๋ถํฌ๊ฐ ํฌ๋ค
2. ์ ๊ทํ(Normalization)
: ํน์ ์์ญ์ ๋ชฐ๋ ค ์๋ ๊ฒฝ์ฐ ํ์ง์ ๊ฐ์
: ์ด๋ฏธ์ง ๊ฐ์ ์ฐ์ฐ ์ ์๋ก ์กฐ๊ฑด์ด ๋ค๋ฅธ ๊ฒฝ์ฐ ๊ฐ์ ์กฐ๊ฑด์ผ๋ก ๋ง๋ค๊ธฐ
dst = cv2.normalize(src, dst, alpha, beta, type_flag)
- src : ์ ๊ทํ ์ด์ ์ ๋ฐ์ดํฐ
- dst : ์ ๊ทํ ์ดํ์ ๋ฐ์ดํฐ
- alpha : ์ ๊ทํ ๊ตฌ๊ฐ 1
- beta : ์ ๊ทํ ๊ตฌ๊ฐ 2, ๊ตฌ๊ฐ ์ ๊ทํ๊ฐ ์๋ ๊ฒฝ์ฐ ์ฌ์ฉ ์ ํจ
- type_flag : ์ ๊ทํ ์๊ณ ๋ฆฌ์ฆ ์ ํ ํ๋๊ทธ ์์
- cv2.NORM_MINMAX : alpha์ beta ๊ตฌ๊ฐ์ผ๋ก ์ ๊ทํ
- cv2.NORM_L1 : ์ ์ฒด ํฉ์ผ๋ก ๋๋๊ธฐ
- cv2.NORM_L2 : ๋จ์ ๋ฒกํฐ๋ก ์ ๊ทํ ํ๊ธฐ
- cv2.NORM_INF : ์ต๋๊ฐ์ผ๋ก ๋๋๊ธฐ
# ํ์คํ ๊ทธ๋จ ์ ๊ทํ
import cv2
import numpy as np
import matplotlib.pylab as plt
#--โ ๊ทธ๋ ์ด ์ค์ผ์ผ๋ก ์์ ์ฝ๊ธฐ
img = cv2.imread('img/hist_unequ.jpg', cv2.IMREAD_GRAYSCALE)
#--โก ์ง์ ์ฐ์ฐํ ์ ๊ทํ
img_f = img.astype(np.float32)
img_norm = ((img_f - img_f.min()) * (255) / (img_f.max() - img_f.min()))
img_norm = img_norm.astype(np.uint8)
#--โข OpenCV API๋ฅผ ์ด์ฉํ ์ ๊ทํ
img_norm2 = cv2.normalize(img, None, 0, 255, cv2.NORM_MINMAX)
#--โฃ ํ์คํ ๊ทธ๋จ ๊ณ์ฐ
hist = cv2.calcHist([img], [0], None, [256], [0, 255])
hist_norm = cv2.calcHist([img_norm], [0], None, [256], [0, 255])
hist_norm2 = cv2.calcHist([img_norm2], [0], None, [256], [0, 255])
cv2.imshow('Before', img)
cv2.imshow('Manual', img_norm)
cv2.imshow('cv2.normalize()', img_norm2)
hists = {'Before' : hist, 'Manual':hist_norm, 'cv2.normalize()':hist_norm2}
for i, (k, v) in enumerate(hists.items()):
plt.subplot(1,3,i+1)
plt.title(k)
plt.plot(v)
plt.show()
- ์ ๊ทํ ์ ์๋ ํฝ์ ๊ฐ์ด ์ค์์ ๋ชจ์
- ์ ๊ทํ ํ์๋ ํฝ์ ๊ฐ์ด ์ ์ฒด์ ์ผ๋ก ํผ์ ธ์ ํ์ง์ด ๊ฐ์
3. ํํํ (Equalization)
: ๊ฐ๊ฐ์ ๊ฐ์ด ์ ์ฒด ๋ถํฌ์ ์ฐจ์งํ๋ ๋น์ค์ ๋ฐ๋ผ ๋ถํฌ๋ฅผ ์ฌ๋ถ๋ฐฐ
: ๋ช ์ ๋๋น๋ฅผ ๊ฐ์ ํ๋ ๋ฐ ํจ๊ณผ์
: ์ ์ฒด ์์ญ์ ๊ณจ๊ณ ๋ฃจ ๋ถํฌ๊ฐ ๋์ด ์์ ๋ ์ข์ ์ด๋ฏธ์ง
1) ํ์คํ ๊ทธ๋จ ํํํ (Histogram Equalization)
: ์ข์ธก์ฒ๋ผ ํน์ ์์ญ์ ์ง์ค๋์ด ์๋ ๋ถํฌ๋ฅผ ์ค๋ฅธ์ชฝ์ฒ๋ผ ๊ณจ๊ณ ๋ฃจ ๋ถํฌํ๋๋ก ํ๋ ์์ ์ ํ์คํ ๊ทธ๋จ ํํํ
dst = cv2.equalizeHist(src, dst)
- src : ๋์ ์ด๋ฏธ์ง, 8๋นํธ 1 ์ฑ๋
- dst(optional) : ๊ฒฐ๊ณผ ์ด๋ฏธ์ง
+) ํ์์กฐ ์ด๋ฏธ์ง ํํํ
# ํ์์กฐ ์ด๋ฏธ์ง์ ํํํ ์ ์ฉ
import cv2
import numpy as np
import matplotlib.pylab as plt
#--โ ๋์ ์์์ผ๋ก ๊ทธ๋ ์ด ์ค์ผ์ผ๋ก ์ฝ๊ธฐ
img = cv2.imread('img/yate.jpg', cv2.IMREAD_GRAYSCALE)
rows, cols = img.shape[:2]
#--โก ์ดํ๋ผ์ด์ฆ ์ฐ์ฐ์ ์ง์ ์ ์ฉ
hist = cv2.calcHist([img], [0], None, [256], [0, 256]) #ํ์คํ ๊ทธ๋จ ๊ณ์ฐ
cdf = hist.cumsum() # ๋์ ํ์คํ ๊ทธ๋จ
cdf_m = np.ma.masked_equal(cdf, 0) # 0(zero)์ธ ๊ฐ์ NaN์ผ๋ก ์ ๊ฑฐ
cdf_m = (cdf_m - cdf_m.min()) /(rows * cols) * 255 # ์ดํ๋ผ์ด์ฆ ํ์คํ ๊ทธ๋จ ๊ณ์ฐ
cdf = np.ma.filled(cdf_m,0).astype('uint8') # NaN์ ๋ค์ 0์ผ๋ก ํ์
print(cdf.shape)
img2 = cdf[img] # ํ์คํ ๊ทธ๋จ์ ํฝ์
๋ก ๋งตํ
#--โข OpenCV API๋ก ์ดํ๋ผ์ด์ฆ ํ์คํ ๊ทธ๋จ ์ ์ฉ
img3 = cv2.equalizeHist(img)
#--โฃ ์ดํ๋ผ์ด์ฆ ๊ฒฐ๊ณผ ํ์คํ ๊ทธ๋จ ๊ณ์ฐ
hist2 = cv2.calcHist([img2], [0], None, [256], [0, 256])
hist3 = cv2.calcHist([img3], [0], None, [256], [0, 256])
#--โค ๊ฒฐ๊ณผ ์ถ๋ ฅ
cv2.imshow('Before', img)
cv2.imshow('Manual', img2)
cv2.imshow('cv2.equalizeHist()', img3)
hists = {'Before':hist, 'Manual':hist2, 'cv2.equalizeHist()':hist3}
for i, (k, v) in enumerate(hists.items()):
plt.subplot(1,3,i+1)
plt.title(k)
plt.plot(v)
plt.show()
- ๋ช ์ ๋๋น๊ฐ ๋ฎ์ ๋ถ๋ฃ๊ฐ ์ด๋ฏธ์ง์ ํ์ง์ด ๊ฐ์
- ๊น๋ฐ์ ์ ํ ๋จ์ด๊ฐ ๋ ๋๋ ทํ๊ฒ ๋จ
- ๋ถ๋ฃ๊ฐ ๋ฐ๋ฅ์ด ์ข๋ ๋ฐ์์ง
+) ์์ ์ด๋ฏธ์ง ํํํ
# ์์ ์ด๋ฏธ์ง์ ๋ํ ํํํ ์ ์ฉ
import numpy as np, cv2
img = cv2.imread('img/yate.jpg') #์ด๋ฏธ์ง ์ฝ๊ธฐ, BGR ์ค์ผ์ผ
#--โ ์ปฌ๋ฌ ์ค์ผ์ผ์ BGR์์ YUV๋ก ๋ณ๊ฒฝ
img_yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)
#--โก YUV ์ปฌ๋ฌ ์ค์ผ์ผ์ ์ฒซ๋ฒ์งธ ์ฑ๋์ ๋ํด์ ์ดํ๋ผ์ด์ฆ ์ ์ฉ
img_yuv[:,:,0] = cv2.equalizeHist(img_yuv[:,:,0])
#--โข ์ปฌ๋ฌ ์ค์ผ์ผ์ YUV์์ BGR๋ก ๋ณ๊ฒฝ
img2 = cv2.cvtColor(img_yuv, cv2.COLOR_YUV2BGR)
cv2.imshow('Before', img)
cv2.imshow('After', img2)
cv2.waitKey()
cv2.destroyAllWindows()
- ํํํ ์ฌ์ฉ ํ ์ด๋ฏธ์ง๊ฐ ๋ ์ ๋ช
- ์ํธ๊ฐ ๋ ๋ฐ์์ง๊ณ ํ์ง์ด ์ข์์ง
- ์ ์ฒด์ ์ผ๋ก ๋ ๋ฐ์์ง
- ์ด๋ฏธ์ง์ ์ ๋ช ๋๊ฐ ๋์์ง
4. CLAHE (Contrast Limited Adaptive Histogram Equalization)
ํํํ
: ์ด๋ฏธ์ง์ ๋ฐ์ ๋ถ๋ถ์ด ๋ ๋ผ๊ฐ
: ๊ฒฝ๊ณ์ ์ ์์๋ณผ ์ ์์
: ์ด๋ฅผ ๋ง๊ธฐ ์ํด ์ด๋ฏธ์ง๋ฅผ ์ผ์ ํ ์์ญ(tileGridSize ํ๋ผ๋ฏธํฐ) ์ผ๋ก ๋๋์ด ํํํ ์ ์ฉ
: ํ์ง๋ง ๋ ธ์ด์ฆ๊ฐ ์๊ฒจ ๋ฌธ์ ๊ฐ ๋ฐ์
CLAHE
: ์ด๋ค ์์ญ์ด๋ ์ง์ ๋ ์ ํ ๊ฐ(์๋ ์ฝ๋์์ clipLimit ํ๋ผ๋ฏธํฐ)์ ๋์ผ๋ฉด ๊ทธ ํฝ์ ์ ๋ค๋ฅธ ์์ญ์ ๊ท ์ผํ๊ฒ ๋ฐฐ๋ถํ์ฌ ์ ์ฉ
clahe = cv2.createCLAHE(clipLimit, tileGridSize)
- clipLimit : ๋๋น(Contrast) ์ ํ ๊ฒฝ๊ณ ๊ฐ, default=40.0
- tileGridSize : ์์ญ ํฌ๊ธฐ, default=8 x 8
- clahe : ์์ฑ๋ CLAHE ๊ฐ์ฒด
clahe.apply(src)
: CLAHE ์ ์ฉ
- src : ์
๋ ฅ ์ด๋ฏธ์ง
# CLAHE ์ ์ฉ
import cv2
import numpy as np
import matplotlib.pylab as plt
#--โ ์ด๋ฏธ์ง ์ฝ์ด์ YUV ์ปฌ๋ฌ์คํ์ด์ค๋ก ๋ณ๊ฒฝ
img = cv2.imread('img/clahe.png')
img_yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)
#--โก ๋ฐ๊ธฐ ์ฑ๋์ ๋ํด์ ์ดํ๋ผ์ด์ฆ ์ ์ฉ
img_eq = img_yuv.copy()
img_eq[:,:,0] = cv2.equalizeHist(img_eq[:,:,0])
img_eq = cv2.cvtColor(img_eq, cv2.COLOR_YUV2BGR)
#--โข ๋ฐ๊ธฐ ์ฑ๋์ ๋ํด์ CLAHE ์ ์ฉ
img_clahe = img_yuv.copy()
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8)) #CLAHE ์์ฑ
img_clahe[:,:,0] = clahe.apply(img_clahe[:,:,0]) #CLAHE ์ ์ฉ
img_clahe = cv2.cvtColor(img_clahe, cv2.COLOR_YUV2BGR)
#--โฃ ๊ฒฐ๊ณผ ์ถ๋ ฅ
cv2.imshow('Before', img)
cv2.imshow('CLAHE', img_clahe)
cv2.imshow('equalizeHist', img_eq)
cv2.waitKey()
cv2.destroyAllWindows()
- ํํํ ํ ๋๋ ๋ฐ์์ ธ์ ์ด๋ฏธ์ง๊ฐ ๋ ๋ผ๊ฐ
- CLAHE ํ ๋๋ ํฝ์ ์ ๊ท ์ผํ๊ฒ ๋ฐฐ๋ถํ์ฌ ์ด๋ฏธ์ง๊ฐ ๊ฐ์ ๋จ
- CLAHE ์ ์ฉํ ๋๋ CLAHE ๊ฐ์ฒด ์์ฑํ๊ณ , ๊ฐ์ฒด์ apply() ํจ์ ์ ์ฉํ๊ธฐ !
์ฌ๋ฐ๋ค. ์์ ์๊ฐ์ ๋ฐฐ์ด ์ด์ผ๊ธฐ๋ค์ด๊ตฐ..!