OpenCV实验(4):实现图像的联合双边滤波处理

实验要求:
给定函数:function im = jbf(D,C,w, sigma_f, sigma_g)
其中:D为输入图像;C为引导图像;W为滤波窗口大小;
sigma_f 为spatial kernel标准差;
sigma_g为range kernel 标准差;
给定公式:
在这里插入图片描述

其中:p是q的邻域中的一个像素。 f和g是空间和距离内核,通常以高斯的形式表示,I是输入图像。
f 是空间滤波器内核,定义为:exp([-d_ f^ 2]/[2*sigma_f^2])

其中:d_ f 为输入图像I的像素位置差,sigma_ f为空间滤波核函数的标准差
g是距离滤波器内核,定义为:exp([-d_g^ 2]/[2*sigma_g^2])
其中:d_g为引导图像 的像素灰度值差,sigma_g为滤波核函数的标准差

输入图像I下采样(1/2)得到LR低分辨率图像,再由LR图像上采样2倍得到引导图像。
注:图像缩放采用双线性插值。
实验思路:
1.根据双线性插值对图像缩放。双线性插值,是将输出图像dst的某像素(x, y)映射到原图像src中的位置,再根据公式计算像素的值。参考了https://blog.csdn.net/pentiumCM/article/details/104720100
2.滤波器的计算没什么难度。
实验效果:
输入时可以尝试的参数:
滤波器尺寸、空间滤波器sigma、距离滤波器sigma
3 1 1: 19秒,感觉几乎没变化
5 10 10:30秒,很轻微的变化
7 10 20:45秒,明显的变化
8 10 20:57秒,明显变化,还行
9 10 20:72秒,很不错,真实
13 13 30:131秒,非常漂亮
25 12.5 50:451秒,有点假
上面的451秒实在是太慢了,难以接受。通过大量修改代码使用numpy运算,451秒变为11秒左右,19秒变为6秒左右。目前还是比较满意的。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

实验代码:

import cv2 as cv
import numpy as np
import math


def jbf(D, C, W, sigma_f, sigma_g):
    """
    brief: 实现图像的联合双边滤波处理

    :param D: 输入图像
    :param C: 引导图像
    :param W: 滤波窗口大小
    :param sigma_f: spatial kernel 标准差
    :param sigma_g: range kernel 标准差
    :return: 元组(JBF后的图像,执行时间)
    """
    timeBegin = cv.getTickCount()  # 记录开始时间
    # 让变量更清楚一些
    imgSrc = D
    imgGuide = C
    size = W // 2 * 2 + 1  # 重新调整滤波器大小,保证为奇数
    sigmaSpatial = sigma_f
    sigmaRange = sigma_g

    # 两次采样后,可能会导致图像的shape不一致,因此选择最小值
    rowsSrc, colsSrc, channels = imgSrc.shape
    rowsGuide, colsGuide, channels = imgGuide.shape
    rows = min(rowsSrc, rowsGuide)
    cols = min(colsSrc, colsGuide)
    imgSrc = imgSrc[:rows, :cols, :]
    imgGuide = imgGuide[:rows, :cols, :]

    # Spatial Kernel
    kernelSpatial = np.zeros([size, size], dtype=np.float)
    center = size // 2  # 将滤波器分为size x size个小方格,中心点为center,坐标为(0, 0)
    for i in range(size):
        for j in range(size):
            x = i - center  # 方格的横坐标
            y = center - j  # 方格的纵坐标
            kernelSpatial[i, j] = np.exp(-(x ** 2 + y ** 2) / (2 * sigmaSpatial ** 2))
            #print(kernelSpatial[i, j], end=' ')  # 打印Spatial模板,与下一行共同使用
        #print('')  # 打印Spatial模板,与上一行共同使用

    # Range Kernel
    # 提前计算 2*sigmaRange**2代入公式,对于这个计算会提升25%的速度,但由于其本身时间仅为0.0004311s
    # 加速后也仅为0.0002932s,因此意义不大
    kernelRange = np.zeros([256], dtype=np.float)
    for i in range(256):
        kernelRange[i] = np.exp(-(i ** 2) / (2 * sigmaRange ** 2))
        #print(kernelRange[i], end=' ')  # 打印Range模板

    # 输出的图像
    imgDst = np.zeros([rows, cols, channels], dtype='uint8')

    # 边界填充,imgFillSrc为对原图的边界扩充,imgFillGuide为对引导图的边界扩充
    border = center  # 需要添加的边界大小
    imgFillSrc = cv.copyMakeBorder(imgSrc, border, border, border, border,
                                    borderType=cv.BORDER_REPLICATE)
    imgFillGuide = cv.copyMakeBorder(imgGuide, border, border, border, border,
                                    borderType=cv.BORDER_REPLICATE)
    # 为了便于计算,改为np.int
    imgFillSrc = imgFillSrc.astype(np.int)
    imgFillGuide = imgFillGuide.astype(np.int)

    # matSpatial就是kernelSpatial的三维形式
    matSpatial = np.zeros([size, size, channels], dtype=np.float)
    for k in range(channels):
        matSpatial[:, :, k] = kernelSpatial
    # matRange 储存距离滤波器内核对imgFillGuide的结果
    matRange = np.zeros([size, size, channels], dtype=np.float)

    # 开始滤波操作
    for i in range(rows):
        for j in range(cols):
            # 计算matRange。colorDiff是灰度差
            colorDiff = np.abs(imgFillGuide[i:i + size, j:j + size, :] - imgFillGuide[i + center, j + center, :])
            matRange = kernelRange[colorDiff]
            # matMulti 储存空间滤波器和内核滤波器相乘的结果
            matMulti = np.multiply(matSpatial, matRange)
            # sumMat为matMulti元素的和,matMulti的shape为(size, size, channels),sumMat的shape为(channels,)
            sumMat = np.sum(np.sum(matMulti, axis=0), axis=0)
            matMulti = np.multiply(matMulti, imgFillSrc[i:i+size, j:j+size, :])
            temp = np.sum(np.sum(matMulti, axis=0), axis=0) / sumMat
            # 限制在0~255范围
            temp = np.minimum(temp, 255)
            temp = np.maximum(temp, 0)
            imgDst[i, j, :] = temp

    timeEnd = cv.getTickCount()  # 记录结束时间
    time = (timeEnd - timeBegin) / cv.getTickFrequency()  # 计算总时间
    return imgDst, time


