python - 如何在 Python OpenCV 中改进可变镜头模糊算法?

标签 python numpy opencv image-processing blur

我想模拟廉价相机镜头的模糊效果(例如 Holga )。
靠近照片中心的地方模糊非常弱。
而且在接近弯道时变得更加果断。

我编写了代码,它通常可以工作。

输入图像:

Input image

结果图像:

Result image .

但我觉得可以做得更好更快。
我发现了类似的question但仍然没有答案。

如何提高算法速度并避免像素迭代?

更新:
它与具有恒定内核大小的标准高斯或 2D 滤镜模糊不同。

import cv2
import numpy as np
import requests
from tqdm import tqdm
import warnings
warnings.filterwarnings("ignore")

def blur(img=None, blur_radius=None, test=False):
    # test image loading
    if img is None:
        test=True
        print('test mode ON')
        print('loading image...')
        url = r'http://www.lenna.org/lena_std.tif'
        resp = requests.get(url, stream=True).raw
        img = np.asarray(bytearray(resp.read()), dtype="uint8")
        img = cv2.imdecode(img, cv2.IMREAD_COLOR)
        cv2.imwrite('img_input.png', img)
        print('image loaded')

    # channels splitting
    img_lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
    l, a, b = cv2.split(img_lab)
    if test:
        cv2.imwrite('l_channel.png', l)
        print('l channel saved')

    # make blur map 
    height, width = l.shape[:2]
    center = np.array([height/2, width/2])
    diag = ((height / 2) ** 2 + (width / 2) ** 2) ** 0.5
    blur_map = np.linalg.norm(
        np.indices(img.shape[:2]) - center[:,None,None] + 0.5,
        axis = 0
    )

    if blur_radius is None:
        blur_radius = int(max(height, width) * 0.03)

    blur_map = blur_map / diag 
    blur_map = blur_map * blur_radius
    if test:
        blur_map_norm = cv2.normalize(blur_map, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_32F)
        cv2.imwrite('blur_map.png', blur_map_norm)
        print('blur map saved')

    # very inefficient blur algorithm!!!
    l_blur = np.copy(l)
    for x in tqdm(range(width)):
        for y in range(height):
            kernel_size = int(blur_map[y, x])
       
            if kernel_size == 0:
                l_blur[y, x] = l[y, x]
                continue
            
            kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernel_size, kernel_size))
            cut = l[
                max(0, y - kernel_size):min(height, y + kernel_size),
                max(0, x - kernel_size):min(width, x + kernel_size)
            ]

            if cut.shape == kernel.shape:
                cut = (cut * kernel).mean()
            else:
                cut = cut.mean()

            l_blur[y, x] = cut
    if test: cv2.imwrite('l_blur.png', l_blur); print('l_blur saved')
    if test: print('done')
    return l_blur

blur() 

最佳答案

实现每个像素的内核都不同的过滤器的唯一方法是为每个像素创建内核并将其应用在循环中,就像 OP 的代码所做的那样。傅里叶变换不适用于这种情况。 Python是一种非常慢的语言,用编译语言实现的相同算法会快得多。除非在每个像素处创建内核的方式有一些预定义的结构,否则无法降低算法的复杂性。

例如,具有方核的均匀滤波器(通常称为“盒子”滤波器)可以基于积分图像进行计算,每个像素仅使用 4 次加法。此实现应该能够为每个像素选择不同的内核大小,而无需任何额外成本。

DIPlib 具有自适应高斯滤波器的实现 [免责声明:我是 DIPlib 的作者,但我没有实现此功能]。 Here is the documentation 。 该滤波器应用高斯滤波器,但高斯核在每个像素处进行不同的缩放和旋转。

镜头模糊不是高斯分布,但大多数情况下肉眼不容易看出差异;仅当存在具有高对比度的非常小的点时,差异才重要。

OP的案例将按如下方式实现:

import diplib as dip

img = dip.ImageRead('examples/trui.ics')

blur_map = dip.CreateRadiusSquareCoordinate(img.Sizes())
blur_map /= dip.Maximum(blur_map)

img_blur = dip.AdaptiveGauss(img, [0, blur_map], sigmas=[5])

(这里的blur_map定义不同,我选择了到中心距离的二次函数,因为我认为它看起来非常好;使用dip.CreateRadiusCooperative() 重现 OP 的 map )。

input image output image

我选择了最大模糊度 5(这是高斯的 sigma,以像素为单位,而不是内核的占用空间),并且 blur_map 在此使用以下因子缩放此 sigma: 0 位于图像的中间,1 位于图像的角落。

另一个有趣的效果如下,与图像中间中心的每个圆相切的模糊逐渐增加,径向模糊非常少:

angle_map = dip.CreatePhiCoordinate(img.Sizes())
img_blur = dip.AdaptiveGauss(img, [angle_map, blur_map], sigmas=[8,1])

output image

关于python - 如何在 Python OpenCV 中改进可变镜头模糊算法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72750870/

相关文章:

python - Asyncio 使用 StreamReader 解码 utf-8

python - 如何通过 Python 使用 Azure 诊断?

python - cv2.addWeighted 除了一些颜色

c++ - 在 OpenCV 中使用 inRange() 检测范围内的颜色

python - Pandas read_csv() : keep 0 as 0 (not convert it to NaN)

python - 将元素追加到列表并从另一个列表中删除

python - 每行numpy的快速列洗牌

python - 如何在 Python 中沿行合并二维数组

python - NumPy array_equal 函数未按预期工作

c++ - OpenCV GPU HOG 检测