图形处理-python

函数篇:


1-imread()

列子:

image1 = cv.resize(image1, (400, 400), interpolation=cv.INTER_AREA)是为了规范图片的大小

2-subplots()

1.subplots包装器参数说明:
subplots(nrows=1, ncols=1, sharex=False,sharey=False, squeeze=True, subplot kw=None, gridspec kw=None, * fig kw
。nrows,ncols参数:整数类型,可选项,默认均为1.分别设置坐标轴的排列网格;
sharex,sharey参数:布尔类型或是{“"none"“al!"“row",co!"字典类型,默认为False,用来指定各子图之间是否共用xy轴属性
·True or“al”:将在所有图像之间共享xy轴属性
False or“none”:每个图像x,y轴属性都是独立的
。"row":进行横向属性共享
。"col":进行列向属性共享
squeeze:布尔类型,默认为True;
True.
。如果只创建了一个子图像,返回的坐标系对象是一个标量
如果创建的是1XN单行或者MX1单列的子图像,返回坐标系对象是一个一维numpy数组。如果创建的是 NXM(N>1.M>1) 多行多列的子图像,返回坐标系对象是一个2维数组False:
。没有进行降维压缩,无论图像如何排列返回的坐标系对象总是2维数组的。
num:整数类型或者字符串类型,默认:None。设置图像的序号或者标签
。subplot kw:字典,可选项。字典里的关键字被传递给add subplot函数去调用图像类型。gridspec kw:字典,可选项。将关键字传递给Gridspec构造函数,该构造函数用于放置子图的网络,。
**fig kw:所有关键字参数都传递给pyplot.figure函数去调用
返回结果:
。fig:画布对象
ax:坐标系子图对象或者轴对象的数组

例子:下列
详细情况请参考:【Matplotlib】plt.figure()、plt.subplot() 、plt.subplots() 、plt.xticks() 、plt.xlim()和 plt.grid() 六个函数的使用-CSDN博客

3-axis()

例子:

import cv2 as cv
from matplotlib import pyplot as plt
import pylab

img1 = cv.imread('.//images//grassland.png', 1)
img1 = cv.resize(img1, (400, 400), interpolation=cv.INTER_AREA)
img2 = cv.imread('.//images//moon.png', 1)
img2 = cv.resize(img2, (400, 400), interpolation=cv.INTER_AREA)
img3 = cv.add(img1, img2)
img4 = img1 + img2
img5 = cv.subtract(img1, img2)
img6 = img1 - img2
img7 = cv.multiply(img1, img2)
img8 = img1 * img2
img9 = cv.divide(img1, img2)
# bgr转换为rgb,并显示
img3 = img3[:, :, ::-1]
plt.subplot(251)
plt.imshow(img3), plt.axis('off'), plt.title('add')
img4 = img4[:, :, ::-1]
plt.subplot(256)
plt.imshow(img4), plt.axis('off'), plt.title('+')
img5 = img5[:, :, ::-1]
plt.subplot(252)
plt.imshow(img5), plt.axis('off'), plt.title('sub')
img6 = img6[:, :, ::-1]
plt.subplot(257)
plt.imshow(img6), plt.axis('off'), plt.title('-')
img7 = img7[:, :, ::-1]
plt.subplot(253)
plt.imshow(img7), plt.axis('off'), plt.title('mul')
img8 = img8[:, :, ::-1]
plt.subplot(258)
plt.imshow(img8), plt.axis('off'), plt.title('*')
img9 = img9[:, :, ::-1]
plt.subplot(254)
plt.imshow(img9), plt.axis('off'), plt.title('div')
img1 = img1[:, :, ::-1]
plt.subplot(255)
plt.imshow(img1), plt.axis('off'), plt.title('img1')
img2 = img2[:, :, ::-1]
plt.subplot(2, 5, 10)
plt.imshow(img2), plt.axis('off'), plt.title('img2')
pylab.show()

4-np.zeros()

5-cv.warpaffine()

cv.warpAffine()函数主要是利用变换矩阵M对图像进行如旋转仿射平移等变换,只需要我们提供一个2*3的变换矩阵M,就可以对图像进行变换。它一般是和cv2.getRotationMatrix2D和cv.GetAffineTransform两个函数在一起使用,这两个函数是用来获取变换矩阵M,这样就不需要我们自己设置M

6-cv.resize()

7-img.shape[ ]

在Python中,img.shape通常用于获取图像(或数组)的形状信息。对于图像而言,img.shape返回一个包含三个元素的元组,分别表示图像的高度、宽度和通道数。因此,img.shape[0]表示图像的高度,img.shape[1]表示图像的宽度,img.shape[2]表示图像的通道数(例如RGB图像通道数为3)。

当使用img.shape[0:2]时,表示获取图像的高度和宽度信息,即返回一个包含高度和宽度两个元素的元组。这在图像处理中经常用于获取图像的尺寸信息,以便进行后续的处理和操作。

8-np.shape

9-cv.getrotationmatrix2d()

10-pylab.show()

11-plt.imshow()

详细情况请参考:matplotlib.pyplot.imshow — Matplotlib 3.8.3 documentation

12-np.uint8()

np.uint8()函数是NumPy库中的一个函数,用于将输入数据转换为8位无符号整数类型。它的作用是将输入数据的值限制在0到255之间,并将其存储为8位无符号整数。

该函数的参数可以是一个数值、一个列表、一个数组或者其他可迭代对象。它会将输入数据的每个元素都转换为8位无符号整数类型,并返回一个新的数组或者标量。

下面是np.uint8()函数的一些示例用法:

  1. 将一个数值转换为8位无符号整数类型:

    x = np.uint8(128) print(x)
    

    输出:128

  2. 将一个列表转换为8位无符号整数类型的数组:

    y = np.uint8([10, 20, 30, 40, 50]) print(y)
    

    输出:[10 20 30 40 50]

  3. 将一个数组转换为8位无符号整数类型的数组:

    z = np.array([100, 200, 300, 400, 500]) w = np.uint8(z) print(w)
    

    输出:[100 200 44 144 244]

请注意,由于8位无符号整数类型的取值范围是0到255,如果输入数据超出了这个范围,会发生截断。例如,对于输入值大于255的情况,会将其取模为255。

13-plt.figure()

详细情况请参考:【Matplotlib】plt.figure()、plt.subplot() 、plt.subplots() 、plt.xticks() 、plt.xlim()和 plt.grid() 六个函数的使用-CSDN博客

14-plt.hist()

15- np.fft.fft2 cv.dft ():::傅里叶变换z

        我们知道,灰度图像是由二维的离散的点构成的。二维离散傅里叶变换(Two-Dimensional Discrete Fourier Transform)常用于图像处理中,对图像进行傅里叶变换后得到其频谱图。频谱图中频率高低表征图像中灰度变化的剧烈程度。图像中边缘和噪声往往是高频信号,而图像背景往往是低频信号。我们在频率域内可以很方便地对图像的高频或低频信息进行操作,完成图像去噪,图像增强,图像边缘提取等操作

import cv2
import numpy as np
from matplotlib import pyplot as plt

plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置显示中文字体
plt.rcParams['axes.unicode_minus'] = False  # 设置正常显示符号


#读取图像
img = cv2.imread('.//images//sl.png', 0)
#傅里叶变换
fft_img = np.fft.fft2(img)
fft_shift = np.fft.fftshift(fft_img)
fft_res = np.log(np.abs(fft_shift))
#傅里叶逆变换
ifft_shift = np.fft.ifftshift(fft_shift)
ifft_img = np.fft.ifft2(ifft_shift)
ifft_img = np.abs(ifft_img)
#显示图像
plt.subplot(131), plt.imshow(img, 'gray')
plt.title('索隆'),plt.axis('off')
plt.subplot(132), plt.imshow(fft_res, 'gray')
plt.title('傅里叶@索隆'),plt.axis('off')
plt.subplot(133), plt.imshow(ifft_img, 'gray')
plt.title('反傅里叶@索隆'),plt.axis('off')
plt.show()

np.log--:区对数运算

第二个图片就代表了第一附图的傅里叶变化后,可以看出索隆在傅里叶变换后就成了一个点

详细请参考:图像傅里叶变换原理 python实现 - 我坚信阳光灿烂 - 博客园 (cnblogs.com)

16-np.array()函数

17-getOptimalDFTSize()函数

18-cv2.magnitude 详解

19-cv2.normalize()

详细参考:OpenCV的函数normalize()的两个作用:调整矩阵的值范围(归一化处理)、规范化矩阵的范数为某个值_normalize函数-CSDN博客

归一化函数normalize详解_normalized-CSDN博客

20-  cv2.createTrackbar

21-resizewindow()

22-np.ones()

23-cv.idft()函数

在OpenCV中,idft()函数用于执行离散傅立叶逆变换(Inverse Discrete Fourier Transform)。该函数的详细参数和作用如下:

 

void cv::dft(cv::InputArray src, cv::OutputArray dst, int flags = 0, int nonzeroRows = 0);

  • src: 输入数组,可以是一个或多个通道的实数或复数数组。
  • dst: 输出数组,用于存储逆傅立叶变换的结果,通常与输入数组具有相同的大小和类型。
  • flags: 控制傅立叶变换的标志。默认为0,表示没有特殊标志。
  • nonzeroRows: 当输入数组是一个矩阵时,指定要处理的非零行数。默认为0,表示所有行都被处理。

idft()函数的作用是将频域的复数值数组转换回空间域的实数值数组。在图像处理中,通常使用傅立叶变换来分析图像的频域特征,而逆傅立叶变换可以将这些特征重新转换回原始图像的空间域表示。

使用idft()函数可以将频域表示的图像进行逆变换,以便在空间域中查看原始图像的内容。这对于图像处理中的滤波、增强和特征提取等任务非常有用。

24-round()

需要注意的是round不一定全是四舍五入,有的时候会受到精度的影响

25-np.abs():

26-np.diag()

27-cv2.getStructuringElement()

kernel = cv2.getStructuringElement(shape, ksize, anchor ) 其中输入参数如下:

shape:内核的形状,有三种形状可以选择:

MORPH_RECT:产生矩形的结构元素

MORPH_ELLIPSEM:产生椭圆形的结构元素;

MORPH_CROSS:产生十字交叉形的结构元素。

ksize:内核的尺寸;

anchor:分别是内核锚点的位置。对于锚点的位置,有默认值Point(-1,-1),表示锚点位于中心点。

在调用腐蚀(erode)以及膨胀(dilate)函数之前,需要先定义一个结构元素(卷积核)。

28-cv2.erode():

dst = cv2.erode(src, element, anchor, iterations, borderType, borderValue)

与卷积操作类似,对于边界处像素的领域有可能会超出图像边界,所以需要扩充图像边界,边界扩充类型中镜像扩充操作效果最好。

 29-skimage.morphology.erosion()

    dst = skimage.morphology.erosion(image, selem=None)

  1. image:这是输入的图像或图像数组,它是进行腐蚀操作的对象。

  2. selem:这是结构元素,用于定义腐蚀操作的形状和大小。结构元素可以是一个二值数组,也可以是一个由 0 和 1 组成的数组,其中 1 表示结构元素的一部分,0 表示不是结构元素的部分。结构元素的形状和大小会影响腐蚀操作的效果。

  3. out:这是可选参数,用于指定输出结果的数组。如果提供了此参数,腐蚀操作的结果会保存在这个数组中,而不是创建一个新的数组来存储结果。

30-cv2.dilate()

dst = cv2.dilate(src, element,anchor, iterations, borderType, borderValue)

  1. src:这是输入的图像或图像数组,是进行膨胀操作的对象。

  2. element:这是一个结构元素,用于定义膨胀操作的形状和大小。结构元素可以是一个矩形、椭圆、十字形等形状,通过它可以控制膨胀操作的效果。

  3. anchor:这是结构元素的锚点位置,用于指定结构元素中心的位置。通常情况下,可以将其设置为(-1, -1),表示结构元素的中心为锚点。

  4. iterations:这是膨胀操作的迭代次数,可以控制膨胀的程度。默认值为 1。

  5. borderType:这是边界扩充的类型,用于处理边界像素的情况。可以选择不同的边界扩充方式,如常量边界、镜像边界等。

  6. borderValue:这是在边界扩充时使用的常量值,用于指定边界像素的取值

31-skimage.morphology.dilation()

dst = skimage.morphology.dilation(image, selem=None)

  1. image:这是输入的图像或图像数组,它是进行膨胀操作的对象。

  2. selem:这是结构元素,用于定义膨胀操作的形状和大小。结构元素可以是一个二值数组,也可以是一个由 0 和 1 组成的数组,其中 1 表示结构元素的一部分,0 表示不是结构元素的部分。结构元素的形状和大小会影响膨胀操作的效果。

  3. out:这是可选参数,用于指定输出结果的数组。如果提供了此参数,膨胀操作的结果会保存在这个数组中,而不是创建一个新的数组来存储结果。

32-cv2.morphologyEx()

cv2.morphologyEx(src, op, kernel,anchor, iterations, borderType, borderValue )

32-skimage.morphology.openning()

dst = skimage.morphology.openning(image, selem=None)

  1. image:这是输入的图像或图像数组,是进行开运算操作的对象。

  2. selem:这是结构元素,用于定义开运算操作的形状和大小。结构元素可以是一个二值数组,也可以是一个由 0 和 1 组成的数组,其中 1 表示结构元素的一部分,0 表示不是结构元素的部分。结构元素的形状和大小会影响开运算操作的效果。

  3. out:这是可选参数,用于指定输出结果的数组。如果提供了此参数,开运算操作的结果会保存在这个数组中,而不是创建一个新的数组来存储结果。

33-skimage.morphology.closing()

dst = skimage.morphology.closing(image, selem=None)

在 skimage.morphology.closing() 函数中,有几个主要参数可以控制闭运算(closing)操作的行为:

1. `image`:这是输入的图像或图像数组,是进行闭运算操作的对象。

2. `selem`:这是结构元素,用于定义闭运算操作的形状和大小。结构元素可以是一个二值数组,也可以是一个由 0 和 1 组成的数组,其中 1 表示结构元素的一部分,0 表示不是结构元素的部分。结构元素的形状和大小会影响闭运算操作的效果。

3. `out`:这是可选参数,用于指定输出结果的数组。如果提供了此参数,闭运算操作的结果会保存在这个数组中,而不是创建一个新的数组来存储结果。

通过调整这些参数,可以控制闭运算操作的效果,闭运算是先进行膨胀操作,再进行腐蚀操作,常用于填充小孔、平滑边界等图像处理任务。
 

问题及原理:


1-为什么要构造掩模函数:

2-平移变换矩阵:

3-opencv什么是比例因子:

在OpenCV中,比例因子(Scale Factor)通常指的是在图像金字塔(Image Pyramid)中用于缩放图像的因子。图像金字塔是一种用于图像处理中的多尺度表达方法,通过在不同尺度下对同一图像进行缩放来实现对图像的多尺度分析。

比例因子在图像金字塔中决定了每一层图像相对于前一层的缩放比例。通常,比例因子小于1时表示图像尺寸缩小,比例因子大于1时表示图像尺寸放大。通过不同比例因子的应用,可以实现对图像在不同尺度下的分析和处理,用于目标检测、特征提取等任务。

4-opencv为什么要构造移动矩阵:

在OpenCV中构造移动矩阵是为了实现图像的平移操作。移动矩阵(Translation Matrix)是一种仿射变换矩阵,用于描述图像在平面上的平移操作。通过构造移动矩阵,可以将图像沿着水平和垂直方向移动指定的距离,实现图像在平面上的位置调整。

平移操作在图像处理中是一种常见的操作,可以用于图像对齐、图像拼接、目标跟踪等应用。通过构造移动矩阵并将其应用到图像上,可以方便地实现图像的平移操作,从而满足不同应用场景下对图像位置的调整需求

5-旋转变化:

 详细情况请参考:cv2.getRotationMatrix2D的旋转矩阵的正确形式-CSDN博客

6-剪切变化:

详细情况请参考:8.openCV 裁剪图像_opencv裁剪图像-CSDN博客

7-直方图的规定化与均衡化

详细情况请参考:直方图均衡化(Histogram equalization)与直方图规定化_如何进行直方图的均衡化和规定化-CSDN博客

8-灰度图与彩色图

9-为什么要将rgb转为bgr:

实例

import pylab

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
img1 = cv.imread('.//images//lena.png')
#bgR=》rgb
img2 = img1[:, :, ::-1]
plt.subplot(2,2,1),plt.imshow(img1, cmap='Greys_r'),plt.axis('off'),plt.title("bgr")
plt.subplot(2,2,2),plt.imshow(img2, cmap='Greys_r'),plt.axis('off'),plt.title("rgb")
pylab.show()

10-高斯,椒盐,随机噪声

实例

import pylab
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置显示中文字体
plt.rcParams['axes.unicode_minus'] = False  # 设置正常显示符号
img1 = cv.imread('.//images//lena.png')
img1 = img1[:, :, ::-1]

# 高斯噪声
def gaosi(image, mean=0, var=0.01):
    # 高斯噪声,mean均值, var方差
    image = np.array(image / 255, dtype=float)
    noise = np.random.normal(mean, var ** 0.5, image.shape)
    img_noise = image + noise
    if img_noise.min() < 0:
        clip = -1
    else:
        clip = 0
    img_noise = np.clip(img_noise, clip, 1.0)
    img_noise = np.uint8(img_noise * 255)
    return img_noise


def sp(img, prob):
    img_noise = np.zeros(img.shape, np.uint8)
    three = 1 - prob
    for i in range(img.shape[0]):
        for j in range(img.shape[1]):
            rNUM = np.random.random()
            if rNUM < prob:
                img_noise[i][j] = 0
            elif rNUM > three:
                img_noise[i][j] = 255
            else:
                img_noise[i][j] = img[i][j]
    return img_noise


def random(image, noise_num):
    img_noise = image
    row, col, chn = img_noise.shape
    for i in range(noise_num):
        x = np.random.randint(0, row)
        y = np.random.randint(0, col)
        img_noise[x, y, :] = 255
    return img_noise


def possion(image, vals):
    vals = len(np.unique(image))
    vals = 2 ** np.ceil(np.log2(vals))
    noisy = np.random.poisson(image * vals) / float(vals)
    return noisy


img2 = gaosi(img1)
img3 = sp(img1, 0.06)
img4 = random(img1, 1000)
img5 = possion(img1, 0.03)
plt.subplot(2, 3, 1), plt.imshow(img1), plt.axis('off'), plt.title("Original")
plt.subplot(2, 3, 2), plt.imshow(img2), plt.axis('off'), plt.title("高斯")
plt.subplot(2, 3, 3), plt.imshow(img3), plt.axis('off'), plt.title("椒盐")
plt.subplot(2, 3, 4), plt.imshow(img4), plt.axis('off'), plt.title("随机")
plt.subplot(2, 3, 5), plt.imshow(img4), plt.axis('off'), plt.title("松泊")
pylab.show()

11-滤波算法

12-卷积核

13-理解什么是卷积:

14-图像变换

反色变换

对数变换

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

plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置显示中文字体
plt.rcParams['axes.unicode_minus'] = False  # 设置正常显示符号


def log_plot(c):  # 绘制曲线
    x = np.arange(0, 255, 0.01)
    y = c * np.log(1 + x)
    plt.plot(x, y, 'r', linewidth=1)
    plt.title('logarithmic')
    plt.xlim(0, 255), plt.ylim(0, 255)
    plt.show()


def log(c, img_Gray):  # 对数变换
    output = c * np.log(1.0 + img_Gray)
    output = np.uint8(output + 0.5)
    return output


img = cv2.imread('.//images//lena.png')
# img = img[:, :, ::-1]
img_Gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
c = 45
log_plot(c)  # 绘制对数变换曲线
result = log(c, img_Gray)  # 图像灰度对数变换

plt.subplot(2, 2, 1), plt.imshow(result ,cmap='Greys_r'), plt.axis('off',), plt.title("Logarithmic transformation")
plt.subplot(2, 2, 2), plt.imshow(img_Gray, cmap='Greys_r'), plt.axis('off',), plt.title("Origin")
plt.show()

伽马变换

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

plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置显示中文字体
plt.rcParams['axes.unicode_minus'] = False  # 设置正常显示符号


# 绘制曲线
def gamma_plot(c, gamma):
    x = np.arange(0, 255, 0.01)
    # y = c * x ** gamma
    y = c * np.power(x, gamma)  # xy
    plt.plot(x, y, 'b', linewidth=1)
    plt.xlim([0, 255]), plt.ylim([0, 255])


def gamma_trans(img, c, gamma1):  # 伽玛变换
    output_img = c * np.power(img / float(np.max(img)), gamma1) * 255.0
    output_img = np.uint8(output_img)
    return output_img


img = cv2.imread('.//images//lena.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
plt.figure(1), gamma_plot(1, 0.5), plt.title('gamma=0.5')
# 伽玛变换曲线
plt.figure(1), gamma_plot(1, 2), plt.title('gamma=2.0')
plt.show()
result = gamma_trans(img_gray, 1, 0.5)
# 图像灰度伽玛变换
result1 = gamma_trans(img_gray, 1, 2.0)
plt.subplot(2, 2, 1), plt.imshow(img_gray, cmap='Greys_r'), plt.axis('off', ), plt.title("Origin")
plt.subplot(2, 2, 3), plt.imshow(result, cmap='Greys_r'), plt.axis('off', ), plt.title("Gamma<1")
plt.subplot(2, 2, 4), plt.imshow(result1, cmap='Greys_r'), plt.axis('off', ), plt.title("Gamma>1")
plt.show()

15-为什么要进行傅里叶变化:

物理意义

傅里叶变换是数字信号处理领域一种很重要的算法。要知道傅里叶变换算法的意义,首先要了解傅里叶原理的意义。傅里叶原理表明:任何连续测量的时序或信号,都可以表示为不同频率的正弦波信号的无限叠加。而根据该原理创立的傅立叶变换算法利用直接测量到的原始信号,以累加方式来计算该信号中不同正弦波信号的频 率、振幅和相位。
和傅立叶变换算法对应的是反傅立叶变换算法。该反变换从本质上说也是一种累加处理,这样就可以将单独改变的正弦波信号转换成一个信号。
        傅立叶变换将原来难以处理的时域信号转换成了易于分析的频域信号(信号的频谱),可以利用一些工具对这些频域信号进行处理、加工。最后还可以利用傅立叶反变换将这些频域信号转换成时域信号

图像意义

图像的频率是表征图像中灰度变化剧烈程度的指标,是灰度在平面空间上的梯度。傅立叶变换在实际中有非常明显的物理意义,设f是一个能量有限的模拟信号,则其傅立叶变换就表示f的 谱。从纯粹的数学意义上看,傅立叶变换是将一个函数转换为一系列周期函数来处理的。从物理效果看,傅立叶变换是将图像从空间域转换到频率域,其逆变换是将 图像从频率域转换到空间域。换句话说,傅立叶变换的物理意义是将图像的灰度分布函数变换为图像的频率分布函数,傅立叶逆变换是将图像的频率分布函数变换为 灰度分布函数
傅立叶变换以前,图像(未压缩的位图)是由对在连续空间(现实空间)上的采样得到一系列点的集合,我们习惯用一个二维矩阵表示空间上各点,则图像可由z=f(x,y)来 表示由于空间是三维的,图像是二维的,因此空间中物体在另一个维度上的关系就由梯度来表示,这样我们可以通过观察图像得知物体在三维空间中的对应关系。 为什么要提梯度?因为实际上对图像进行二维傅立叶变换得到频谱图,就是图像梯度的分布图,当然频谱图上的各点与图像上各点并不存在一一对应的关系,即使在 不移频的情况下也是没有。傅立叶频谱图上我们看到的明暗不一的亮点,实际上图像上某一点与邻域点差异的强弱,即梯度的大小,也即该点的频率的大小(可以这 么理解,图像中的低频部分指低梯度的点,高频部分相反)。一般来讲,梯度大则该点的亮度强,否则该点亮度弱。这样通过观察傅立叶变换后的频谱图,也叫功率 图,我们首先就可以看出,图像的能量分布,如果频谱图中暗的点数更多,那么实际图像是比较柔和的(因为各点与邻域差异都不大,梯度相对较小),反之,如果 频谱图中亮的点数多,那么实际图像一定是尖锐的,边界分明且边界两边像素差异较大的。对频谱移频到原点以后,可以看出图像的频率分布是以原点为圆心,对称 分布的。将频谱移频到圆心除了可以清晰地看出图像频率分布以外,还有一个好处,它可以分离出有周期性规律的干扰信号,比如正弦干扰,一副带有正弦干扰,移 频到原点的频谱图上可以看出除了中心以外还存在以某一点为中心,对称分布的亮点集合,这个集合就是干扰噪音产生的,这时可以很直观的通过在该位置放置带阻 滤波器消除干扰

详细信息请参考:通俗易懂的理解傅里叶变换(一)[收藏] - 知乎 (zhihu.com)

傅里叶变换是什么?来看最直观的傅里叶变换图解_哔哩哔哩_bilibili

16-滤波

滤波

        低通滤波器:只保留低频,会使图像模糊

        高通滤波器:只保留高频,使图像细节增强

图像滤波

高通滤波

import cv2
import numpy as np
from matplotlib import pyplot as plt
# 第一步读入图片
img = cv2.imread('.//images//lena.jpg', 0)
# 第二步:进行数据类型转换
img_float = np.float32(img)
# 第三步:使用cv2.dft进行傅里叶变换
dft = cv2.dft(img_float, flags=cv2.DFT_COMPLEX_OUTPUT)
# 第四步:使用np.fft.fftshift将低频转移到图像中心
dft_center = np.fft.fftshift(dft)
# 第五步:定义掩模:生成的掩模中间为0周围为1
# 求得图像的中心点位置
crow, ccol = int(img.shape[0] / 2), int(img.shape[1] / 2)
  #设置掩模区域为10*10的正方形
mask = np.ones((img.shape[0], img.shape[1], 2), np.uint8)
mask[crow-5:crow+5, ccol-5:ccol+5] = 0
 #设置掩模区域为60*60的正方形
mask1 = np.ones((img.shape[0], img.shape[1], 2), np.uint8)
mask1[crow-30:crow+30, ccol-30:ccol+30] = 0
# 第六步:将掩模与傅里叶变化后图像相乘,保留中间部分
mask_img = dft_center * mask
mask1_img = dft_center * mask1
# 第七步:使用np.fft.ifftshift将低频移动到原来的位置
img_idf = np.fft.ifftshift(mask_img)
img1_idf = np.fft.ifftshift(mask1_img)
# 第八步:使用cv2.idft进行傅里叶的反变换
img_idf = cv2.idft(img_idf)
img1_idf = cv2.idft(img1_idf)
# 第九步:使用cv2.magnitude转换为空间域
img_idf = cv2.magnitude(img_idf[:, :, 0], img_idf[:, :, 1])
img1_idf = cv2.magnitude(img1_idf[:, :, 0], img1_idf[:, :, 1])
# 第十步:显示结果图像
plt.subplot(131),plt.title('Origin Image')
plt.imshow(img, cmap='gray'),plt.axis('off')
plt.subplot(132),plt.title('Highpass mask=5')
plt.imshow(img_idf, cmap='gray'),plt.axis('off')
plt.subplot(133),plt.title('Highpass mask=30')
plt.imshow(img1_idf, cmap='gray'),plt.axis('off')
plt.show()

低通滤波

import cv2
import numpy as np
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置显示中文字体
plt.rcParams['axes.unicode_minus'] = False  # 设置正常显示符号
#
# 第一步读入图像
img = cv2.imread('.//images//xiyng.png', 0)
# 第二步:进行数据类型转换
img_float = np.float32(img)
# 第三步:使用cv2.dft进行傅里叶变换
dft = cv2.dft(img_float, flags=cv2.DFT_COMPLEX_OUTPUT)
# 第四步:使用np.fft.fftshift将低频转移到图像中心
dft_center = np.fft.fftshift(dft)
# 第五步:定义掩模:生成的掩模中间为1,周围为0
   # 求得图像的中心点位置
crow, ccol = int(img.shape[0] / 2), int(img.shape[1] / 2)
  #设置掩模区域为40*40的正方形
mask = np.zeros((img.shape[0], img.shape[1], 2), np.uint8)
mask[crow-20:crow+20, ccol-20:ccol+20] = 1
  #设置掩模区域为100*100的正方形
mask1 = np.zeros((img.shape[0], img.shape[1], 2), np.uint8)
mask1[crow-50:crow+50, ccol-50:ccol+50] = 1
# 第六步:将掩模与傅里叶变化后图像相乘,保留中间部分
mask_img = dft_center * mask
mask1_img = dft_center * mask1
# 第七步:使用np.fft.ifftshift将低频移动到原来的位置
img_idf = np.fft.ifftshift(mask_img)
img1_idf = np.fft.ifftshift(mask1_img)
# 第八步:使用cv2.idft进行傅里叶的反变换
img_idf = cv2.idft(img_idf)
img1_idf = cv2.idft(img1_idf)
# 第九步:使用cv2.magnitude转换为空间域
img_idf = cv2.magnitude(img_idf[:, :, 0], img_idf[:, :, 1])
img1_idf = cv2.magnitude(img1_idf[:, :, 0], img1_idf[:, :, 1])
# 第十步:输出图像
plt.subplot(131),plt.title('夕阳')
plt.imshow(img, cmap='gray'),plt.axis('off')
plt.subplot(132),plt.title('低通夕阳 mask=20')
plt.imshow(img_idf, cmap='gray'),plt.axis('off')
plt.subplot(133),plt.title('低通夕阳 mask=50')
plt.imshow(img1_idf, cmap='gray'),plt.axis('off')
plt.show()

巴特沃斯低通滤波

import cv2
import numpy as np
def combine_images(images):  # 滤波后图像与频域图组合在一起
    shapes = np.array([mat.shape for mat in images])
    rows = np.max(shapes[:, 0])
    copy_imgs = [cv2.copyMakeBorder(img, 0, rows - img.shape[0], 0, 0,cv2.BORDER_CONSTANT, (0, 0, 0)) for img in images]
    return np.hstack(copy_imgs)


def fft(img):  # 傅里叶变换
    rows, cols = img.shape[:2]
    nrows = cv2.getOptimalDFTSize(rows)  # 得到傅里叶最优尺寸大小
    ncols = cv2.getOptimalDFTSize(cols)
    nimg = np.zeros((nrows, ncols))
    nimg[:rows, :cols] = img
    fft_mat = cv2.dft(np.float32(nimg), flags=cv2.DFT_COMPLEX_OUTPUT)
    return np.fft.fftshift(fft_mat)


def fft_image(fft_mat):
    log_mat = cv2.log(1 + cv2.magnitude(fft_mat[:, :, 0], fft_mat[:, :, 1]))
    cv2.normalize(log_mat, log_mat, 0, 255, cv2.NORM_MINMAX)
    return np.uint8(np.around(log_mat))


def ifft(fft_mat):  # 逆傅里叶变换
    f_ishift_mat = np.fft.ifftshift(fft_mat)
    img_back = cv2.idft(f_ishift_mat)
    img_back = cv2.magnitude(*cv2.split(img_back))
    cv2.normalize(img_back, img_back, 0, 255, cv2.NORM_MINMAX)
    return np.uint8(np.around(img_back))


def fft_distances(m, n):
    u = np.array([i if i <= m / 2 else m - i for i in range(m)], dtype=np.float32)
    v = np.array([i if i <= m / 2 else m - i for i in range(m)], dtype=np.float32)
    v.shape = n, 1
    ret = np.sqrt(u * u + v * v)
    return np.fft.fftshift(ret)


def BWfilter(rows, cols, d0, n):  # 巴特沃斯低通滤波
    duv = fft_distances(*fft_mat.shape[:2])
    filter_mat = 1 / (1 + np.power(duv / d0, 2 * n))
    filter_mat = cv2.merge((filter_mat, filter_mat))
    return filter_mat


def do_filter(_=None):
    d0 = cv2.getTrackbarPos('D0', filter_win)
    n = cv2.getTrackbarPos('n', filter_win)
    filter_mat = BWfilter(fft_mat.shape[0], fft_mat.shape[1], d0, n)
    filtered_mat = filter_mat * fft_mat
    img_back = ifft(filtered_mat)
    cv2.imshow(image_win, combine_images([img_back, fft_image(filter_mat)]))


if __name__ == '__main__':
    img = cv2.imread('.//images//lena.jpg', 0)
    rows, cols = img.shape[:2]
    filter_win = 'Filter Parameters'
    image_win = 'Butterworth Low Pass Filtered Image'
    fft_mat = fft(img)
    cv2.namedWindow(filter_win)
    cv2.namedWindow(image_win)
    cv2.createTrackbar('D0', filter_win, 20, min(rows, cols) // 4, do_filter)
    cv2.createTrackbar('n', filter_win, 1, 5, do_filter)
    do_filter()
    cv2.resizeWindow(filter_win, 512, 20)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
逆滤波

详细信息参考:图像处理---逆滤波和维纳滤波-CSDN博客,逆滤波-CSDN博客

维纳滤波

import cv2
import math
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置显示中文字体
plt.rcParams['axes.unicode_minus'] = False  # 设置正常显示符号



# 仿真运动模糊
def motion_process(image_size, motion_angle):
    PSF = np.zeros(image_size)
    center_position = (image_size[0] - 1) / 2

    slope_tan = math.tan(motion_angle * math.pi / 180)
    slope_cot = 1 / slope_tan
    if slope_tan <= 1:
        for i in range(15):
            offset = round(i * slope_tan)
            PSF[int(center_position + offset), int(center_position - offset)] = 1
        return PSF / PSF.sum()  # 对点扩散函数进行归一化亮度
    else:
        for i in range(15):
            offset = round(i * slope_cot)
            PSF[int(center_position - offset), int(center_position + offset)] = 1
        return PSF / PSF.sum()



# 对图片进行运动模糊
def make_blurred(input, PSF, eps):
    input_fft = np.fft.fft2(input)  # 进行二维数组的傅里叶变换
    PSF_fft = np.fft.fft2(PSF) + eps
    blurred = np.fft.ifft2(input_fft * PSF_fft)
    blurred = np.abs(np.fft.fftshift(blurred))
    return blurred


def inverse(input, PSF, eps):  # 逆滤波处理
    input_fft = np.fft.fft2(input)
    PSF_fft = np.fft.fft2(PSF) + eps  # 噪声功率,这是已知的,考虑epsilon
    result = np.fft.ifft2(input_fft / PSF_fft)  # 计算F(u,v)的傅里叶反变换
    result = np.abs(np.fft.fftshift(result))
    return result


def gaosi(image, mean=0, var=0.01):
    # 高斯噪声,mean均值, var方差
    image = np.array(image / 255, dtype=float)
    noise = np.random.normal(mean, var ** 0.5, image.shape)
    img_noise = image + noise
    if img_noise.min() < 0:
        clip = -1
    else:
        clip = 0
    img_noise = np.clip(img_noise, clip, 1.0)
    img_noise = np.uint8(img_noise * 255)
    return img_noise


def wiener(input, PSF, eps, K=0.01):  # 维纳滤波,K=0.01
    input_fft = np.fft.fft2(input)
    PSF_fft = np.fft.fft2(PSF) + eps
    PSF_fft_1 = np.conj(PSF_fft) / (np.abs(PSF_fft) ** 2 + K)
    result = np.fft.ifft2(input_fft * PSF_fft_1)
    result = np.abs(np.fft.fftshift(result))
    return result




if __name__ == '__main__':
    image = cv2.imread('.//images//lena.jpg')
    image = image[:,:,::-1]
    # 灰度化处理
    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # 进行运动模糊处理
    img_h, img_w = image.shape[0:2]
    PSF = motion_process((img_h, img_w), 60)
    blurred = np.abs(make_blurred(image, PSF, 1e-3))
    plt.subplot(231), plt.axis('off')
    plt.title("运动模糊")
    plt.imshow(blurred, cmap='gray')
    result_inv = inverse(blurred, PSF, 1e-3)  # 逆滤波
    plt.subplot(232), plt.axis('off')
    plt.title("逆滤波")
    plt.imsave('.//images/lenaInv.jpg', result_inv)
    plt.imshow(result_inv, cmap='gray')

    # 添加噪声,standard_normal产生随机的函数
    blurred_noisy = gaosi(image)
    plt.subplot(233), plt.axis('off')
    plt.title("运动模糊 & 高斯噪声")
    plt.imshow(blurred_noisy, cmap='gray')  # 显示添加噪声且运动模糊的图像
    plt.imsave('.//images/lenaInv_Gaosi.jpg', blurred_noisy)
    # 对添加噪声的图像进行逆滤波
    result_invn = inverse(blurred_noisy, PSF, 0.1 + 1e-3)
    plt.subplot(234), plt.axis('off')
    plt.title("逆滤波(高斯&运动模糊)")
    plt.imshow(result_invn, cmap='gray')
    plt.imsave('.//images/lenaInv_gaosi_n.jpg', result_invn)
    # 维纳滤波
    resultwd = wiener(blurred, PSF, 1e-3)
    plt.subplot(235), plt.axis('off')
    plt.title("维纳滤波(k=0.01)")
    plt.imshow(resultwd, cmap='gray')
    plt.imsave('.//images/lenaWd.jpg', resultwd)

    plt.show()

17-什么是图像锐化(图像增强)

 

18-图像锐化的相关算子

Kirsch算子

Laplacian算子

卷积核算子

import cv2
img = cv2.imread('.//images//lena.jpg')   # 读入一幅彩色图像
img = img[:,:,::-1]
# 进行拉普拉斯算子运算
lap = cv2.Laplacian(img,cv2.CV_16S,ksize=3)
# 求绝对值并转为8比特图像
laplacian = cv2.convertScaleAbs(lap)
# 显示滤波后的图像
plt.subplot(121),plt.title('Origin Image')
plt.imshow(img),plt.axis('off')
plt.subplot(122),plt.title("Laplacian")
plt.imshow(laplacian, ),plt.axis('off')
plt.show()


















Scharr算子

19-什么是非锐化掩膜和高频提升滤波

高频提升滤波(High-Boost Filtering)是一种图像增强技术,用于增强图像的高频细节信息,从而使图像看起来更加清晰和锐利。

在高频提升滤波中,首先对图像进行高通滤波,以突出图像中的高频信息(边缘、纹理等),然后将增强后的高频信息与原始图像相加,以生成增强后的图像。

具体来说,高频提升滤波的步骤如下:

  1. 对原始图像进行高通滤波,通常使用拉普拉斯算子或高通滤波器(如Sobel、Prewitt等)来突出图像的高频细节。
  2. 将得到的高频信息乘以一个增益因子(称为增益常数),这个增益因子控制着对高频信息的增强程度。
  3. 将增强后的高频信息与原始图像相加,得到增强后的图像。

通过高频提升滤波,可以增强图像的边缘、纹理等细节信息,从而改善图像的视觉效果和质量。这种滤波技术常用于图像增强、图像复原和图像处理等领域。

import cv2
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置显示中文字体
plt.rcParams['axes.unicode_minus'] = False  # 设置正常显示符号



def fun_uh(img, k):
    imgBlur = cv2.GaussianBlur(img, (3, 3), 0)
    imgMask = img - imgBlur
    res = img + np.uint8(k * imgMask)
    return res


img = cv2.imread('.//images//xiyng.png', 0)
mask_img = fun_uh(img, 1)  # 非锐化掩膜,系数k=1
high_img = fun_uh(img, 3)  # 高频提升滤波,系数k=3

# 显示图像
plt.subplot(131), plt.title('Origin Image')
plt.imshow(img, cmap='gray'), plt.axis('off')
plt.subplot(132), plt.title("非锐化掩膜")
plt.imshow(mask_img, cmap='gray'), plt.axis('off')
plt.subplot(133), plt.title("非锐化掩膜")
plt.imshow(high_img, cmap='gray'), plt.axis('off')
plt.subplot(133), plt.title("高频提升滤波")
plt.show()

20-什么是运动模糊

图像去模糊

21-图像退化与复原

退化 图像

图像的退化是指图像在形成、传输和记录过程中,由于成像系统、传输介质和设备的不完善,使图像的质量下降(变坏)其典型表现为: 模糊失真有噪声

针对图像的模糊、失真、有噪声等造成的退化,我们需要对退化后的图像进行复原。

图像复原就是尽可能恢复退化图像的本来面目,它是沿图像退化的逆过程进行处理,也就是如果我们知道图像是经历了什么样的过程导致退化,就可以按其逆过程来复原图像。

因此,图像复原过程流程如下:

找退化原因→建立退化模型→反向推演→恢复图像

典型的图像复原是根据图像退化的先验知识,建立退化现象的数学模型,再根据模型进行反向的推演运算,以恢复原来的景物图像。

因此,图像复原的关键是知道图像退化的过程,即图像退化模型。并据此采用相反的过程求得原始图像。

下列是用运动模糊函数的代码实现

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


# 仿真运动模糊
def motion_process(image_size, motion_angle):
    PSF = np.zeros(image_size)
    center_position = (image_size[0] - 1) / 2
    slope_tan = math.tan(motion_angle * math.pi / 180)
    slope_cot = 1 / slope_tan
    if slope_tan <= 1:
        for i in range(15):
            offset = round(i * slope_tan)
            PSF[int(center_position + offset), int(center_position - offset)] = 1
        return PSF / PSF.sum()  # 对点扩散函数进行归一化亮度
    else:
        for i in range(15):
            offset = round(i * slope_cot)
            PSF[int(center_position - offset), int(center_position + offset)] = 1
        return PSF / PSF.sum()


# 对图像进行运动模糊
def make_blurred(input, PSF, eps):
    input_fft = np.fft.fft2(input)  # 二维数组的傅里叶变换
    PSF_fft = np.fft.fft2(PSF) + eps
    blurred = np.fft.ifft2(input_fft * PSF_fft)
    blurred = np.abs(np.fft.fftshift(blurred))
    return blurred


img = cv2.imread('.//images//lena.jpg')
img = img[:, :, ::-1]
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
plt.subplot(121), plt.axis('off')
plt.title("Origin image"), plt.imshow(img_gray)

# 进行运动模糊处理
img_h, img_w = img.shape[0:2]
PSF = motion_process((img_h, img_w), 80)
blurred = np.abs(make_blurred(img_gray, PSF, 1e-3))
plt.subplot(121), plt.axis('off')
plt.title("Original")
plt.imshow(img_gray)
plt.subplot(122), plt.axis('off')
plt.title("Motion blurred")
plt.imshow(blurred)
plt.show()

下列是用运动模糊核实现的

import cv2
import numpy as np
from matplotlib import pyplot as plt


def  motion_blur(image, degree=12, angle=80):
    image = np.array(image)
    #生成任意角度的运动模糊kernel的矩阵,degree越大,模糊程度越高
    M = cv2.getRotationMatrix2D((degree / 2, degree / 2), angle, 1)
    motion_blur_kernel = np.diag(np.ones(degree))
#输出矩阵的对角线元素
    #放射变换函数
    motion_blur_kernel = cv2.warpAffine(motion_blur_kernel, M, (degree, degree))
    motion_blur_kernel = motion_blur_kernel / degree
    blurred = cv2.filter2D(image, -1, motion_blur_kernel)
    cv2.normalize(blurred, blurred, 0, 255, cv2.NORM_MINMAX)   #归一化函数

    blurred = np.array(blurred, dtype=np.uint8)
    return blurred
img = cv2.imread('.//images//lena.jpg')
img = img[:,:,::-1]
img_blurred = motion_blur(img)
plt.subplot(121), plt.axis('off')
plt.title('Origin image')
plt.imshow(img)
plt.subplot(122), plt.axis('off')
plt.title('Blurred image')
plt.imshow(img_blurred)
plt.show()

22-图像质量的评价

MSE:0.017521031141868513
PSNR:17.56440338462125
SSIM:0.7079986846390507
MSE:0.11591895232602846
PSNR:9.358455526669786
SSIM:0.05705373401169676
 




import cv2
import math
import numpy as np
from scipy.signal import convolve2d

def compute_mse(img1, img2):
    mse = np.mean( (img1/255. - img2/255.) ** 2)
    return mse
def compute_psnr(img1, img2):
    mse = np.mean((img1/1.0 - img2/1.0) ** 2 )
    if mse < 1.0e-10:
        return 100
    return 10 * math.log10(255.0**2/mse)
def matlab_style_gauss2D(shape=(3,3),sigma=0.5):
    m,n = [(ss-1.)/2. for ss in shape]
    y,x = np.ogrid[-m:m+1,-n:n+1]
    h = np.exp( -(x*x + y*y) / (2.*sigma*sigma) )
    h[ h < np.finfo(h.dtype).eps*h.max() ] = 0
    sumh = h.sum()
    if sumh != 0:
        h /= sumh
    return h
def filter2(x, kernel, mode='same'):
    return convolve2d(x, np.rot90(kernel, 2), mode=mode)
def compute_ssim(im1, im2, k1=0.01, k2=0.03, win_size=11, L=255):
    if not im1.shape == im2.shape:
        raise ValueError("Input Imagees must have the same dimensions")
    if len(im1.shape) > 2:
        raise ValueError("Please input the images with 1 channel")
    M, N = im1.shape
    C1 = (k1*L)**2
    C2 = (k2*L)**2
    window = matlab_style_gauss2D(shape=(win_size,win_size), sigma=1.5)
    window = window/np.sum(np.sum(window))
    if im1.dtype == np.uint8:
          im1 = np.double(im1)
    if im2.dtype == np.uint8:
          im2 = np.double(im2)
    mu1 = filter2(im1, window, 'valid')
    mu2 = filter2(im2, window, 'valid')
    mu1_sq = mu1 * mu1
    mu2_sq = mu2 * mu2
    mu1_mu2 = mu1 * mu2
    sigma1_sq = filter2(im1*im1, window, 'valid') - mu1_sq
    sigma2_sq = filter2(im2*im2, window, 'valid') - mu2_sq
    sigmal2 = filter2(im1*im2, window, 'valid') - mu1_mu2
    ssim_map = ((2*mu1_mu2+C1) * (2*sigmal2+C2)) / ((mu1_sq+mu2_sq+C1) * (sigma1_sq+sigma2_sq+C2))
    return np.mean(np.mean(ssim_map))

if __name__ == '__main__':
    img_origin = cv2.imread('.//images/lena.jpg',0)
    cv2.imshow('Origin image',img_origin)
    img_Mb = cv2.imread('.//images/lenaInv.jpg',0)
    cv2.imshow('Move blur image',img_Mb)
    img_wd = cv2.imread('.//images/lenaWd.jpg',0)
    cv2.imshow('Winer filter image',img_wd)
    img_wdn = cv2.imread('.//images/lenaInv_gaosi_n.jpg', 0)
    cv2.imshow('nWiner noise filter image', img_wdn)

    # 原图与运动模糊后的维纳滤波图像之间的评价参数
    mse = compute_mse(img_origin, img_wd)
    print('MSE:{}'.format(mse))
    psnr = compute_psnr(img_origin, img_wd)
    print('PSNR:{}'.format(psnr))
    ssim = compute_ssim(img_origin, img_wd)
    print('SSIM:{}'.format(ssim))

    # 原图与运动模糊+噪声后的维纳滤波图像之间的评价参数
    mse = compute_mse(img_origin, img_wdn)
    print('MSE:{}'.format(mse))
    psnr = compute_psnr(img_origin, img_wdn)
    print('PSNR:{}'.format(psnr))
    ssim = compute_ssim(img_origin, img_wdn)
    print('SSIM:{}'.format(ssim))

    cv2.waitKey(0)
    cv2.destroyAllWindows()


23-数学形态学

数学形态学(mathematical morphology)诞生于1964年,基本思想是用具有一定形态的结构元素(SE,structural element)或内核去量度和提取数字图像中的对应形状,以实现对图像进行分析和识别的目的。 形态学分析是一门建立在集合论基础上的学科,它是几何形态分析和描述的有力工具,是一种新的图像处理与分析方法。 数学形态学的基本思想也适应于图像处理的很多方面,包括图像增强、边缘检测、图像分割、特征提取、图像复原、文字识别、医学图像处理、图像压缩,以及机器视觉等众多领域。 在工农业生产中,视觉零部件检测、产品质量检测、食品安全检测、生物医学图像分析和纹理分析等方面,数学形态学取得了非常成功的应用,创造了较好的经济效益和社会效益。

相较于现在使用的神经网络来检测织物缺陷,数学形态学方法是传统的检测方法,其检测的核心思想就是用具有一定形态学结构的元素去度量和提取图像中对应的形状,以达到对图像分析和识别的目的。所以数学形态学对于具有较强的几何形状的织物缺陷有较强的检测能力,但是现实情况是织物疵点的种类繁多,疵点的大小不一,形态各异,并且不是所有织物的缺陷都是具有几何形状的特点,所以要找到一种能检测出所有疵点的通用模式识别方法是很困难的,因此数学形态学一般用来检测破洞,断经,缺纬这三类的织物缺陷

数学形态学是基于图像形状的操作,主要针对的是二值图像,最基本的操作是腐蚀和膨胀,其他如开运算、闭运算、高帽运算、黑帽运算等都是在腐蚀和膨胀操作的基础上进行的。

腐蚀

膨胀是对图像中的高亮部分进行膨胀,类似于领域扩张,结果拥有比原图更大的高亮区域;

腐蚀是卷积核沿着图像滑动,把物体的边界腐蚀掉。如果卷积核对应的原图所有像素值为1,那么中心元素就保持原来的值,否则变为零。 对于消除小的白噪声、分离两个连接的对象等非常有用。腐蚀是和膨胀相反的操作,它将0值扩充到邻近像素,扩大黑色部分,减小白色部分。可用来提取骨干信息,去掉毛刺,去掉孤立的像素。

  • 对象大小减少1个像素(3*3)
  • 平滑对象边缘
  • 弱化或者分割图像之间的半岛型连接

import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('.//images//sl.png',0)
kernel = np.ones((5,5),np.uint8)
erosion = cv2.erode(img,kernel,iterations = 1)
plt.figure('erode',figsize=(8,8))
plt.subplot(121)
plt.title('origin image')
plt.imshow(img,plt.cm.gray)
plt.axis('off')
plt.subplot(122)
plt.title('erode image')
plt.imshow(erosion,plt.cm.gray)
plt.axis('off')
plt.show()

from skimage import data
import skimage.morphology as sm
import matplotlib.pyplot as plt

img = data.checkerboard()  # 调用棋格板
# 用边长为5的正方形滤波器进行腐蚀滤波
dst1 = sm.erosion(img, sm.square(5))
# 用边长为15的正方形滤波器进行腐蚀滤波
dst2 = sm.erosion(img, sm.square(15))

plt.figure('morphology', figsize=(8, 8))
plt.subplot(131)
plt.title('origin image')
plt.imshow(img, plt.cm.gray)
plt.subplot(132)
plt.title('5*5 eroded image')
plt.imshow(dst1, plt.cm.gray)

plt.subplot(133)
plt.title(' 15*15 eroded image')
plt.imshow(dst2, plt.cm.gray)
plt.show()

结论:可以看出,原图对比腐蚀后的图,腐蚀让图片“重”色彩,却缺少细节,说明图片被“放大”

膨胀

腐蚀是对原图的高亮部分腐蚀,类似于领域被蚕食,结果拥有比原图更小的高亮区域。

膨胀的原理是卷积核所对应的原图像像素值只要有一个是1,中心像素值就是1,即用结构元素的原点遍历所有原图像中的背景点,看结构元素与原图像中的前景点有没有重叠的部分,若有重叠的部分,则标记原图像中的该背景点,使之成为新的前景点,它与腐蚀正好相反。 一般对二值图像进行操作,找到像素值为1的点,将它的邻近像素点都设置成1值(1值表示白,0值表示黑),因此膨胀操作可以扩大白色值范围,压缩黑色值范围,因此它会增加图像中的白色区域或增加前景对象的大小。通常,膨胀一般用来扩充边缘或填充小的孔洞

  • 对象大小增加一个像素(3*3)
  • 平滑对象边缘
  • 减少或者填充对象之间的距离

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('.//images//sl.png',0)
kernel = np.ones((5,5),np.uint8)
dilation = cv2.dilate(img,kernel,iterations = 1)

plt.figure('erode',figsize=(8,8))
plt.subplot(121)
plt.title('origin image')
plt.imshow(img,plt.cm.gray)
plt.axis('off')
plt.subplot(122)
plt.title(' dilate image')
plt.imshow(dilation,plt.cm.gray)
plt.axis('off')
plt.show()

from skimage import data
import skimage.morphology as sm
import matplotlib.pyplot as plt

img = data.checkerboard()
#边长为5的正方形卷积核
dst1 = sm.dilation(img,sm.square(5))
#边长为15的正方形卷积核
dst2 = sm.dilation(img,sm.square(15))

plt.figure('morphology',figsize=(8,8))
plt.subplot(131)
plt.title('origin image')
plt.imshow(img,plt.cm.gray)
plt.subplot(132)
plt.title('5*5 dilation image')
plt.imshow(dst1,plt.cm.gray)
plt.subplot(133)
plt.title('15*15 dilation image')
plt.imshow(dst2,plt.cm.gray)
plt.show( )

开运算

开运算是先腐蚀然后再膨胀的运算,主要用于消除亮度较高的细小区域,在纤细点处分离物体,对于较大物体,可以在不明显改变其面积的情况下平滑其边界。 开运算的典型应用是从目标中消除细小的尖刺,并断开窄小的连接,保持面积大小不变等,它还能使目标轮廓的边界平滑,对于消除噪音很有用

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('.//images//sl.png',0)
kernel = np.ones((5,5),np.uint8)
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)

plt.figure(' MORPH_OPEN ',figsize=(8,8))
plt.subplot(121)
plt.title('origin image')
plt.imshow(img,plt.cm.gray)
plt.axis('off')
plt.subplot(122)
plt.title(' opening image')
plt.imshow(opening,plt.cm.gray)
plt.axis('off')
plt.show()

from skimage import io,color
import skimage.morphology as sm
import matplotlib.pyplot as plt

img=color.rgb2gray(io.imread('.//images//sl.png'))
#用直径为4的圆形滤波器进行开运算
dst=sm.opening(img,sm.disk(4))
plt.figure('morphology',figsize=(8,8))
plt.subplot(121)
plt.title('origin image')
plt.imshow(img,plt.cm.gray)
plt.axis('off')
plt.subplot(122)
plt.title('opening image')
plt.imshow(dst,plt.cm.gray)
plt.axis('off')
plt.show()

闭运算

闭运算与开运算正好相反,先膨胀然后再腐蚀。主要用于填充白色物体内细小黑色空洞的区域,连接邻近物体,同一个结构元,多次迭代处理,可以在不明显改变其面积的情况下平滑其边界。 闭运算的典型应用是填充小孔、连接狭窄的断裂以及闭合图像中目标间的间隙而不改变目标的尺寸。它也能平滑目标的轮廓,对消除目标对象内部的小孔或消除目标对象上的小黑点很有用

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('.//images/sl.png',0)
kernel = np.ones((5,5),np.uint8)
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)

plt.figure('MORPH_CLOSE ',figsize=(8,8))
plt.subplot(121)
plt.title('origin image')
plt.imshow(img,plt.cm.gray)
plt.axis('off')
plt.subplot(122)
plt.title('closing image')
plt.imshow(closing,plt.cm.gray)
plt.axis('off')
plt.show()

from skimage import io,color
import skimage.morphology as sm
import matplotlib.pyplot as plt

img=color.rgb2gray(io.imread('.//images/sl.png'))
#用边长为5的圆形滤波器进行膨胀滤波
dst=sm.closing(img,sm.disk(5))
plt.figure('morphology',figsize=(8,8))
plt.subplot(121)
plt.title('origin image')
plt.imshow(img,plt.cm.gray)
plt.axis('off')
plt.subplot(122)
plt.title('closing image')
plt.imshow(dst,plt.cm.gray)
plt.axis('off')

plt.show()
 形态学梯度:

形态学梯度(Morphological Gradient)是形态学梯度实为膨胀图与腐蚀图之差,来实现增强结构元素领域中像素的强度,突出高亮区域的外围。所以形态学梯度的处理结果是图像中物体的边界,处理结果看起来像目标对象的轮廓。 在OpenCV的cv2.morphologyEx()函数中进行梯度运算,其op形态学操作的类型设置为cv2.MORPH_GRADIENT,即相当于 dst=morph_grad(src,element)=dilate(src,element)-erode(src,element) 计算图像的形态学梯度是形态学重要操作,常常将膨胀和腐蚀基础操作组合起来一起使用实现一些复杂的图像形态学梯度。

作用:保留物体的边缘轮廓,便于轮廓分析(突出高亮区域)

形态学梯度定义:        G = I⊕S − I!S

(其中G为输出图像,I为输入图像,S为卷积核函数。⊕为膨胀运算,!为腐蚀运算。

形态学梯度操作的输出图像像素值是在对应结构元素而非局部过渡区域所定义的领域中灰度级强度变化的最大值。
对二值图像进行形态学操作可以将团块(blob)的边缘突出出来,可以用形态学梯度来保留物体的边缘轮廓。

(1)基本梯度

——基本梯度是用膨胀后的图像减去腐蚀后的图像得到差值图像,称为梯度图像也是OpenCV中支持的计算形态学梯度的方法,而此方法得到的梯度称为基本梯度。

(2)内部梯度

——是用原图像减去腐蚀之后的图像得到差值图像,称为图像的内部梯度。

(3)外部梯度

——是用图像膨胀之后再减去原来的图像得到的差值图像,称为图像的外部梯度。

(4)方向梯度

——方向梯度是使用X方向与Y方向的直线作为结构元素之后得到图像梯度,用X方向的直线做结构元素,分别做膨胀与腐蚀操作之后得到的图像,再与原图像求差值得到X方向梯度;用Y方向的直线做结构元素,分别做膨胀与腐蚀操作之后得到的图像,再与原图像求差值得到Y方向梯度。

import cv2
import numpy as np


def gradient_basic(image):  # 基本梯度
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    ret, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
    dst = cv2.morphologyEx(binary, cv2.MORPH_GRADIENT, kernel)
    cv2.imshow("gradient", dst)


def gradient_in_out(image):
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
    dm = cv2.dilate(image, kernel)
    em = cv2.erode(image, kernel)
    dst1 = cv2.subtract(image, em)  # 内部梯度
    dst2 = cv2.subtract(dm, image)  # 外部梯度
    cv2.imshow("internal", dst1)
    cv2.imshow("external", dst2)


def gradient_X(image):
    kernel_x = np.array([[0, 0, 0], [1, 1, 1], [0, 0, 0]], dtype=np.uint8)
    dmx = cv2.dilate(image, kernel_x)
    emx = cv2.erode(image, kernel_x)
    dstx = cv2.subtract(dmx, emx)  # X方向梯度
    cv2.imshow("X-direction", dstx)


def gradient_Y(image):
    kernel_y = np.array([[0, 1, 0], [0, 1, 0], [0, 1, 0]], dtype=np.uint8)
    dmy = cv2.dilate(image, kernel_y)
    emy = cv2.erode(image, kernel_y)
    dsty = cv2.subtract(dmy, emy)  # Y方向梯度
    cv2.imshow("Y-direction", dsty)


src = cv2.imread(".//images//sl.png")
cv2.namedWindow("original", cv2.WINDOW_AUTOSIZE)
cv2.imshow("original", src)
gradient_basic(src)
gradient_in_out(src)
gradient_X(src)
gradient_Y(src)
cv2.waitKey(0)
cv2.destroyAllWindows()

详细信息参考:OpenCV(八)形态学操作3--形态学梯度实现轮廓分析(基本梯度、内部梯度、外部梯度、方向梯度X(Y))_opencv 形态学梯度-CSDN博客

 顶帽(top-hat):

高帽运算是将原图像减去它的开运算值,开运算可以消除暗背景下的较亮区域,所以高帽运算可以得到原图像中灰度较亮的区域。 高帽运算的一个很重要的作用就是校正不均匀光照,返回比结构元素小的白点。

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('.//images/sl.png',0)
kernel = np.ones((9,9),np.uint8)
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)

plt.figure(' MORPH_ TOPHAT ',figsize=(8,8))
plt.subplot(121)
plt.title('origin image')
plt.imshow(img,plt.cm.gray)
plt.axis('off')

plt.subplot(122)
plt.title(' tophat image')
plt.imshow(tophat,plt.cm.gray)
plt.axis('off')
plt.show()

from skimage import io,color
import skimage.morphology as sm
import matplotlib.pyplot as plt
img=color.rgb2gray(io.imread('.//images/sl.png'))
dst=sm.white_tophat(img,sm.square(30))
plt.figure('morphology',figsize=(8,8))
plt.subplot(121)
plt.title('origin image')
plt.imshow(img,plt.cm.gray)
plt.axis('off')
plt.subplot(122)
plt.title('tophat image')
plt.imshow(dst,plt.cm.gray)
plt.axis('off')
plt.show()
 黑帽(black-hat)

        黑帽就是将原图像减去它的闭运算值,闭运算可以删除亮度较高背景下的较暗区域,所以黑帽运算可以得到原图片中灰度较暗的区域,故而又称黑帽变换。返回比结构元素小的黑点,且将这些黑点反色

import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('.//images/sl.png',0)
kernel = np.ones((11,11),np.uint8)
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
plt.figure(' MORPH_ BLACKHAT ',figsize=(8,8))
plt.subplot(121)
plt.title('origin image')
plt.imshow(img,plt.cm.gray)
plt.axis('off')
plt.subplot(122)
plt.title(' blackhat image')
plt.imshow(blackhat,plt.cm.gray)
plt.axis('off')
plt.show()

from skimage import io,color
import skimage.morphology as sm
import matplotlib.pyplot as plt

img=io.imread('./images/sl.png')
img_gray=color.rgb2gray(img)
dst=sm.black_tophat(img_gray,sm.square(30))
plt.figure('morphology',figsize=(8,8))
plt.subplot(121)
plt.title('origin image')
plt.imshow(img,plt.cm.gray)
plt.axis('off')
plt.subplot(122)
plt.title('blackhat image')
plt.imshow(dst,plt.cm.gray)
plt.axis('off')
plt.show()

import cv2


def nothing(*args):  # 设置回调函数
    pass


# 创建窗口
cv2.namedWindow('morphology', cv2.WINDOW_FREERATIO)
r, MAX_R = 0, 20  # 结构元素初始半径,最大半径
i, MAX_I = 0, 20  # 初始迭代次数,最大迭代次数
# 创建滑动条,分别为半径和迭代次数
cv2.createTrackbar('r', 'morphology', r, MAX_R, nothing)
cv2.createTrackbar('i', 'morphology', i, MAX_I, nothing)

src = cv2.imread('.//images/sl.png', flags=0)
while True:
    r = cv2.getTrackbarPos('r', 'morphology')  # 获得进度条上当前的r值
    i = cv2.getTrackbarPos('i', 'morphology')  # 获得进度条上当前的i值
    # 创建结构元素
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2 * r + 1, 2 * r + 1))
    # 形态学处理:形态梯度
    result = cv2.morphologyEx(src, cv2.MORPH_GRADIENT, kernel, iterations=i)

    # 显示效果
    cv2.imshow('morphology', result)
    ch = cv2.waitKey(1)
    if ch == 27:  # 按Esc退出
        break