def sampling(imgSrc, scaleRow, scaleCol):
    """
    双线性插值法,来调整图片尺寸

    :param imgSrc: 原始图片
    :param scaleRow: x的倍数变化,eg. 0.5; 1; 2
    :param scaleCol: y的倍数变化,eg. 0.5; 1; 2
    :return: 元组(缩放后的图像,执行时间)
    """
    timeBegin = cv.getTickCount()  # 记录开始时间
    rowsSrc, colsSrc, channels = imgSrc.shape
    rowsDst = int(rowsSrc*scaleRow)
    colsDst = int(colsSrc*scaleCol)
    dst_img = np.zeros((rowsDst, colsDst, channels), dtype='uint8')
    # i:纵坐标y,j:横坐标x
    # 缩放因子,scaleCol 和 scaleRow,和函数的参数不一样
    scaleCol = colsSrc / colsDst
    scaleRow = rowsSrc / rowsDst

    for i in range(rowsDst):
        for j in range(colsDst):
            srcX = float((j + 0.5) * scaleCol - 0.5)
            srcY = float((i + 0.5) * scaleRow - 0.5)

            # 向下取整,代表靠近源点的左上角的那一点的行列号
            srcXint = math.floor(srcX)
            srcYint = math.floor(srcY)

            # 取出小数部分,用于构造权值
            srcXfloat = srcX - srcXint
            srcYfloat = srcY - srcYint

            if srcXint + 1 == colsSrc or srcYint + 1 == rowsSrc:
                dst_img[i, j, :] = imgSrc[srcYint, srcXint, :]
                continue
            dst_img[i, j, :] = (1. - srcYfloat) * (1. - srcXfloat) * imgSrc[srcYint, srcXint, :] + \
                               (1. - srcYfloat) * srcXfloat * imgSrc[srcYint, srcXint + 1, :] + \
                               srcYfloat * (1. - srcXfloat) * imgSrc[srcYint + 1, srcXint, :] + \
                               srcYfloat * srcXfloat * imgSrc[srcYint + 1, srcXint + 1, :]

    timeEnd = cv.getTickCount()  # 记录结束时间
    time = (timeEnd - timeBegin) / cv.getTickFrequency()  # 计算总时间
    return dst_img, time


size = int(input('Please input the size of kernel: '))
sigmaSpatial = float(input('Please input the value of Spatial sigma: '))
sigmaRange = float(input('Please input the value of Range sigma'))
"""
不断的优化后,下面的时间已经大为减少,如最后一个451秒,现在也只需要11.28秒,最开始的一个19秒,变为6.52秒
参数(按照上面顺序):
    3  1  1: 19秒,感觉几乎没变化
    5  10 10:30秒,很轻微的变化
    7  10 20:45秒,明显的变化
    8  10 20:57秒,明显变化,还行
    9  10 20:72秒,很不错,真实
    13 13 30:131秒,非常漂亮
    25 12.5 50:451秒,有点假
"""

imgSrc = cv.imread('../images/images2_2/2_2.png')
imgDownSamp, timeDown = sampling(imgSrc, 0.5, 0.5)  # 这里可以修改下采样率
imgUpSamp, timeUp = sampling(imgDownSamp, 2, 2)  # 这里可以修改上采样率
imgGuide = imgUpSamp

imgJBF, timeJBF = jbf(imgSrc, imgGuide, size, sigmaSpatial, sigmaRange)

print('DownSamp image(s):', timeDown)
print('UpSamp image(s):', timeUp)
print('JBF image(s):', timeJBF)

cv.imshow('source image', imgSrc)
cv.imshow('guide image', imgGuide)
cv.imshow('jbf image', imgJBF)

cv.waitKey(0)
cv.destroyAllWindows()
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值