使用opencv实现图像滤波

news2024/11/23 5:06:59

1 图像滤波介绍

滤波是信号和图像处理中的基本任务之一,其旨在有选择地提取图像的某些特征,可以用于在给定应用程序的上下文中传达重要信息,例如,去除图像中的噪声、提取所需的视觉特征、图像重采样等。

1.1 图像滤波理论

图像滤波即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可缺少的操作。消除图像中的噪声又叫做图像滤波或平滑,滤波的目的有两个,一是突出特征以方便处理,二是抑制噪声。

空间域滤波就是在图像平面上对像素进行操作。空间域滤波大体分为两类:平滑、锐化。

  • 平滑滤波:模糊处理,用于减小噪声,实际上是低通滤波,典型的滤波器是高斯滤波。

  • 锐化滤波:提取边缘突出边缘及细节、弥补平滑滤波造成的边缘模糊。实际上是高通滤波。

空间域处理可由下式表示:

g(x,y)=T[f(x,y)]

式中,f(x,y)是输入图像,g(x,y)是处理后的图像,T是在点(x,y)的邻域上定义的关于f的一种算子,算子可应用于单幅图像或图像集合。

1.2  频域分析

频域分析 (frequency domain analysis) :不同图像具有不同的灰度分布,可以用图像的灰度分布作为表征图像的一种方式,但同时,还存在另一种分析图像的观点。观察图像中的灰度变化,可以发现,某些图像包含强度几乎恒定的大面积区域(例如,蓝天),而某些图像上的灰度强度变化很快(例如,拥挤的街道)。因此,可以用图像中的变化频率作为表征图像的另一种方式,这种方式被称为频域,而通过观察图像的灰度分布来表征图像的方式则被称为空间域。

频域分析将图像从最低频率到最高频率分解为其频率内容,图像强度缓慢变化的区域仅包含低频,而高频是由强度的快速变化产生的。有多种用于计算图像的频率内容的变换,例如傅立叶变换或余弦变换。需要注意的是,由于图像是二维平面,因此频率由垂直频率(垂直方向的变化)和水平频率(水平方向的变化)组成。

在频域分析中,滤波器是一种放大图像某些频带同时减少其他频带的操作。因此,低通滤波器 (low-pass filters) 是消除图像高频成分的滤波器,而高通滤波器 (high-pass filters) 消除图像的低频成分。

1.3 邻域滤波算子

  • 空间滤波器由一个邻域(通常是一个较小的矩形)和对该邻域所包围图像像素执行的预定义操作组成。对预定义的点(x,y)为中心的领域内的像素进行计算。

  • 滤波产生一个新像素,用计算后的新像素值代替点(x,y)的值。

  • 循环步骤1和2,滤波器的中心遍历图像中的每个像素后,就生成了滤波后的图像。

  • 如果在图像像素上执行的是线性操作,则该滤波器称为线性空间滤波器,否则,称为非线性空间滤波器。

一般来说,使用大小为 m×n的滤波器对大小为 M×N的图像进行线性空间滤波,可由下式表示:

 2 opencv图像滤波

2.1 opencv滤波概述

滤波处理分为两大类:线性滤波和非线性滤波。OpenCV里有这些滤波的函数,使用起来非常方便。

2.1.1 线性滤波

  • 方框滤波BoxBlur:模糊图像

  • 均值滤波Blur:模糊图像

  • 高斯滤波GaussianBlur:信号的平滑处理,去除符合正太分布的噪声

平滑滤波:一般来说,图像具有局部连续的性质,即相邻的像素的值相近,而噪声使得噪点处产生像素跳跃,所以通过平滑噪点可以减少噪声,去除图像中的不相关细节。

方框滤波BoxBlur和均值滤波Blur都是对邻域内做平均值来滤波,属于平滑滤波。滤波的输出是包含在滤波器模板邻域内的像素的平均值,方框滤波做归一化之后就变为均值滤波,这两个滤波器都是低通滤波器。

高斯滤波:高斯滤波对于图像来说就是一个低通滤波,广泛用于消除高斯噪声,高速滤波就是一种加权滤波,只不过模板中的系数由高斯分布来确定的,高斯滤波器根据高斯函数的形状来选择滤波模板权值的线性平滑滤波器。高斯平滑滤波器对于抑制服从正态分布的噪声非常有效。

