7/28 opencv-python学习笔记

高斯平滑

高斯平滑即采用高斯卷积核对图像矩阵进行卷积操作。高斯卷积核是一个近似服从高斯分布的矩阵,随着距离中心点的距离增加,其值变小。这样进行平滑处理时,图像矩阵中锚点处像素值权重大,边缘处像素值权重小,下为一个3*3的高斯卷积核:
在这里插入图片描述
opencv中提供了GaussianBlur()函数来进行高斯平滑,其对应参数如下:

dst = cv2.GaussianBlur(src,ksize,sigmaX,sigmay,borderType)
        src: 输入图像矩阵,可为单通道或多通道,多通道时分别对每个通道进行卷积
        dst:输出图像矩阵,大小和数据类型都与src相同
        ksize:高斯卷积核的大小,宽,高都为奇数,且可以不相同
        sigmaX: 一维水平方向高斯卷积核的标准差
        sigmaY: 一维垂直方向高斯卷积核的标准差,默认值为0,表示与sigmaX相同
        borderType:填充边界类型

代码使用示例和效果如下:(相比于原图,平滑后图片变模糊)

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np


img = cv.imread(r"D:\new20200726213331.png")
img_gauss = cv.GaussianBlur(img,(3,3),1)
cv.imshow("img",img)
cv.imshow("img_gauss",img_gauss)
cv.waitKey(0)
cv.destroyAllWindows()

在这里插入图片描述
在这里插入图片描述
对于上面的高斯卷积核,可以由如下两个矩阵相乘进行构建,说明高斯核是可分离卷积核,因此高斯卷积操作可以分成先进行垂直方向的一维卷积,再进行一维水平方向卷积。
在这里插入图片描述
  opencv中getGaussianKernel()能用来产生一维的高斯核,分别获得水平和垂直的高斯核,分两步也能完成高斯卷积,获得和GaussianBlur一样的结果。其参数如下:

