第四节:图像平滑1

一:高斯平滑

      通过以上步骤我们写一个构建高斯卷积算子的代码

def getGaussKernel(sigma, H, W):    

    # 第一步:构建高斯矩阵
    gaussMatrix = np.zeros([H, W], np.float32)
    # 得到中心点的位置
    cH = (H - 1)/2
    cW = (W - 1)/2
    # 计算guass(sigma, r, c)
    for r in range(H):
        for c in range(W):
            norm2 = math.pow(r-cH, 2) + math.pow(c-cW, 2)
            gaussMatrix[r][c] = math.exp(-norm2/(2*math.pow(sigma, 2)))
    # 第二步:计算高斯矩阵的和
    sumGM = np.sum(gaussMatrix)
    # 第三步:归一化
    gaussKernel = gaussMatrix/sumGM
    return gaussKernel

     其实在真正的实践中,这个高斯卷积算子不用我们去实现,opencv给我们提供了函数cv2.getGaussianKernel()。。还有一点我想说一下,高斯算子是一个可分的算子,也就是分为一个子算子乘另一个子算子。。我们在做卷积的时候,图像可以先与水平高斯卷积核卷积,卷积出来的结果再与垂直方向的高斯卷积核卷积。。 我们做一个高斯卷积平滑的例子。。

import numpy as np
from scipy import signal
import cv2

def gaussBlur(image, sigma, H, W, _boundary = 'fill', _fillvalue=0):
    # 构建水平方向上的高斯卷积核
    gaussKernel_x = cv2.getGaussianKernel(sigma, W, cv2.CV_64F)
    # 转置
    gaussKernel_x = np.transpose(gaussKernel_x)
    # 图像矩阵与水平高斯卷积核卷积
    gaussBlur_x = signal.convolve2d(image, gaussKernel_x, mode='same', boundary=_boundary, fillvalue=_fillvalue)

    gaussKernel_y = cv2.getGaussianKernel(sigma, H, cv2.CV_64F)
    # 与垂直方向上的高斯卷积核卷积
    gaussBlur_xy = signal.convolve2d(gaussBlur_x, gaussKernel_y, mode='same', boundary=_boundary, fillvalue=_fillvalue)
    return gaussBlur_xy

if __name__ == '__main__':
    image = cv2.imread('p1.jpg', cv2.IMREAD_GRAYSCALE)
    # 显示原图
    cv2.imshow('origin', image)
    # 高斯平滑
    blurImage = gaussBlur(image, 5, 51, 51, 'symm')

    # 对blurImage进行灰度级显示
    blurImage = np.round(blurImage)
    blurImage = blurImage.astype(np.uint8)
    cv2.imshow('smooth', blurImage)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

输出结果:

二:均值平滑+快速均值平滑

        均值平滑

     快速均值平滑

然后,图像积分完成后,计算某个位置的像素值,用下面的公式。。

  下面是代码实现:
 

import numpy as np
import cv2

def integral(image):
    '''
    图像积分
    :param image:
    :return:
    '''
    rows, cols = image.shape

    # 行积分运算
    inteImageC = np.zeros((rows, cols), np.float32)
    for r in range(rows):
        for c in range(cols):
            if c == 0:
                inteImageC[r][c] = image[r][c]
            else:
                inteImageC[r][c] = inteImageC[r][c-1] + image[r][c]

    # 列积分
    interImage = np.zeros((rows, cols), np.float32)
    for c in range(cols):
        for r in range(rows):
            if r == 0:
                interImage[r][c] = inteImageC[r][c]
            else:
                interImage[r][c] = interImage[r-1][c] + inteImageC[r][c]
    # 先行再列  或者 先列再行  积分的结果是一致的

    # 上边和左边补零
    interImage_0 = np.zeros((rows+1, cols+1), np.float32)
    interImage_0[1:rows+1, 1:cols+1] = interImage
    return interImage_0

# 快速均值滤波
def fastMeanBlur(image, winSize, borderType=cv2.BORDER_DEFAULT):

    halfH = int((winSize[0] - 1) / 2)
    halfW = int((winSize[1] - 1) / 2)
    ratio = 1.0 / (winSize[0] * winSize[1])
    # 边界扩充
    paddImage = cv2.copyMakeBorder(image, halfH, halfH, halfW, halfW, borderType)
    # 图像积分
    paddIntegral = integral(paddImage)

    # 获取图像的高宽
    rows, cols = image.shape

    # 均值滤波后的结果
    meanBlurImage = np.zeros((rows, cols), np.float32)

    r, c = 0, 0
    for h in range(halfH, halfH+rows, 1):   # 这里要想到边界扩充
        for w in range(halfW, halfW+cols, 1):
            meanBlurImage[r][c] = (paddIntegral[h+halfH+1][w+halfW+1] + paddIntegral[h-halfH][w-halfW]
                                   -paddIntegral[h+halfH+1][w-halfW]-paddIntegral[h-halfH][w+halfW+1])*ratio
            c += 1
        r += 1
        c = 0
    return meanBlurImage