2.1.2 非线性滤波:

  • 中值滤波mediaBlur:去除椒盐噪声

  • 双边滤波BilateralFilter:保边去噪

中值滤波:中值滤波属于非线性滤波,其思想用滤波模板邻域内的像素的平均值来代替像素点的灰度值。中值滤波器是一种统计排序滤波器,图像上点(x,y),中值滤波以该点为中心,领域内所有像素的统计排序中值作为此点的响应,中值滤波是非线性滤波。相比与均值滤波和高斯滤波,中值滤波可以有效的降低随机噪声,直接忽略掉噪声点,把噪声引起的模糊降到最低。线性滤波器在滤波的同时会造成图像细节模糊,中值滤波可以避免这个问题,其典型的应用就是中值滤波消除斑点噪声、椒盐噪声。

双边滤波:高斯滤波属于加权平均滤波,距离中心点越近的点越有较大权重,这种方法符合图像的平滑变化的特征,但是在边缘区域,像素值出现突变,这种方法反而会滤掉边缘轮廓,损失掉有用的边缘信息。边缘保护滤波方法,双边滤波就是最常用的边缘保护滤波方法,就是为了处理这种情况而发明的。

双边滤波(Bilateral filter)是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折衷处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的。双边滤波将高斯滤波中通过各个点到中心点的空间临近度计算的各个权值进行优化,将其优化为空间临近度计算的权值和像素值相似度计算的权值的乘积,优化后的权值再与图像作卷积运算,从而达到保边去噪的效果。

2.2 opencv核心概念

  • 低通滤波:低通滤波可以去除图像的噪音或平滑图像。

  • 高通滤波:可以帮助查找图像的边缘。

  • 噪音:即对一幅图像的产生负面效果,过暗或过亮的部分,一幅图像中,低于或高于某个像素点的值,都可以认为是噪音。

  • 卷积核:即用来滤波的矩阵,卷积核一般为奇数,如3×3、5×5、7×7等;

  • 锚点:卷积核最中间的坐标点。

  • 卷积核越大,卷积的效果越好,但是计算量随之也会增大。

  • 边界扩充:当卷积核大于1,并且不进行边界扩充,输出尺寸相应缩小、当卷积核一标准方式进行边界扩充,则输出的空间尺寸与输入相等。

  • 图像卷积

3 opencv图像滤波实现

2.1 filter2D:图像卷积

函数原型:

    dst = cv2.filter2D(src, ddepth, kernel, anchor, delta, borderType)
   

    src:原图像

    ddepth:输出图像的尺寸,默认为-1

    kernel:卷积核(是一个矩阵)

    anchor:锚点,默认随卷积核变化

    delta:卷积后加一个值,默认为0

    borderType:有映射类型,加一个黑边,默认不设置

示例代码:

import cv2
import numpy as np

# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
    """
    :param scale: float 原图缩放的尺度
    :param imglist: list 待显示的图像序列
    :param order: list or tuple 显示顺序 行×列
    :param border: int 图像间隔距离
    :param border_color: tuple 间隔区域颜色
    :return: 返回拼接好的numpy数组
    """
    if order is None:
        order = [1, len(imglist)]
    allimgs = imglist.copy()
    ws , hs = [], []
    for i, img in enumerate(allimgs):
        if np.ndim(img) == 2:
            allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
        allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
        ws.append(allimgs[i].shape[1])
        hs.append(allimgs[i].shape[0])
    w = max(ws)
    h = max(hs)

    # 将待显示图片拼接起来
    sub = int(order[0] * order[1] - len(imglist))

    # 判断输入的显示格式与待显示图像数量的大小关系
    if sub > 0:
        for s in range(sub):
            allimgs.append(np.zeros_like(allimgs[0]))
    elif sub < 0:
        allimgs = allimgs[:sub]
    imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
    imgblank = imgblank.astype(np.uint8)
    for i in range(order[0]):
        for j in range(order[1]):
            imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
    return imgblank


original = cv2.imread('../data/girl03.jpeg')

# 创建一个5*5的卷积核
kernel = np.ones((5, 5), np.float32) / 25
filter2d = cv2.filter2D(original, -1, kernel)

img = show_multi_imgs(2, [original, filter2d], (1, 2))
cv2.namedWindow('origi&filter2D', 0)
cv2.imshow('origi&filter2D', img)
cv2.waitKey(0)

