【图像处理学习笔记】 灰度变换

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

灰度变换

  • 背景
  • 基本灰度变换函数
    • 图像反转
    • 对数变换
    • 幂律(伽马)变换
    • 分段线性变换
    • 灰度级分层
  • 直方图
    • 直方图
    • 直方图均衡化

背景

  1. 空间域既是图像的像素平面,空间域技术操作图像中的像素。基于表达式 g ( x , y ) = T ( f ( x , y ) ) g(x,y)=T(f(x,y)) g(x,y)=T(f(x,y))其中 f ( x , y ) f(x,y) f(x,y)为输入图像, T T T为针对 f f f的算子, g ( x , y ) g(x,y) g(x,y)为输出图像
  2. 空间域操作包括了对单个像素点的操作,包括了对邻域内所有元素的操作(模板template),包括几何操作。
  3. 噪声图像表达式为 g ( x , y ) = f ( x , y ) + n ( x , y ) g(x,y)=f(x,y)+n(x,y) g(x,y)=f(x,y)+n(x,y)
  4. 计算机中的图像就是二维或三维(rgb)矩阵,操作都是对矩阵进行运算

基本灰度变换函数

图像反转

假如图像灰度值范围 [ 0 , L − 1 ] [0,L-1] [0,L1]

  1. image inverse 公式: s = L − 1 − r s=L-1-r s=L1r

  2. 作用或效果:
    增强图像暗色区域的白色或灰色细节

  3. 手写代码

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()

【图像处理学习笔记】 灰度变换_第1张图片

对数变换

  1. 公式 s = c l o g ( 1 + r ) s=clog(1+r) s=clog(1+r)
  2. 作用或效果
  • 原图动态范围过大,超出显示设备的允许动态范围,用对数变换来压缩
  • 较窄的低灰度值范围扩大,较高的高灰度值范围压缩(对数的性质),既提高暗色区域,降低明亮对比度
  1. 手写代码
# 对数变换
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()

要点:

  1. 读取灰度图imread中形参flags =0
  2. 傅里叶变换np.fft.fft2()
  3. 中心化np.fft.fftshift()
  4. 频谱np.abs()

【图像处理学习笔记】 灰度变换_第2张图片

幂律(伽马)变换

  1. 公式 s = c r γ s = cr^\gamma s=crγ
  • c = γ = 1 c=\gamma=1 c=γ=1,恒等变换
  • γ > 1 \gamma>1 γ>1,提升明亮区域对比度
  • γ < 1 \gamma<1 γ<1,提升暗区域对比度
  • 提升对比度就是说拉伸灰度值的范围
  1. 手写代码
#幂律变换
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()

【图像处理学习笔记】 灰度变换_第3张图片

分段线性变换

  1. 公式(例子) { 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=r2r1s2s1(xr1)+s1y=L1r2L1s2(xr2)+s2

  2. 解释
    ( 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)点之间的直线执行第二个式子,其他执行第三个式子。既分段执行不同的线性变换

  3. 手写代码(提升暗部区域)

# 分段线性变换
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()

【图像处理学习笔记】 灰度变换_第4张图片

灰度级分层

  1. 公式 { 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)

  2. 解释
    将感兴趣的区域所有灰度值设置为一个值,其余灰度值设置为另一个值(或保持不变)

  3. 手写代码

# 灰度分层
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()

【图像处理学习笔记】 灰度变换_第5张图片

直方图

直方图

  1. 直方图既图像中各灰度值的个数,公式表达如下(归一化) p ( r k ) = n k M N p(r_k)=\frac{n_k}{MN} p(rk)=MNnk

  2. 累积直方图 h ( r k ) = ∑ i = 0 k n i h(r_k)=\sum_{i=0}^kn_i h(rk)=i=0kni

  3. 函数

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()

要点:

  1. cv2.calcHist()的参数都要用方括号。channels表示处理的通道,mask掩码,histSize表示直方柱的数目,一般取256,range表示像素值范围
  2. img.flatten()表示图像展平
  3. 关于plt的一些知识
  • xticks=[] 坐标轴空白
  • plt.axis([x_left,x_right,y_bottom,y_top])

【图像处理学习笔记】 灰度变换_第6张图片

直方图均衡化

由于人眼视觉特性,直方图均匀分布的图像视觉效果较好。直方图均衡化的基本思想是对图像中占比大的灰度级进行展宽,而对占比小的灰度级进行压缩,使图像的直方图分布较为均匀,扩大灰度值差别的动态范围,从而增强图像整体的对比度。

因此,直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,本质上是根据直方图对图像进行线性或非线性灰度变换。

例如,直方图均衡化可以把原始图像的直方图调整到均匀分布,增加像素之间灰度值差别的动态范围,从而增强图像整体的对比

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<=L1为单调递增函数
(b) 对于 0 < = r < = L − 1 0<=r<=L-1 0<=r<=L1,同样有 0 < = T ( r ) < = L − 1 0<=T(r)<=L-1 0<=T(r)<=L1

【图像处理学习笔记】 灰度变换_第7张图片

上图两个面积相等,有公式 ∫ 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=0kpr(i)

2. 书中的例子
【图像处理学习笔记】 灰度变换_第8张图片

  • 计算出每个灰度值的个数,利用公式 p r ( r k ) = n k M N p_r(r_k)=\frac{n_k}{MN} pr(rk)=MNnk算出概率密度。
  • 利用公式 s k = T ( r ) = ( L − 1 ) ∑ j = 0 k p r ( r j ) s_k=T(r)=(L-1)\sum_{j=0}^kp_r(r_j) sk=T(r)=(L1)j=0kpr(rj)算出每个像素的输出值
  • 四舍五入

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()

【图像处理学习笔记】 灰度变换_第9张图片

  1. opencv函数

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()

【图像处理学习笔记】 灰度变换_第10张图片

你可能感兴趣的:(数字图像处理与OpenCV,图像处理,学习,计算机视觉)