if __name__ == '__main__':

    image = cv2.imread('p1.jpg', cv2.IMREAD_GRAYSCALE)

    meanBlurImage = fastMeanBlur(image, (5, 5), borderType=cv2.BORDER_DEFAULT)
    meanBlurImage = meanBlurImage.astype(np.uint8)
    cv2.imshow('origin', image)
    cv2.imshow('meanBlurImage', meanBlurImage)

    # common_mean = common_mean_blur(image, (3, 3))
    # common_mean = np.round(common_mean)
    # common_mean = common_mean.astype(np.uint8)
    # cv2.imshow('common_image', common_mean)

    cv2.waitKey(0)
    cv2.destroyAllWindows()

输出结果:

 三:中值平滑

       中值平滑对有椒盐噪声的图片效果非常好。。所以我们下面将实验一下效果:先对图像加椒盐噪声,再对图像做中值平滑

   代码实现:

import numpy as np
import random
import cv2

# 先加一点椒盐噪声   1000个白点,1000个黑点
def salt(image, number):
    # 图像的宽,高
    rows, cols = image.shape
    # 加入椒盐噪声后的图像
    saltImage = np.copy(image)
    for i in range(number):
        randR = random.randint(0, rows - 1)
        randC = random.randint(0, cols - 1)
        saltImage[randR][randC] = 255
    for i in range(number):
        randR = random.randint(0, rows - 1)
        randC = random.randint(0, cols - 1)
        saltImage[randR][randC] = 0

    return saltImage

def medianBlur(image, winSize):
    # 图像的高,宽
    rows, cols = image.shape
    # 窗口的宽高均为奇数
    winH, winW = winSize
    halfWinH = int((winH - 1) / 2)
    halfWinW = int((winW - 1) / 2)

    # 中值滤波后的输出图像
    medianBlurImage = np.zeros(image.shape, image.dtype)
    for r in range(rows):
        for c in range(cols):
            # 判断边界
            rTop = 0 if r-halfWinH < 0 else r-halfWinH
            rBottom = rows-1 if r+halfWinH > rows-1 else r+halfWinH
            cLeft = 0 if c-halfWinW < 0 else c-halfWinW
            cRight = cols if c+halfWinW > cols-1 else c+halfWinW

            # 取邻域
            region = image[rTop:rBottom+1, cLeft:cRight+1]
            # 求中值
            medianBlurImage[r][c] = np.median(region)
    return medianBlurImage

if __name__ == '__main__':

    image = cv2.imread('p1.jpg', cv2.IMREAD_GRAYSCALE)
    saltImage = salt(image, 1000)

    # 用中值滤波平滑加了椒盐噪声的图片
    medianBlurImage = medianBlur(saltImage, (5, 5))
    
    cv2.imshow('saltImage', saltImage)
    cv2.imshow('meanImage', medianBlurImage)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

 输出结果:

四:双边滤波平滑

  代码实现:

import cv2
import numpy as np
import math

def getClosenessWeight(sigma_g, H, W):

    # 计算空间距离权重模板
    r, c = np.mgrid[0:H:1, 0:W:1]  # 构造三维表
    r -= int((H-1) / 2)
    c -= int((W-1) / 2)
    closeWeight = np.exp(-0.5*(np.power(r, 2)+np.power(c, 2))/math.pow(sigma_g, 2))
    return closeWeight

def bfltGray(I, H, W, sigma_g, sigma_d):

    # 构建空间距离权重模板
    closenessWeight = getClosenessWeight(sigma_g, H, W)

    # 模板的中心点位置
    cH = int((H - 1) / 2)
    cW = int((W - 1) / 2)

    # 图像矩阵的行数和列数
    rows, cols = I.shape

    # 双边滤波后的结果
    bfltGrayImage = np.zeros(I.shape, np.float32)
    for r in range(rows):
        for c in range(cols):
            pixel = I[r][c]
            # 判断边界
            rTop = 0 if r-cH < 0 else r-cH
            rBottom = rows-1 if r+cH > rows-1 else r+cH
            cLeft = 0 if c-cW < 0 else c-cW
            cRight = cols-1 if c+cW > cols-1 else c+cW

            # 权重模板的作用区域
            region = I[rTop:rBottom+1, cLeft:cRight+1]

            # 构建灰度值相似性的权重因子
            similarityWeightTemp = np.exp(-0.5*np.power(region-pixel, 2.0)/math.pow(sigma_d, 2))
            closenessWeightTemp = closenessWeight[rTop-r+cH:rBottom-r+cH+1, cLeft-c+cW:cRight-c+cW+1]

            # 两个权重模板相乘
            weightTemp = similarityWeightTemp * closenessWeightTemp

            # 归一化权重模板
            weightTemp = weightTemp / np.sum(weightTemp)

            # 权重模板和对应邻域值相乘求和
            bfltGrayImage[r][c] = np.sum(region * weightTemp)
    return bfltGrayImage

if __name__ == '__main__':

    image = cv2.imread('p1.jpg', cv2.IMREAD_GRAYSCALE)
    # 显示原图
    cv2.imshow('origin', image)

    # 将灰度值进行归一化
    image = image / 255.0

    # 双边滤波
    bfltImage = bfltGray(image, 33, 33, 19, 0.2)
    cv2.imshow('bfltImage', bfltImage)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

     双边滤波的代码运行不出来,不知道代码有问题还是电脑有问题?  大家复制过去自己跑一下。。。。

 

     未完待续。。。。。。图像平滑还有第二部分。。。