运行代码显示如下:

 经过图像处理后,看着变模糊了,图像更平滑了

2.2 低通滤波boxFilter:方盒滤波

函数原型:

dst = cv2.boxFilter(src, ddepth, ksize , anchor, normalize, borderType)

src:输入图像
ddepth:输出图像的尺寸,默认为-1
kernel:卷积核大小(x, y)
anchor:锚点,默认随卷积核变化
normalize:布尔类型默认为True;True:a为1/W*H(均值滤波),false:a=1

borderType:有映射类型,加一个黑边,默认不设置

示例代码:

import cv2
import numpy as np


# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
    """
    :param scale: float 原图缩放的尺度
    :param imglist: list 待显示的图像序列
    :param order: list or tuple 显示顺序 行×列
    :param border: int 图像间隔距离
    :param border_color: tuple 间隔区域颜色
    :return: 返回拼接好的numpy数组
    """
    if order is None:
        order = [1, len(imglist)]
    allimgs = imglist.copy()
    ws , hs = [], []
    for i, img in enumerate(allimgs):
        if np.ndim(img) == 2:
            allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
        allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
        ws.append(allimgs[i].shape[1])
        hs.append(allimgs[i].shape[0])
    w = max(ws)
    h = max(hs)

    # 将待显示图片拼接起来
    sub = int(order[0] * order[1] - len(imglist))

    # 判断输入的显示格式与待显示图像数量的大小关系
    if sub > 0:
        for s in range(sub):
            allimgs.append(np.zeros_like(allimgs[0]))
    elif sub < 0:
        allimgs = allimgs[:sub]
    imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
    imgblank = imgblank.astype(np.uint8)
    for i in range(order[0]):
        for j in range(order[1]):
            imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
    return imgblank

img = cv2.imread('../data/girl03.jpeg')

# 方盒滤波(当为True时)变成均值滤波,当为False时,就只加和不变化,超过255的结果设置为255
img2 = cv2.boxFilter(img, -1, (5, 5), normalize=True)
img3 = cv2.boxFilter(img, -1, (5, 5), normalize=False)
new_image = show_multi_imgs(4, [img, img2, img3], (1, 3))
cv2.namedWindow('img&img2&img3', 0)
cv2.imshow('img&img2&img3', new_image)
cv2.waitKey(0)

运行代码显示如下:

blur():均值滤波

函数原型

    方盒滤波的参数为True时,就是均值滤波,所以这个API用的不多。
    dst = cv2.blur(scr, ksize, anchor, borderType)
   

    scr:源图像
    kernel:卷积核大小(x,y)
    anchor:锚点
    borderType:有映射类型,加一个黑边,默认不设置

2.3  低通滤波GaussianBlur:高斯滤波(去高斯噪音)

适用于有高斯噪点的图片,函数原型:

dst = cv2.GaussianBlur(img, ksize, sigmaX, sigmaY, …)

img:输入的图像
ksize:卷积核大小
sigmaX:表示高斯核函数在X方向的的标准偏差。
sigmaY:表示高斯核函数在Y方向的的标准偏差。

重点关注前三个参数

示例代码:

import cv2
import numpy as np


# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
    """
    :param scale: float 原图缩放的尺度
    :param imglist: list 待显示的图像序列
    :param order: list or tuple 显示顺序 行×列
    :param border: int 图像间隔距离
    :param border_color: tuple 间隔区域颜色
    :return: 返回拼接好的numpy数组
    """
    if order is None:
        order = [1, len(imglist)]
    allimgs = imglist.copy()
    ws , hs = [], []
    for i, img in enumerate(allimgs):
        if np.ndim(img) == 2:
            allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
        allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
        ws.append(allimgs[i].shape[1])
        hs.append(allimgs[i].shape[0])
    w = max(ws)
    h = max(hs)

    # 将待显示图片拼接起来
    sub = int(order[0] * order[1] - len(imglist))

    # 判断输入的显示格式与待显示图像数量的大小关系
    if sub > 0:
        for s in range(sub):
            allimgs.append(np.zeros_like(allimgs[0]))
    elif sub < 0:
        allimgs = allimgs[:sub]
    imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
    imgblank = imgblank.astype(np.uint8)
    for i in range(order[0]):
        for j in range(order[1]):
            imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
    return imgblank

img = cv2.imread('../data/gauss2.jpeg')

