如何在OpenCV中对图像进行锐化处理?

158

我如何使用OpenCV来增强图像的清晰度?

有许多平滑或模糊的方法,但似乎没有任何增强清晰度的方法。

9个回答

191

一般的步骤可以在维基百科关于反遮罩处理中找到:

使用高斯平滑滤波器,并以加权方式从原始图像中减去平滑版本(使常量区域的值保持不变)。

要将frame的锐化版本转换为image: (两者均为cv::Mat类型)

cv::GaussianBlur(frame, image, cv::Size(0, 0), 3);
cv::addWeighted(frame, 1.5, image, -0.5, 0, image);

那里的参数需要您自行调整。

还有拉普拉斯锐化,在谷歌上搜索应该能找到相关内容。


1
有没有一种方法可以复制 Photoshop 的反锐化掩模的效果? - Royi
@Drazick 你是因为无法复制而问吗?上面已经给出了维基百科的链接。具体来说,是数字化反锐化掩蔽技术。 - hlkstuv_23900
@tilaprimera,我问这个是因为Photoshop的USM与“经典”的USM不同。 - Royi
1
请确保 frame 是 float32 类型,而不是 int8。否则将输出一个具有混乱舍入的图像,这在视觉上不会引起注意,但在每个通道的直方图上会清晰可见。 - apatsekin

71

你可以尝试一个简单的内核filter2D函数,例如在Python中:

kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]])
im = cv2.filter2D(im, -1, kernel)

Wikipedia提供了一个良好的内核概述,其中包含一些更多示例 - https://en.wikipedia.org/wiki/Kernel_(image_processing)

在图像处理中,内核(也称卷积矩阵或掩模)是一个小矩阵。它用于模糊、锐化、浮雕、边缘检测等操作。这是通过对内核和图像进行卷积实现的。


2
这基本上是“拉普拉斯”滤波器(8个邻居)。它增强了边缘、角落等高频区域。4个邻居的拉普拉斯滤波器:[[0,-1,0],[-1,5,-1],[0,-1,0]]。 - SKG
使用不正确的内核变量进行锐化。请使用以下变量:https://dev59.com/nG445IYBdhLWcg3wLXWh#63864292 - leenremm
1
他们是针对8个像素的邻域进行校正的。@leenremm提供的那些是针对4个像素的邻域进行校正的。 - vatbub
1
它们对于一个8像素的邻域是正确的。@leenremm链接的那些对于一个4像素的邻域是正确的。 - undefined

34

您可以使用锐化掩蔽(unsharp mask)来锐化图像。您可以在这里找到有关锐化掩蔽的更多信息。以下是使用OpenCV的Python实现代码:

import cv2 as cv
import numpy as np

def unsharp_mask(image, kernel_size=(5, 5), sigma=1.0, amount=1.0, threshold=0):
    """Return a sharpened version of the image, using an unsharp mask."""
    blurred = cv.GaussianBlur(image, kernel_size, sigma)
    sharpened = float(amount + 1) * image - float(amount) * blurred
    sharpened = np.maximum(sharpened, np.zeros(sharpened.shape))
    sharpened = np.minimum(sharpened, 255 * np.ones(sharpened.shape))
    sharpened = sharpened.round().astype(np.uint8)
    if threshold > 0:
        low_contrast_mask = np.absolute(image - blurred) < threshold
        np.copyto(sharpened, image, where=low_contrast_mask)
    return sharpened

def example():
    image = cv.imread('my-image.jpg')
    sharpened_image = unsharp_mask(image)
    cv.imwrite('my-sharpened-image.jpg', sharpened_image)

这似乎是一个非常方便的版本。你能否添加一些关于参数的更多信息呢?核大小和sigma可以在OpenCV中查找,但是amount和threshold呢?谢谢! - choise
2
@choise amount 只是锐化的程度。例如,2.0 的 amount 值相对于默认值 1.0 给出了更清晰的图像。threshold 是低对比度掩膜的阈值。换句话说,输入和模糊图像之间差异小于 threshold 的像素将保持不变。 - Soroush

