OpenCV--Python 图像增强
整理时间:2020年5月26日星期二,本星期内重现下面的代码。
图像增强主要解决由于图像的灰度级范围较小造成的对比度较低的问题,目的就是将输出图像的灰度级放大到指定的程度,使得图像中的细节看起来增加清晰。对比度增强有几种常用的方法,如线性变换、分段线性变换、伽马变换、直方图正规化、直方图均衡化、局部自适应直方图均衡化等。
1.灰度直方图
在讲解图像增强的方法之前先来认识一下灰度直方图,灰度直方图是图像灰度级的函数,用来描述每个灰度级在图像矩阵中的像素个数或者占有率。接下来使用程序实现直方图:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
def calcGrayHist(I):
    # 计算灰度直方图
    h, w = I.shape[:2]
    grayHist = np.zeros([256], np.uint64)
    for i in range(h):
        for j in range(w):
            grayHist[I[i][j]] += 1
    return grayHist
img = cv.imread("../testImages/4/img1.jpg", 0)
grayHist = calcGrayHist(img)
x = np.arange(256)
# 绘制灰度直方图
plt.plot(x, grayHist, 'r', linewidth=2, c='black')
plt.xlabel("gray Label")
plt.ylabel("number of pixels")
plt.show()
# cv.imshow("img", img)
Matplotlib本身也提供了计算直方图的函数hist,以下由matplotlib实现直方图的生成:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img = cv.imread("../testImages/4/img1.jpg", 0)
h, w = img.shape[:2]
pixelSequence = shape([h * w, ])
numberBins = 256
histogram, bins, patch = plt.hist(pixelSequence, numberBins,
                                  facecolor='black', histtype='bar')
plt.xlabel("gray label")
plt.ylabel("number of pixels")
plt.axis([0, 255, 0, np.max(histogram)])
plt.show()
cv.imshow("img", img)
cv.waitKey()
图像的对比度是通过灰度级范围来度量的,而灰度级范围可通过观察灰度直方图得到,灰度级范围越大代表对比度越高;反之对比度越低,低对比度的图像在视觉上给人的感觉是看起来不够清晰,所以通过算法调整图像的灰度值,从而调整图像的对比度是有必要的。最简单的一种对比度增强的方法是通过灰度值的线性变换实现的。
2.线性变换
假设输入图像为I,宽为W,高为H,输出图像记为O,图像的线性变换可以用以下公式定义:
如下图所示,当a=1,b=0时,的一个副本;如果a>1,则输出图像的对比度比有所增大;如果0<a<1,则的对比度比有所减小。而b值的改变,影响的是输出图像的亮度,当b>0时,亮度增加;当b<0时,亮度减小。下面代码实现:
1.import numpy as np
2.a = np.array([[0, 200], [23, 4]], np.uint8)
3.b = 2 * a
4.print(b.dtype)
局部直方图均衡化
5.print(b)
6.'''
7.uint8
8.[[  0 144]
9. [ 46  8]]
10.
在上面代码中,输入的是一个uint8类型的ndarray,用数字2乘以该数组,返回的ndarray的数据类型是uint8。注意输出第0行第1列,200*2应该等于400,但是400超出了uint8的数据范围,Numpy是通过模运算归到uint8范围的,即400%256=144,从而转换成uint8类型。
如果将常数2改为2.0,虽然这个常数只是整型和浮点型的区别,但是结果却不一样。代码如下:
1.import numpy as np
2.a = np.array([[0, 200], [23, 4]], np.uint8)
3.b = 2.0 * a
4.print(b.dtype)
5.print(b)
6.'''
7.float64
8.[[  0.  400.]
9. [  46.    8.]]
10.
可以发现返回的ndarray的数据类型变成了float64,也就是说,相乘的常数是2和2.0会导致返回的ndarray的数据类型不一样,就会造成200*2的返回值是144,而200*2.0的值却是400;而对8位图进行对比度增强来说,线性变换计算出的输出值可能要大于255,需要将这些值截断为255,而不是取模运算,所以不能简单地只是用“*”运算来实现线性变换。具体代码如下:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 绘制直方图函数
def grayHist(img):
    h, w = img.shape[:2]
    pixelSequence = shape([h * w, ])
    numberBins = 256
    histogram, bins, patch = plt.hist(pixelSequence, numberBins,
                                      facecolor='black', histtype='bar')
    plt.xlabel("gray label")
    plt.ylabel("number of pixels")
    plt.axis([0, 255, 0, np.max(histogram)])
    plt.show()
img = cv.imread("../testImages/4/img4.jpg", 0)
out = 2.0 * img
# 进行数据截断,大于255的值截断为255
out[out > 255] = 255
# 数据类型转换
out = np.around(out)
out = out.astype(np.uint8)
# 分别绘制处理前后的直方图
# grayHist(img)
# grayHist(out)
cv.imshow("img", img)
cv.imshow("out", out)
cv.waitKey()
以上线性变换是对整个灰度级范围使用了相同的参数,有的时候也需要针对不同灰度级范围进行不同的线性变换,这就是常用的分段线性变换,经常用于降低较亮或较暗区域的对比度来增强灰度级处于中间范围的对比度,或者压低中间灰度级处的对比度来增强较亮或者较暗区域的对比度。从下图(a)的灰度直方图(b)可以看出,图像的灰度主要集中在 [100,150]之间,可以通过以下分段线性变换将主要的灰度级拉伸到[50,230],结果如图(c)所示,对比度拉伸后显然比原图能够更加清晰地看到更多的细节。
img = cv.imread("../testImages/4/img7.jpg", 0)
img = cv.resize(img, None, fx=0.3, fy=0.3)
h, w = img.shape[:2]
out = np.zeros(img.shape, np.uint8)
for i in range(h):
    for j in range(w):
        pix = img[i][j]
        if pix < 50:
            out[i][j] = 0.5 * pix
        elif pix < 150:
            out[i][j] = 3.6 * pix - 310
        else:
            out[i][j] = 0.238 * pix + 194
        # 数据类型转换
out = np.around(out)
out = out.astype(np.uint8)
# grayHist(img)
# grayHist(out)
cv.imshow("img", img)
cv.imshow("out", out)
cv.waitKey()
线性变换的参数需要根据不同的应用及图像自身的信息进行合理的选择,可能需要进行多次测试,所以选择合适的参数是相当麻烦的。直方图正规化就是基于当前图像情况自动选取a和b的值的方法,下面介绍这种方法。
3.直方图正规化
下面使用Python代码实现直方图正规化:
img = cv.imread("../testImages/4/img6.jpg", 0)
# 计算原图中出现的最小灰度级和最大灰度级
# 使用函数计算
Imin, Imax = cv.minMaxLoc(img)[:2]
# 使用numpy计算
# Imax = np.max(img)
# Imin = np.min(img)
Omin, Omax = 0, 255
# 计算a和b的值
a = float(Omax - Omin) / (Imax - Imin)
b = Omin - a * Imin
out = a * img + b
out = out.astype(np.uint8)
cv.imshow("img", img)

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。