# 高斯去噪
img2 = cv2.GaussianBlur(img, (3, 3), 0)

new_image = show_multi_imgs(4, [img, img2], (1, 2))
cv2.namedWindow('img&img2', 0)
cv2.imshow('img&img2', new_image)
cv2.waitKey(0)

运行代码显示如下:

 

2.4 低通滤波medianBlur:中值滤波(去胡椒噪音)

函数原型:

对胡椒噪音去噪明显,取中间的值作为卷积结果

dst = cv2.medianBlur(img, ksize)

img:输入图像
ksize:卷积核大小一个数字

示例代码:

import cv2
import numpy as np


# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
    """
    :param scale: float 原图缩放的尺度
    :param imglist: list 待显示的图像序列
    :param order: list or tuple 显示顺序 行×列
    :param border: int 图像间隔距离
    :param border_color: tuple 间隔区域颜色
    :return: 返回拼接好的numpy数组
    """
    if order is None:
        order = [1, len(imglist)]
    allimgs = imglist.copy()
    ws , hs = [], []
    for i, img in enumerate(allimgs):
        if np.ndim(img) == 2:
            allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
        allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
        ws.append(allimgs[i].shape[1])
        hs.append(allimgs[i].shape[0])
    w = max(ws)
    h = max(hs)

    # 将待显示图片拼接起来
    sub = int(order[0] * order[1] - len(imglist))

    # 判断输入的显示格式与待显示图像数量的大小关系
    if sub > 0:
        for s in range(sub):
            allimgs.append(np.zeros_like(allimgs[0]))
    elif sub < 0:
        allimgs = allimgs[:sub]
    imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
    imgblank = imgblank.astype(np.uint8)
    for i in range(order[0]):
        for j in range(order[1]):
            imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
    return imgblank

img = cv2.imread('../data/girl01.jpg')

# 高斯去噪
img2 = cv2.medianBlur(img, 5)

new_image = show_multi_imgs(4, [img, img2], (1, 2))
cv2.namedWindow('img&img2', 0)
cv2.imshow('img&img2', new_image)
cv2.waitKey(0)

运行代码显示如下:

2.5 低通滤波bilateralFilter:双边滤波

函数原型

双边滤波的主要应用场景是视频美颜

cv2.bilateralFilter(img, d, sigmaColor, sigmaSpace, …)

img:输入图像
d:直径,与卷积核中心点的距离,一般取5
sigmaColor:颜色空间滤波器的sigma值。这个参数的值越大,就表明该像素邻域内有更宽广的颜色会被混合到一起,产生较大的半相等颜色区域。
sigmaSpace:sigmaSpace坐标空间中滤波器的sigma值,坐标空间的标注方差。他的数值越大,意味着越远的像素会相互影响,从而使更大的区域足够相似的颜色获取相同的颜色。当d>0,d指定了邻域大小且与sigmaSpace无关。否则,d正比于sigmaSpace。
 

双边滤波的作用::图像去噪保边,对相关分析的结果有较大的影响,对于裂缝比较强,噪声比较少的图像来说,可以将去噪的程度放大,对以后的相关分析的结果就会有更少的噪声。对于噪声不是很集中的图像,并有较多细节的图像,增加保边的效果,让相关分析及后续进行进一步的结构处理,去噪。

示例代码:

import cv2
import numpy as np

# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
    """
    :param scale: float 原图缩放的尺度
    :param imglist: list 待显示的图像序列
    :param order: list or tuple 显示顺序 行×列
    :param border: int 图像间隔距离
    :param border_color: tuple 间隔区域颜色
    :return: 返回拼接好的numpy数组
    """
    if order is None:
        order = [1, len(imglist)]
    allimgs = imglist.copy()
    ws , hs = [], []
    for i, img in enumerate(allimgs):
        if np.ndim(img) == 2:
            allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
        allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
        ws.append(allimgs[i].shape[1])
        hs.append(allimgs[i].shape[0])
    w = max(ws)
    h = max(hs)

    # 将待显示图片拼接起来
    sub = int(order[0] * order[1] - len(imglist))

    # 判断输入的显示格式与待显示图像数量的大小关系
    if sub > 0:
        for s in range(sub):
            allimgs.append(np.zeros_like(allimgs[0]))
    elif sub < 0:
        allimgs = allimgs[:sub]
    imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
    imgblank = imgblank.astype(np.uint8)
    for i in range(order[0]):
        for j in range(order[1]):
            imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
    return imgblank

