💡计算机视觉从入门到放弃(一): 用OpenCV实现图片锐化

7,953 阅读4分钟

最近打自走棋被打到心态崩了,为了找点别的事干就学习了一下计算机视觉。

作为本菜鸡入门CV的第一篇笔记,本文主要介绍了使用Python + OpenCV实现图片锐化的两种方法:

  • Unsharp Masking(反锐化掩模)
  • Laplacian Sharpening(拉普拉斯锐化)

1. Unsharp Masking

USM是一种主要通过增强图像中边缘部分的明暗变化幅度来提高视觉效果的锐化算法。

1.1 简单版本

其简易方法的实现如下图:

  1. 首先I是我们的输入图像,第一步把输入图像I高斯模糊后得到输出图片L
  2. 然后把原图I和模糊图像L相减,得到第二幅图片H,高斯模糊是低通滤波,因此相减后得到的H就是一个包含高通内容的图像。
  3. 最后将原图I与高频部分H叠加,我们就可以得到一个锐化以后的最终结果O

举个例子,以经典的乌蝇哥表情包为例:

输入图像 I.jpg

高斯模糊:

sigma = 3
kernel_size = (0,0)
L = cv2.GaussianBlur(I, kernel_size, sigma)

上面设置kernel_size=(0,0),是因为cv2.GaussianBlurksize均为0的情况下,可以用sigma值算出核大小。

为了让结果更加明显,这里我把sigma设置为3

模糊图像 L.jpg

相减: H = cv2.subtract(I, L)

增强后的高频部分 H.jpg

最后:

    a = 1.5
    O = cv2.addWeighted(I, 1, H, a, 0)

输出结果 O.jpg

其中a参数可调,1.5只是一个实验值。

嗯,可以很清楚地看到“食屎啦你”这几个字边缘很清楚地增强了...除此之外乌蝇哥脸部表情也变得狰狞了一点点...

完整代码如下

def shapen_USM(I):
    sigma = 3
    kernel_size = (0,0)
    L = cv2.GaussianBlur(I, kernel_size, sigma)
    L = L.astype(numpy.int32)
    I = I.astype(numpy.int32)
    H = I - L
    a = 1.5
    O = I + a*H
    O[O > 255] = 255
    O[O < 0] = 0
    O = O.astype(numpy.uint8)
    return O

其实也可以直接用cv2.addWeightedcv2.subtract方法,还不用类型转换了,只不过像上面这么写更加清晰一些。

1.2 改进版本

还有一种更改进的方法,这种方法则不是直接叠加高通部分H和原图I了,它引入了原图I的高对比图像C,原理如下图。

以这个大爷为例:

输入图像 I.jpg

使用同样的方法获得的Unsharp Mask

Unsharp Mask: H.jpg

然后使用CLAHE算法得到的高对比度版本(在这里我把原图转化成了LAB模式,只对L channel做了对比度增强,避免颜色变化)

最后叠加:

从左到右分别为: 原始Unsharp Mask生成的锐化图像,原始输入图像以及改善版本生成的锐化图像。

可以看到在应用同样的高斯模糊的情况下,改善后的锐化滤镜看起来要自然很多。

当然实际上很多参数是要调的,包括前面的Sigma和后面的各种threshold,上面的大爷图我也是为了让对比更加才用了个相对大的参数。

Talk is cheap:

# Contrast Enhancement
def CLAHE(I):
    lab = cv2.cvtColor(I, cv2.COLOR_BGR2LAB)
    l, a, b = cv2.split(lab)
    clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
    l = clahe.apply(l)
    O = cv2.merge([l, a, b])
    O = cv2.cvtColor(O, cv2.COLOR_LAB2BGR)
    return O
    
def shapen_USM_C(I):
    row, col, dem = I.shape
    sigma = 3
    kernel_size = (0, 0)
    C = CLAHE(I)
    L = cv2.GaussianBlur(I, kernel_size, sigma)
    L = L.astype(numpy.int32)
    I = I.astype(numpy.int32)
    H = I - L
    H[H > 255] = 255
    H[H < 0] = 0
    lab_H = cv2.cvtColor(H.astype(numpy.uint8), cv2.COLOR_BGR2LAB)
    l_H = cv2.split(lab_H)[0]
    threshold = 1
    O = I.copy()
    for i in range(row):
        for j in range(col):
            for k in range(dem):
                percent = l_H[i, j] / 100
                diff = C[i, j, k] - I[i, j, k]
                delta = percent * diff
                if abs(delta) >= threshold:
                    O[i, j, k] += delta
    O[O > 255] = 255
    O[O < 0] = 0
    O = O.astype(numpy.uint8)
    return O

2. Laplacian Sharpening

拉普拉斯锐化图像是根据图像某个像素的周围像素到此像素的突变程度有关,也就是说它的依据是图像像素的变化程度。

这种方法应该是比较经典的了,简单地说就是利用拉普拉斯算子对原图做卷积,获得一张处理后的图像再和原图叠加。

一个最基础的矩阵模板:

实际上,这种方法使用OpenCV实现起来十分简单:

def shapen_Laplacian(in_img):
    I = in_img.copy()
    kernel = numpy.array([[0, -1, 0], [-1, 4, -1], [0, -1, 0]])
    L = cv2.filter2D(I, -1, kernel)
    a = 0.5
    O = cv2.addWeighted(I, 1, L, a, 0)
    O[O > 255] = 255
    O[O < 0] = 0
    return O

其中cv2.filter2D这两行也可以换成L = cv2.Laplacian(I,-1)

上面分别就是原图(I),处理后得出的图像(L)与最后叠加生成的结果(O)

懒得说了。

小结

还是ins和美图秀秀用着方便😂