cv2.destroyAllWindows()

详细信息参考:形态学运算、膨胀、腐蚀、开运算、闭运算、形态学梯度(附代码)_数学形态学是一门建立在格论和拓扑学基础之上的图像分析学科是数学形态学图像-CSDN博客

24-图像边缘检测

形态学检测边缘的原理:膨胀时图像中的物体会向周围“扩张”,腐蚀时图像中的物体会“收缩”。由于这两幅图像其变化的区域只发生在边缘,所以将两幅图像相减,得到的就是图像中物体的边缘。

边缘检测是图像处理与计算机视觉中的重要技术之一。其目的是检测识别出图像中亮度变化剧烈的像素点构成的集合。图像边缘的正确检测对于分析图像中的内容、实现图像中物体的分割、定位等具有重要的作用。边缘检测大大减少了源图像的数据量,剔除了与目标不相干的信息,保留了图像重要的结构属性。

图像的边缘指的是图像中像素灰度值突然发生变化的区域,如果将图像的每一行像素和每一列像素都描述成一个关于灰度值的函数,那么图像的边缘对应在灰度值函数中是函数值突然变大的区域。函数值的变化趋势可以用函数的导数描述。当函数值突然变大时,导数也必然会变大,而函数值变化较为平缓区域,导数值也比较小,因此可以通过寻找导数值较大的区域去寻找函数中突然变化的区域,进而确定图像中的边缘位置。