img = cv2.imread('../data/biteral.jpg')

# 双边滤波
img2 = cv2.bilateralFilter(img, 5, 20, 50)

new_image = show_multi_imgs(4, [img, img2], (1, 2))
cv2.namedWindow('img&img2', 0)
cv2.imshow('img&img2', new_image)
cv2.waitKey(0)

运行代码显示如下:

2.6 高通滤波Sobel

只能一次在x方向上或者y方向上求导,然后把结果相加。

dst1 = cv2.Sobel(src, ddepth, dx, dy, ksize = 3, scale = 1, delta = 0, borderType = BORDER_DEFAULT )

src:输入原图像
ddepth:位深,默认为-1
dx,dy:只能选择一个方向上要么0、1,要么1、0
ksize:卷积核大小,默认为3,当-1时为沙尔
scale:缩放大小,一般就用默认值
delta:偏移量,一般就用默认值
borderType:边界扩充类型,一般就用默认值
 

 示例代码:

import cv2
import numpy as np

# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
    """
    :param scale: float 原图缩放的尺度
    :param imglist: list 待显示的图像序列
    :param order: list or tuple 显示顺序 行×列
    :param border: int 图像间隔距离
    :param border_color: tuple 间隔区域颜色
    :return: 返回拼接好的numpy数组
    """
    if order is None:
        order = [1, len(imglist)]
    allimgs = imglist.copy()
    ws , hs = [], []
    for i, img in enumerate(allimgs):
        if np.ndim(img) == 2:
            allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
        allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
        ws.append(allimgs[i].shape[1])
        hs.append(allimgs[i].shape[0])
    w = max(ws)
    h = max(hs)

    # 将待显示图片拼接起来
    sub = int(order[0] * order[1] - len(imglist))

    # 判断输入的显示格式与待显示图像数量的大小关系
    if sub > 0:
        for s in range(sub):
            allimgs.append(np.zeros_like(allimgs[0]))
    elif sub < 0:
        allimgs = allimgs[:sub]
    imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
    imgblank = imgblank.astype(np.uint8)
    for i in range(order[0]):
        for j in range(order[1]):
            imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
    return imgblank

img = cv2.imread('../data/biteral.jpg')

# Sobel
dx = cv2.Sobel(img, -1, 1, 0, ksize=3)
dy = cv2.Sobel(img, -1, 0, 1, ksize=3)

# dst = dx+dy
dst = cv2.add(dx, dy)

new_image = show_multi_imgs(4, [img, dx, dy, dst], (1, 4))
cv2.namedWindow('img1-4', 0)
cv2.imshow('img1-4', new_image)
cv2.waitKey(0)

运行代码显示如下:

一幅图的边缘被很好的分割出来。

2.7 高通滤波Scharr

与Sobel类似,只不过使用的ksize值不同,Scharr不能改变卷积核的大小,只能是3*3的。同样只能求一个方向上的边缘。

cv2.Scharr(src, ddepth, dx, dy, scale = 1, delta = 0, borderType = BORDER_DEFAULT).
src:输入原图像
ddepth:位深,默认为-1
dx,dy:只能选择一个方向上要么0、1,要么1、0
scale:缩放大小,一般就用默认值
delta:偏移量,一般就用默认值
borderType:边界扩充类型,一般就用默认值

示例代码:

import cv2
import numpy as np


# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
    """
    :param scale: float 原图缩放的尺度
    :param imglist: list 待显示的图像序列
    :param order: list or tuple 显示顺序 行×列
    :param border: int 图像间隔距离
    :param border_color: tuple 间隔区域颜色
    :return: 返回拼接好的numpy数组
    """
    if order is None:
        order = [1, len(imglist)]
    allimgs = imglist.copy()
    ws , hs = [], []
    for i, img in enumerate(allimgs):
        if np.ndim(img) == 2:
            allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
        allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
        ws.append(allimgs[i].shape[1])
        hs.append(allimgs[i].shape[0])
    w = max(ws)
    h = max(hs)

    # 将待显示图片拼接起来
    sub = int(order[0] * order[1] - len(imglist))

    # 判断输入的显示格式与待显示图像数量的大小关系
    if sub > 0:
        for s in range(sub):
            allimgs.append(np.zeros_like(allimgs[0]))
    elif sub < 0:
        allimgs = allimgs[:sub]
    imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
    imgblank = imgblank.astype(np.uint8)
    for i in range(order[0]):
        for j in range(order[1]):
            imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
    return imgblank


