Date:2023.01.07
参考:
https://youcans.blog.csdn.net/article/details/125112487
https://www.bilibili.com/video/BV1RF41177xo/?spm_id_from=333.999.0.0&vd_source=9e9b4b6471a6e98c3e756ce7f41eb134
假如图像灰度值范围 [ 0 , L − 1 ] [0,L-1] [0,L−1]
image inverse 公式: s = L − 1 − r s=L-1-r s=L−1−r
作用或效果:
增强图像暗色区域的白色或灰色细节
手写代码
img = cv2.imread("./image/xiezhenmeinv.jpg")
img2 = 255-img
plt.subplot(1,2,1)
plt.imshow(img)
plt.subplot(1,2,2)
plt.imshow(img2)
plt.show()
# 对数变换
img = cv2.imread("./image/xiezhenmeinv.jpg",flags=0)
normImg = lambda x: 255. * (x-x.min()) / (x.max()-x.min()+1e-6)
fft = np.fft.fft2(img) # 傅里叶变换
fft_shift = np.fft.fftshift(fft) # 中心化
amp = np.abs(fft_shift) #频谱
amp = np.uint8(normImg(amp)) #映射到0-255
ampLog = np.abs(np.log(1+np.abs(fft_shift))) #对数变换
ampLog = np.uint8(normImg(ampLog))
plt.figure(figsize=(9, 5))
plt.subplot(131), plt.imshow(img, cmap='gray', vmin=0, vmax=255), plt.title('Original'), plt.axis('off')
plt.subplot(132), plt.imshow(amp, cmap='gray', vmin=0, vmax=255), plt.title("FFT spectrum"), plt.axis('off')
plt.subplot(133), plt.imshow(ampLog, cmap='gray', vmin=0, vmax=255), plt.title("FFT spectrum - log trans"), plt.axis('off')
plt.tight_layout()
plt.show()
要点:
imread
中形参flags =0
np.fft.fft2()
np.fft.fftshift()
np.abs()
#幂律变换
img = cv2.imread("./image/xiezhenmeinv.jpg",flags=0)
normImg = lambda x: 255. * (x-x.min()) / (x.max()-x.min()+1e-6)
img2 = np.power(img,0.5)
img2 = np.uint8(normImg(img2))
plt.figure(figsize=(9,5))
plt.subplot(121),plt.imshow(img, cmap='gray', vmin=0, vmax=255), plt.title('Original'), plt.axis('off')
plt.subplot(122),plt.imshow(img2, cmap='gray', vmin=0, vmax=255), plt.title('0.5 times'), plt.axis('off')
plt.show()
公式(例子) { y = s 1 r 1 x y = s 2 − s 1 r 2 − r 1 ( x − r 1 ) + s 1 y = L − 1 − s 2 L − 1 − r 2 ( x − r 2 ) + s 2 \left\{\begin{matrix} y = \frac{s_1}{r_1}x \\ y = \frac{s_2-s_1}{r_2-r_1}(x-r_1)+s_1\\ y = \frac{L-1-s_2}{L-1-r_2}(x-r_2)+s_2 \end{matrix}\right. ⎩ ⎨ ⎧y=r1s1xy=r2−r1s2−s1(x−r1)+s1y=L−1−r2L−1−s2(x−r2)+s2
解释
既 ( 0 , 0 ) 到 ( r 1 , s 1 ) (0,0)到(r_1,s_1) (0,0)到(r1,s1)点之间的直线执行第一个式子; ( r 1 , s 1 ) 到 ( r 2 , s 2 ) (r_1,s_1)到(r_2,s_2) (r1,s1)到(r2,s2)点之间的直线执行第二个式子,其他执行第三个式子。既分段执行不同的线性变换
手写代码(提升暗部区域)
# 分段线性变换
img = cv2.imread("./image/xiezhenmeinv.jpg",flags=0)
height,width = img.shape[:2]
imgStretch = np.empty((height,width),np.uint8)
r1,s1 = 40,80
r2,s2 = 200,180
k1 = s1/r1
k2 = (s2-s1)/(r2-r1)
k3 = (254-s2)/(254-r2)
for h in range(height):
for w in range(width):
if img[h,w]<r1:
imgStretch[h,w]= k1*img[h,w]
elif r1<=img[h,w]<=r2:
imgStretch[h,w] = k2*(img[h,w]-r1)+s1
else:
imgStretch[h,w] = k3*(img[h,w]-r2)+s2
plt.figure(figsize=(10,3.5))
plt.subplots_adjust(left=0.2, bottom=0.2, right=0.9, top=0.8, wspace=0.1, hspace=0.1)
plt.subplot(131), plt.title("s=T(r)")
x = [0, 40, 200, 254]
y = [0, 80, 180, 254]
plt.plot(x, y)
plt.axis([0,256,0,256])
plt.text(105, 25, "(r1,s1)", fontsize=10)
plt.text(120, 215, "(r2,s2)", fontsize=10)
plt.xlabel("r, Input value")
plt.ylabel("s, Output value")
plt.subplot(132), plt.imshow(img, cmap='gray', vmin=0, vmax=255), plt.title("Original"), plt.axis('off')
plt.subplot(133), plt.imshow(imgStretch, cmap='gray', vmin=0, vmax=255), plt.title("Stretch"), plt.axis('off')
plt.show()
公式 { y = s 1 ( r 1 < = r < = r 2 ) y = s 2 ( o t h e r s ) \left\{\begin{matrix} y = s1\space (r1<=r<=r2) \\y=s2\space(others) \end{matrix}\right. {y=s1 (r1<=r<=r2)y=s2 (others)
解释
将感兴趣的区域所有灰度值设置为一个值,其余灰度值设置为另一个值(或保持不变)
手写代码
# 灰度分层
img = cv2.imread("./image/xiezhenmeinv.jpg",flags=0)
height,width = img.shape[:2]
a,b = 30,70
imgC1 = img.copy()
imgC1[(img[:,:]<a)|(img[:,:]>b)]=0
imgC1[(img[:,:]>=a)&(img[:,:]<=b)]=254
imgC2 = img.copy()
imgC2[(img[:,:]>=a)&(img[:,:]<=b)]=254
plt.figure(figsize=(10, 6))
plt.subplot(131), plt.imshow(img, cmap='gray'), plt.title('Original'), plt.axis('off')
plt.subplot(132), plt.imshow(imgC1, cmap='gray'), plt.title('Binary layered'), plt.axis('off')
plt.subplot(133), plt.imshow(imgC2, cmap='gray'), plt.title('Grayscale layered'), plt.axis('off')
plt.show()
直方图既图像中各灰度值的个数,公式表达如下(归一化) p ( r k ) = n k M N p(r_k)=\frac{n_k}{MN} p(rk)=MNnk
累积直方图 h ( r k ) = ∑ i = 0 k n i h(r_k)=\sum_{i=0}^kn_i h(rk)=i=0∑kni
函数
np.histogram(img.flatten(), 256)
cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate ]]) → hist
说明
- images:输入图像,用 [] 括号表示
- channels: 直方图计算的通道,用 [] 括号表示
- mask:掩模图像,一般置为 None
- histSize:直方柱的数量,一般取 256
- ranges:像素值的取值范围,一般为 [0,256]
- 返回值 hist:返回每一像素值在图像中的像素总数,形状为 (histSize,1)
# 灰度分层
img = cv2.imread("./image/xiezhenmeinv.jpg",flags=0)
histCV = cv2.calcHist([img],[0],None,[256],[0,256])
histNP,bins = np.histogram(img.flatten(),256)
print(histCV.shape, histNP.shape) # histCV: (256, 1), histNP: (256,)
plt.figure(figsize=(10,3))
plt.subplot(131), plt.imshow(img, cmap='gray', vmin=0, vmax=255), plt.title("Original"), plt.axis('off')
plt.subplot(132,xticks=[], yticks=[]), plt.axis([-10,280,0,np.max(histCV)])
plt.bar(range(256), histCV[:,0]), plt.title("Gray Hist(cv2.calcHist)")
plt.subplot(133,xticks=[], yticks=[]), plt.axis([-10,280,0,np.max(histCV)])
plt.bar(bins[:-1], histNP), plt.title("Gray Hist(np.histogram)")
plt.show()
要点:
cv2.calcHist()
的参数都要用方括号。channels
表示处理的通道,mask
掩码,histSize
表示直方柱的数目,一般取256,range
表示像素值范围img.flatten()
表示图像展平xticks=[]
坐标轴空白plt.axis([x_left,x_right,y_bottom,y_top])
由于人眼视觉特性,直方图均匀分布的图像视觉效果较好。直方图均衡化的基本思想是对图像中占比大的灰度级进行展宽,而对占比小的灰度级进行压缩,使图像的直方图分布较为均匀,扩大灰度值差别的动态范围,从而增强图像整体的对比度。
因此,直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,本质上是根据直方图对图像进行线性或非线性灰度变换。
例如,直方图均衡化可以把原始图像的直方图调整到均匀分布,增加像素之间灰度值差别的动态范围,从而增强图像整体的对比
1. 公式推导
假设灰度映射 s = T ( r ) s=T(r) s=T(r)
假设:
(a) T ( r ) T(r) T(r)在 0 < = r < = L − 1 0<=r<=L-1 0<=r<=L−1为单调递增函数
(b) 对于 0 < = r < = L − 1 0<=r<=L-1 0<=r<=L−1,同样有 0 < = T ( r ) < = L − 1 0<=T(r)<=L-1 0<=T(r)<=L−1
上图两个面积相等,有公式 ∫ 0 a p r ( r k ) d r = ∫ 0 b p s ( s ) d s \int_{0}^ap_r(r_k)dr=\int_{0}^bp_s(s)ds ∫0apr(rk)dr=∫0bps(s)ds
对s求导,得 p s ( s ) = p r ( r ) ∣ d r d s ∣ p_s(s)=p_r(r)|\frac{dr}{ds}| ps(s)=pr(r)∣dsdr∣
如果输出为均匀分布,那么 p s ( s ) = 1 p_s(s)=1 ps(s)=1,那么上式推导的
p r ( r ) = d s d r p_r(r) = \frac{ds}{dr} pr(r)=drds
对 r r r积分,可得: s = T ( r ) = ∫ 0 x p r ( r ) d r s=T(r) = \int_{0}^{x}p_r(r)dr s=T(r)=∫0xpr(r)dr
离散化 s k = ∑ i = 0 k p r ( i ) s_k = \sum_{i=0}^kp_r(i) sk=i=0∑kpr(i)
3. 手写代码
#手写直方图均衡化
def get_pdf(img):
total = img.shape[0]*img.shape[1]
return [np.sum(img==i)/total for i in range(256)]
def hist_equal(img):
Pr = get_pdf(img) #概率密度
out_img = np.copy(img) #副本
SUMk =0. #累积值存储
for i in range(256):
SUMk = SUMk + Pr[i]
out_img[(img==i)] = SUMk*255.
return np.uint8(out_img)
img = cv2.imread("./image/xiezhenmeinv.jpg",flags=0)
img2 = hist_equal(img)
hist1 = cv2.calcHist([img],[0],None,[256],[0,256])
hist2 = cv2.calcHist([img2],[0],None,[256],[0,256])
plt.figure(figsize=(9,5))
plt.subplot(221,xticks=[],yticks=[]), plt.imshow(img,cmap='gray'),plt.title('original')
plt.subplot(222,xticks=[],yticks=[]), plt.imshow(img2,cmap='gray'),plt.title('original')
plt.subplot(223,xticks=[], yticks=[]), plt.axis([0,255,0,np.max(hist1)])
plt.bar(range(256), hist1[:,0]), plt.title("original")
plt.subplot(224,xticks=[], yticks=[]), plt.axis([0,255,0,np.max(hist2)])
plt.bar(range(256), hist2[:,0]), plt.title("hist_equal")
plt.show()
cv2.equalizeHist(src[, dst]) → dst
说明:
- src:输入图像
- 返回值 dst:输出图像,直方图均衡化
img = cv2.imread("./image/xiezhenmeinv.jpg",flags=0)
img2 = cv2.equalizeHist(img)
hist1 = cv2.calcHist([img],[0],None,[256],[0,256])
hist2 = cv2.calcHist([img2],[0],None,[256],[0,256])
plt.figure(figsize=(9,5))
plt.subplot(221,xticks=[],yticks=[]), plt.imshow(img,cmap='gray'),plt.title('original')
plt.subplot(222,xticks=[],yticks=[]), plt.imshow(img2,cmap='gray'),plt.title('original')
plt.subplot(223,xticks=[], yticks=[]), plt.axis([0,255,0,np.max(hist1)])
plt.bar(range(256), hist1[:,0]), plt.title("original")
plt.subplot(224,xticks=[], yticks=[]), plt.axis([0,255,0,np.max(hist2)])
plt.bar(range(256), hist2[:,0]), plt.title("hist_equal")
plt.show()