import cv2
import matplotlib.pyplot as plt

image = cv2.imread(".//images/sl.png", cv2.IMREAD_GRAYSCALE)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
dilate_img = cv2.dilate(image, kernel)
erode_img = cv2.erode(image, kernel)

absdiff_img = cv2.absdiff(dilate_img, erode_img);  # 将两幅图像相减获得边缘
# 上面得到的结果是灰度图,将其二值化以便观察结果
retval, threshold_img = cv2.threshold(absdiff_img, 40, 255, cv2.THRESH_BINARY);
# 反色,对二值图每个像素取反
result = cv2.bitwise_not(threshold_img)
cv2.imshow("origin_img", image)
cv2.imshow("dilate_img", dilate_img)
cv2.imshow("erode_img", erode_img)
cv2.imshow("absdiff_img", absdiff_img)
cv2.imshow("threshold_img", threshold_img)

cv2.imshow("result", result)
plt.subplot(231), plt.imshow(image, 'gray'), plt.title('original image'), plt.axis('off')
plt.subplot(2, 3, 2), plt.imshow(dilate_img, cmap='gray'), plt.title("dilated image"), plt.axis('off')
plt.subplot(2, 3, 3), plt.imshow(erode_img, cmap='gray'), plt.title("erode image"), plt.axis('off')
plt.subplot(2, 3, 4), plt.imshow(absdiff_img, cmap='gray'), plt.title("absdiff image"), plt.axis('off')
plt.subplot(2, 3, 5), plt.imshow(threshold_img, cmap='gray'), plt.title("threshold image"), plt.axis('off')
plt.subplot(2, 3, 6), plt.imshow(result, cmap='gray'), plt.title("result image"), plt.axis('off')
plt.show()

  • 29
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

k笔墨丹青

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值