18

任何图像都是由各种频率的信号集合组成的。高频率控制边缘,低频率控制图像内容。当相邻单元格中的像素值发生突然变化(如0和255)时,就会形成边缘。显然这是一个急剧的变化,因此产生了边缘和高频率。为了锐化图像,这些转换可以进一步增强。

一种方法是将自制的滤波器核与图像卷积。

    import cv2
    import numpy as np

    image = cv2.imread('images/input.jpg')
    kernel = np.array([[-1,-1,-1], 
                       [-1, 9,-1],
                       [-1,-1,-1]])
    sharpened = cv2.filter2D(image, -1, kernel) # applying the sharpening kernel to the input image & displaying it.
    cv2.imshow('Image Sharpening', sharpened)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

另一种从图像的明亮版本中减去模糊版本的方法有助于锐化图像。但是应该小心使用,因为我们只是增加像素值。想象一下灰度像素值为190,如果乘以2的权重,就会变成380,但由于最大允许的像素范围而被截断为255。这是信息损失,会导致图像变得发白。

    addWeighted(frame, 1.5, image, -0.5, 0, image);

对不起,但内核中心应该是8而不是9吧? - Farhood ET
@FarhoodET 不,它必须加起来等于1才能保持亮度。想一想如果图像是恒定的。 - fferen

17
你可以在OpenCV文档中找到有关使用“unsharp mask”算法进行图像锐化的示例代码。
更改sigmathresholdamount的值将产生不同的结果。
// sharpen image using "unsharp mask" algorithm
Mat blurred; double sigma = 1, threshold = 5, amount = 1;
GaussianBlur(img, blurred, Size(), sigma, sigma);
Mat lowContrastMask = abs(img - blurred) < threshold;
Mat sharpened = img*(1+amount) + blurred*(-amount);
img.copyTo(sharpened, lowContrastMask);

11

为了更好地理解本主题,需要明确以下几点:

  1. 锐化图像是一个不适定问题。换句话说,模糊是一种有损操作,通常无法从中恢复。
  2. 要对单个图像进行锐化,您需要在所需图像的类型和其如何变得模糊方面添加约束(假设)。这是自然图像统计领域。锐化的方法通过显式或隐式地使用算法来保留这些统计数据(深度学习是最隐式编码的方法)。常见的方法是加权DOG或拉普拉斯金字塔分解的某些级别,这是Brian Burns答案的概括,假设高斯模糊了图像,并且加权方式与图像原始内容的假设相关联。
  3. 其他信息源可以使锐化问题成为适定问题。常见的信息源是移动对象的视频或多视角设置。在该设置中进行锐化通常称为超分辨率(这是一个非常糟糕的名称,但它已经在学术圈中流行起来)。OpenCV中有超分辨率方法很长时间了...虽然我上次检查时它们通常效果不太好。我期望深度学习在这方面也产生了一些出色的结果。也许有人会在评论中发布有价值的信息。

6

您也可以尝试使用这个筛选器

sharpen_filter = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])
sharped_img = cv2.filter2D(image, -1, sharpen_filter)

3
为了让图像更加清晰,我们可以使用滤镜(就像之前许多答案中提到的)。
kernel = np.array([[-1, -1, -1],[-1, 8, -1],[-1, -1, 0]], np.float32) 

kernel /= denominator * kernel

当分母为1时,它将达到最大值,并随着增加而减小(2.3..)

最常用的是分母为3时。

以下是实现方法。

kernel = np.array([[-1, -1, -1],[-1, 8, -1],[-1, -1, 0]], np.float32) 

kernel = 1/3 * kernel

dst = cv2.filter2D(image, -1, kernel)

在“它将是最”附近似乎缺少了一些东西。 - Peter Mortensen

-7

试试这个:

cv::bilateralFilter(img, 9, 75, 75);

你可以在这里找到更多信息。


9
问题是关于图像锐化,而不是边缘保留平滑。 - Michael Burdinov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接