img = cv2.imread('../data/biteral.jpg')

# Sobel,当ksize=-1时,就是Scharr
# dx = cv2.Sobel(img, -1, 1, 0, ksize=-1)
# dy = cv2.Sobel(img, -1, 0, 1, ksize=-1)

dx = cv2.Scharr(img, -1, 1, 0)
dy = cv2.Scharr(img, -1, 0, 1)

# dst = dx+dy
dst = cv2.add(dx,dy)

new_image = show_multi_imgs(4, [img, dx, dy, dst], (1, 4))
cv2.namedWindow('img1-4', 0)
cv2.imshow('img1-4', new_image)
cv2.waitKey(0)

运行代码显示如下:

2.8 高通滤波Laplacian(拉普拉斯)

Laplacian可以同时求两个方向上的边缘,但是对噪音比较敏感,一般需要先进行去噪再调用Laplacian。

dst = cv2.Laplacian(src, ddepth, ksize = 1 ,scale = 1, borderType = BORDER_DEFAULT)

src:输入原图像
ddepth:位深,默认为-1
ksize:卷积核大小,默认为1
scale:缩放大小,一般就用默认值
delta:偏移量,一般就用默认值
borderType:边界扩充类型,一般就用默认值

示例代码:

import cv2
import numpy as np

# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
    """
    :param scale: float 原图缩放的尺度
    :param imglist: list 待显示的图像序列
    :param order: list or tuple 显示顺序 行×列
    :param border: int 图像间隔距离
    :param border_color: tuple 间隔区域颜色
    :return: 返回拼接好的numpy数组
    """
    if order is None:
        order = [1, len(imglist)]
    allimgs = imglist.copy()
    ws , hs = [], []
    for i, img in enumerate(allimgs):
        if np.ndim(img) == 2:
            allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
        allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
        ws.append(allimgs[i].shape[1])
        hs.append(allimgs[i].shape[0])
    w = max(ws)
    h = max(hs)

    # 将待显示图片拼接起来
    sub = int(order[0] * order[1] - len(imglist))

    # 判断输入的显示格式与待显示图像数量的大小关系
    if sub > 0:
        for s in range(sub):
            allimgs.append(np.zeros_like(allimgs[0]))
    elif sub < 0:
        allimgs = allimgs[:sub]
    imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
    imgblank = imgblank.astype(np.uint8)
    for i in range(order[0]):
        for j in range(order[1]):
            imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
    return imgblank

img = cv2.imread('../data/biteral.jpg')

dst = cv2.Laplacian(img, -1, ksize=5)
new_image = show_multi_imgs(4, [img, dst], (1, 2))
cv2.namedWindow('img&dst', 0)
cv2.imshow('img&dst', new_image)
cv2.waitKey(0)

卷积核大小为5*5的结果

运行代码结果显示:

2.8 高通滤波Canny

使用5*5高斯滤波消除噪声,可以计算图像的四个方向上的边缘(0,45,90,135),取局部的最大值,多了一个阈值计算。高于阈值我们认为是边缘,低于阈值就不是边缘,显然A为边缘,如果,但是B和C介于最大值最小值之间,BC既不是边缘也是边缘,但是C与A在一条直线上,所以C也是边缘。

dst = cv2.Canny(img, minVal, maxVal)

img:原图像
minVal:最小阈值
maxVal:最大阈值
低于最小阈值就不是边缘,高于最大阈值是边缘。 

示例代码:

import cv2
import numpy as np

# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
    """
    :param scale: float 原图缩放的尺度
    :param imglist: list 待显示的图像序列
    :param order: list or tuple 显示顺序 行×列
    :param border: int 图像间隔距离
    :param border_color: tuple 间隔区域颜色
    :return: 返回拼接好的numpy数组
    """
    if order is None:
        order = [1, len(imglist)]
    allimgs = imglist.copy()
    ws , hs = [], []
    for i, img in enumerate(allimgs):
        if np.ndim(img) == 2:
            allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
        allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
        ws.append(allimgs[i].shape[1])
        hs.append(allimgs[i].shape[0])
    w = max(ws)
    h = max(hs)

    # 将待显示图片拼接起来
    sub = int(order[0] * order[1] - len(imglist))

    # 判断输入的显示格式与待显示图像数量的大小关系
    if sub > 0:
        for s in range(sub):
            allimgs.append(np.zeros_like(allimgs[0]))
    elif sub < 0:
        allimgs = allimgs[:sub]
    imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
    imgblank = imgblank.astype(np.uint8)
    for i in range(order[0]):
        for j in range(order[1]):
            imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
    return imgblank