cv2.getGaussianKernel(ksize,sigma,ktype)
        ksize:奇数,一维核长度
        sigma:标准差
        ktype:数据格式,应该为CV_32F 或者 CV_64F返回矩阵如下:垂直的矩阵[[ 0.27406862] [ 0.45186276] [ 0.27406862]
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
from scipy import signal

#convolve2d只是对单通道进行卷积,若要实现cv.GaussianBlur()多通道高斯卷积,需要拆分三个通道进行,再合并

def gaussianBlur(img,h,w,sigma,boundary="fill",fillvalue=0):
    kernel_x = cv.getGaussianKernel(w,sigma,cv.CV_64F)   #默认得到的为垂直矩阵
    
    kernel_x = np.transpose(kernel_x)  #转置操作,得到水平矩阵
    
    #水平方向卷积
    gaussian_x = signal.convolve2d(img,kernel_x,mode="same",boundary=boundary,fillvalue=fillvalue)
    
    #垂直方向卷积
    kernel_y = cv.getGaussianKernel(h,sigma,cv.CV_64F)
    gaussian_xy = signal.convolve2d(gaussian_x,kernel_y,mode="same",boundary=boundary,fillvalue=fillvalue)
    
    #cv.CV_64F数据转换为uint8
    gaussian_xy = np.round(gaussian_xy)
    gaussian_xy = gaussian_xy.astype(np.uint8)
    
    return gaussian_xy

if __name__=="__main__":
    img = cv.imread(r"C:\Users\Administrator\Desktop\timg.jpg",0)
    img_gauss = gaussianBlur(img,3,3,1)
    cv.imshow("img",img)
    cv.imshow("img_gauss",img_gauss)
    cv.waitKey(0)
    cv.destroyAllWindows()

均值平滑

高斯卷积核,对卷积框中像素值赋予不同权重,而均值平滑赋予相同权重,一个3*5的均值卷积核如下,均值卷积核也是可分离的。
在这里插入图片描述opencv的boxFilter()函数和blur()函数都能用来进行均值平滑,其参数如下:

cv2.boxFilter(src,ddepth,ksize,dst,anchor,normalize,borderType)
        src: 输入图像对象矩阵,
        ddepth:数据格式,位深度
        ksize:高斯卷积核的大小,格式为(宽,高)
        dst:输出图像矩阵,大小和数据类型都与src相同
        anchor:卷积核锚点,默认(-1,-1)表示卷积核的中心位置
        normalize:是否归一化 (若卷积核3*5,归一化卷积核需要除以15)
        borderType:填充边界类型
        
    cv2.blur(src,ksize,dst,anchor,borderType)
        src: 输入图像对象矩阵,可以为单通道或多通道
        ksize:高斯卷积核的大小,格式为(宽,高)
        dst:输出图像矩阵,大小和数据类型都与src相同
        anchor:卷积核锚点,默认(-1,-1)表示卷积核的中心位置
        borderType:填充边界类型

示例代码和使用效果如下:

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np

img = cv.imread(r"C:\Users\Administrator\Desktop\timg.jpg")
img_blur = cv.blur(img,(3,5))
# img_blur = cv.boxFilter(img,-1,(3,5))
cv.imshow("img",img)
cv.imshow("img_blur",img_blur)
cv.waitKey(0)
cv.destroyAllWindows()

在这里插入图片描述

中值平滑

中值平滑也有核,但并不进行卷积计算,而是对核中所有像素值排序得到中间值,用该中间值来代替锚点值。opencv中利用medianBlur()来进行中值平滑,中值平滑特别适合用来去除椒盐噪声,其参数如下:

  cv2.medianBlur(src,ksize,dst)
        src: 输入图像对象矩阵,可以为单通道或多通道
        ksize:核的大小,格式为 3      #注意不是(3,3)
        dst:输出图像矩阵,大小和数据类型都与src相同

其使用代码及效果如下:(加上的白点噪声都被平滑掉了)

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
import random

img = cv.imread(r"C:\Users\Administrator\Desktop\timg.jpg")
rows,cols = img.shape[:2]

#加入椒盐噪声
for i in range(100):
    r = random.randint(0,rows-1)
    c = random.randint(0,cols-1)
    img[r,c]=255


img_medianblur = cv.medianBlur(img,5)

cv.imshow("img",img)
cv.imshow("img_medianblur",img_medianblur)
cv.waitKey(0)
cv.destroyAllWindows()

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

中值滤波自己实现代码如下:

import heapq
import cv2

#实现图片的中值滤波算法
# 2D median filter with no stride, zero padding

def medain_filter(img, kernel_w, kernel_h):
    """
    img: cv2 matrix
    kernel_w: kernel width
    kernel_h: kernel height
    """
    rows, cols = img.shape[:2]
    for i in range(rows):
        for j in range(cols):
            left = max(0, j-kernel_w//2)
            right = min(cols-1, j+kernel_w//2)
            upper = max(0, i-kernel_h//2)
            lower = min(rows-1, i+kernel_h//2)
            
            #kernel中的像素点放入堆中
            hq = []
            for m in range(upper, lower+1):
                for n in range(left, right+1):
                    heapq.heappush(hq, img[m, n])
            size = (right-left+1)*(lower-upper+1)
            if size&1:  #奇数个元素,取中间值
                for k in range(0, (size+1)//2):
                    median = heapq.heappop(hq)
                img[i, j] = median
            else:  #偶数个元素,取中间两个元素平均值
                for k in range(0, (size+1)//2):
                    median1 = heapq.heappop(hq)
                median2 = heapq.heappop(hq)
                img[i, j] = (median1+median2)//2
    return img

if __name__ == "__main__":
    img = cv2.imread(r"C:\Users\Administrator\Desktop\dog.jpg", 0)
    img2 = medain_filter(img, 3, 3)
    cv2.imshow("img", img)
    cv2.imshow("img2", img2)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

双边滤波

相比于上面几种平滑算法,双边滤波在平滑的同时还能保持图像中物体的轮廓信息。双边滤波在高斯平滑的基础上引入了灰度值相似性权重因子,所以在构建其卷积核核时,要同时考虑空间距离权重和灰度值相似性权重。在进行卷积时,每个位置的邻域内,根据和锚点的距离d构建距离权重模板,根据和锚点灰度值差异r构建灰度值权重模板,结合两个模板生成该位置的卷积核。opencv中的bilateralFilter()函数实现了双边滤波,其参数对应如下:

dst = cv2.bilateralFilter(src,d,sigmaColor,sigmaSpace,borderType)
        src: 输入图像对象矩阵,可以为单通道或多通道
        d:用来计算卷积核的领域直径,如果d<=0,从sigmaSpace计算d
        sigmaColor:颜色空间滤波器标准偏差值,决定多少差值之内的像素会被计算(构建灰度值模板)
        sigmaSpace:坐标空间中滤波器标准偏差值。如果d>0,设置不起作用,否则根据它来计算d值(构建距离权重模板)
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
import random
import math

img = cv.imread(r"C:\Users\Administrator\Desktop\timg.jpg")
img_bilateral = cv.bilateralFilter(img,0,0.2,40)

cv.imshow("img",img)
cv.imshow("img_bilateral",img_bilateral)
cv.waitKey(0)
cv.destroyAllWindows()

在这里插入图片描述
同样,利用numpy也可以自己实现双边滤波算法,同样需要对每个通道进行双边滤波,最后进行合并,下面代码只对单通道进行了双边滤波,代码和效果如下图:

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
import random
import math


def getDistanceWeight(sigmaSpace,H,W):
    r,c = np.mgrid[0:H:1,0:W:1]

    r = r-(H-1)/2
    c =c-(W-1)/2
    distanceWeight = np.exp(-0.5*(np.power(r,2)+np.power(c,2))/math.pow(sigmaSpace,2))
    return distanceWeight

def bilateralFilter(img,H,W,sigmaColor,sigmaSpace):
    distanceWeight = getDistanceWeight(sigmaSpace,H,W)
    cH = (H-1)/2
    cW = (W-1)/2
    rows,cols = img.shape[:2]
    bilateralImg = np.zeros((rows,cols),np.float32)
    for r in range(rows):
        for c in range(cols):
            pixel = img[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=img[rTop:rBottom+1,cLeft:cRight+1]
            
            #灰度值差异权重
            colorWeight = np.exp(0.5*np.power(region-pixel,2.0)/math.pow(sigmaColor,2))
            print(colorWeight.shape)
            #距离权重
            distanceWeightTemp = distanceWeight[cH-(r-rTop):rBottom-r+cH+1,cW-(c-cLeft):cRight-c+cW+1]
            print(distanceWeightTemp.shape)
            #权重相乘并归一化
            weightTemp = colorWeight*distanceWeightTemp
            weightTemp = weightTemp/np.sum(weightTemp)
            bilateralImg[r][c]=np.sum(region*weightTemp)
    return bilateralImg

if __name__=="__main__":
    img = cv.imread(r"C:\Users\Administrator\Desktop\timg.jpg",0)
    img_temp = img/255.0
    img_bilateral = bilateralFilter(img_temp,3,3,0.2,19)*255
    img_bilateral[img_bilateral>255] = 255
    img_bilateral = img_bilateral.astype(np.uint8)
    cv.imshow("img",img)
    cv.imshow("img_bilateral",img_bilateral)
    cv.waitKey(0)
    cv.destroyAllWindows()

在这里插入图片描述

联合双边滤波

双边滤波是根据原图中不同位置灰度相似性来构建相似性权重模板,而联合滤波是先对原图进行高斯平滑,然后根据平滑后的图像灰度值差异建立相似性模板,再与距离权重模板相乘得到最终的卷积核,最后再对原图进行处理。所以相比于双边滤波,联合双边滤波只是建立灰度值相似性模板的方法不一样。
联合双边滤波作为边缘保留滤波算法时,进行joint的图片即为自身原图片,如果将joint换为其他引导图片,联合双边滤波算法还可以用来实现其他功能。opencv 2中不支持联合双边滤波,opencv 3中除了主模块,还引入了contrib,其中的ximgproc模块包括了联合双边滤波的算法。因此如果需要使用opencv的联合双边滤波,需要安装opencv-contrib-python包。

安装opencv主模块和contrib附加模块步骤:
  pip uninstall opencv-python (如果已经安装opencv-python包,先卸载)
  pip install opencv-contrib-python

联合双边滤波: cv2.xmingproc.jointBilateralFilter(), 其相关参数如下:

dst = cv2.xmingproc.jointBilateralFilter(joint,src,d,sigmaColor,sigmaSpace,borderType)
        joint: 进行联合滤波的导向图像,可以为单通道或多通道,保持边缘的滤波算法时常采用src
        src: 输入图像对象矩阵,可以为单通道或多通道
        d:用来计算卷积核的领域直径,如果d<0,从sigmaSpace计算d
        sigmaColor:颜色空间滤波器标准偏差值,决定多少差值之内的像素会被计算(构建灰度值模板)
        sigmaSpace:坐标空间中滤波器标准偏差值。如果d>0,设置不起作用,否则根据它来计算d值(构建距离权重模板)

下面是联合双边滤波的使用代码和效果:(采用src的高斯平滑图片作为joint)

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
import random
import math

src = cv.imread(r"C:\Users\Administrator\Desktop\timg.jpg")
joint  = cv.GaussianBlur(src,(7,7),1,0)
dst = cv.ximgproc.jointBilateralFilter(joint,src,33,2,0)
# dst = cv.ximgproc.jointBilateralFilter(src,src,33,2,0) #采用src作为joint

cv.imshow("img",src)
cv.imshow("joint",joint)
cv.imshow("dst",dst)
cv.waitKey(0)
cv.destroyAllWindows()

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值