img = cv2.imread('../data/biteral.jpg')

# canny
dst = cv2.Canny(img, 100, 200)
dst = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)

new_image = show_multi_imgs(4, [img, dst], (1, 2))
cv2.namedWindow('img&dst', 0)
cv2.imshow('img&dst', new_image)
cv2.waitKey(0)

运行代码显示如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1259026.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【论文解读】基于生成式面部先验的真实世界盲脸修复

论文地址&#xff1a;https://arxiv.org/pdf/2101.04061.pdf 代码地址&#xff1a;https://github.com/TencentARC/GFPGAN 图片解释&#xff1a; 与最先进的面部修复方法的比较&#xff1a;HiFaceGAN [67]、DFDNet [44]、Wan 等人。[61] 和 PULSE [52] 在真实世界的低质量图像…

final关键字-Java

final关键字 一、使用场景1、当不希望类被继承时&#xff0c;可以用final修饰。2、当不希望父类的某个方法被子类覆盖/重写(override)时&#xff0c;可以用final修饰。3、当不希望类的的某个属性的值被修改&#xff0c;可以用final修饰。4、当不希望某个局部变量被修改&#xf…

CSGO搬砖如何选品?选品软件和教程靠谱吗?

说到CSGO搬砖项目&#xff0c;目前平台最火的就是CSGO游戏搬砖。在CSGO搬砖项目中&#xff0c;选品是至关重要的环节&#xff0c;直接影响到利润。而选品软件可以帮助我们更快地了解市场变化、计算成本利润等关键信息&#xff0c;提高选品的效率和准确性。可靠的选品软件还能够…

技术人员都了解,动态代理IP和静态代理IP的区别及适用的场景

动态代理IP和静态代理IP是两种常见的代理IP技术&#xff0c;它们在网络通信中起到了重要的作用。虽然它们都可以用于隐蔽真实的IP地址&#xff0c;但在实际应用中有一些区别和适用的场景。本文将介绍这两种代理IP的区别以及它们适用于哪些场景。 一、静态代理IP 静态代理IP是指…

Java的threadd常用方法

常用API 给当前线程命名 主线程 package com.itheima.d2;public class ThreadTest1 {public static void main(String[] args) {Thread t1 new MyThread("子线程1");//t1.setName("子线程1");t1.start();System.out.println(t1.getName());//获得子线程…

eutil.dll文件缺失修复全指南,教你快速修复eutil.dll

eutil.dll缺失了要怎么办&#xff1f;eutil.dll是一种常见的动态链接库&#xff08;DynamicLinkLibrary&#xff0c;DLL&#xff09;文件&#xff0c;它在Windows操作系统中发挥着重要作用。DLL文件允许程序共享代码以执行诸如打印或连接网络之类的功能。这不仅节省了系统资源&…

浅谈API自动化测试

前言 本文主要针对API测试的概念及API测试在Choerodon中的实践展开。 API&#xff08;应用程序编程接口&#xff09;测试是一种软件测试&#xff0c;可以直接在API级别执行验证。它是集成测试的一部分&#xff0c;它确定API是否满足测试人员对功能&#xff0c;可靠性&#xf…

【第五节:微信小程序 小程序UI组件B】微信小程序入门,以思维导图的方式展开5

上图若是看不清&#xff0c;可私信给发大图哈 5、小程序UI组件B 表单form button 按钮 size String default 有效值 default, mini type String default 按钮的样式类型&#xff0c;有效值 primary, default, warn plain Bo…

[算法总结] - 蓄水池采样算法

问题描述 在长度为N的数组中&#xff0c;随机等概率选取K个元素&#xff0c;如何实现这个随机算法。 思路很简单&#xff0c;生成一个[0, N]的随机数index&#xff0c;然后返回index上的数值即可。 但是&#xff0c;如果输入是一个长度未知的数组比如stream&#xff0c;先遍历…

IDEA中Tomcat启动web项目

1.首先【Run】-->【Edit Configurations】&#xff0c;进入对应功能界面 2.点击左上角【】&#xff0c;选择Tomcat Server -->Local 3.Name输入自己中意的&#xff0c;下面两个port&#xff0c;保证没被占用就行 4.切到【Deployment】页签&#xff0c;点击【】&#xff…

elk日志分析系统:

elk日志分析系统: elk是一套完整的日志集中处理方案&#xff0c;由三个开源的软件简称组成&#xff1b; E:Easticsearch 简称ES是一个开源的&#xff0c;分布式的存储检索引擎&#xff0c;&#xff08;索引型的非关系数据库&#xff09;存储日志 由java代码开发的&#xff0…

【Java Spring】SpringBoot 五大类注解

文章目录 Spring Boot 注解简介1、五大类注解的作用2、五大类注解的关系3、通过注解获取对象4、获取Bean对象名规则解析 Spring Boot 注解简介 Spring Boot的核心就是注解。Spring Boot通过各种组合注解&#xff0c;极大地简化了Spring项目的搭建和开发。五大类注解是Spring B…

用泰勒展开线性化

在点附近做泰勒展开&#xff1a; 当和很接近的时候&#xff0c;很小&#xff0c;更小&#xff0c;所以可以忽略及后面的高阶项&#xff0c;得到 因为、都是常数&#xff0c;所以等式右边是 x的线性方程&#xff0c;在点附近进行了线性化。 举个例子&#xff1a; 假设 那么做一…

【Linux】安卓端JuiceSSH结合内网穿透实现远程连接服务器

目录 前言1. Linux安装cpolar2. 创建公网SSH连接地址3. JuiceSSH公网远程连接4. 固定连接SSH公网地址5. SSH固定地址连接测试 前言 处于内网的虚拟机如何被外网访问呢&#xff1f;如何手机就能访问虚拟机呢&#xff1f; 本文介绍 cpolarJuiceSSH 实现手机端远程连接Linux虚拟…

linux 命令 sudo、su 命令

sudo命令详解 1、初识sudo sudo是linux下常用的允许普通用户使用超级用户权限的工具&#xff0c;sudo 用来执行需要提升权限&#xff08;通常是作为 root 用户&#xff09;的命令&#xff0c;允许系统管理员让普通用户执行一些或者全部的root命令&#xff0c;如halt&#xff…

C++中类的静态成员、存储、this、友元和运算符重载

静态成员 在类定义中&#xff0c;它的成员&#xff08;包括成员变量和成员函数&#xff09;&#xff0c;这些成员可以用关键字static 声明为静态的&#xff0c;称为静态成员。 不管这个类创建了多少个对象&#xff0c;静态成员只有一个拷贝&#xff0c;这个拷贝被所有属于这个…

搜索百度可以直接生成代码拉

先看效果图&#xff1a; 使用示例&#xff1a; 比如我要搜索“JS取一个数在两个数更近”的方法&#xff0c;直接搜“JS取一个数在两个数更近”&#xff0c;点击百度一下&#xff0c;就会出现想要的代码&#xff0c;如上图。

网站频频告警故障排查实录

故障描述 位于某Proxmox VE超融合集群上的一个网站频频报警&#xff0c;表现的形式是一会儿服务不可用&#xff0c;一会儿又恢复&#xff08;如下图所示&#xff09;&#xff0c;但同一集群上的其他Web站点未发现异常。 可能的原因 1&#xff09;出口带宽占满。 2&#xff09;…

【技巧】Excel表格如何退出“只读方式”?

如果Excel表格被设置了“只读模式”&#xff0c;那每次打开Excel都会出现对话框提示是否以“只读方式”打开&#xff0c;并且以“只读方式”打开的Excel&#xff0c;如果进行更改是无法保存原文件的。那要如何退出“只读方式”呢&#xff1f; 首先&#xff0c;我们要看下Excel表…

【数据结构】顺序表---C语言版

【数据结构】顺序表 前言&#xff1a;一、线性表二、顺序表1.顺序表的概念及结构&#xff1a;2.顺序表的分类&#xff1a;3.顺序表缺陷&#xff1a; 三、顺序表的代码实现&#xff1a;1.头文件&#xff1a;2.函数文件&#xff1a;3.测试文件&#xff1a; 四、顺序表的相关OJ题&…