人工智能 — 数字图像

目录

  • 一、图像
    • 1、像素
    • 2、图像分辨率
    • 3、RGB 模型
    • 4、灰度
    • 5、通道
    • 6、对比度
    • 7、RGB 转化为 Gray
    • 8、RGB 值转化为浮点数
    • 9、二值化
    • 10、常用视觉库
    • 11、频率
    • 12、幅值
  • 二、图像的取样与量化
    • 1、数字图像
    • 2、取样
    • 3、量化
  • 三、上采样与下采样
    • 1、上采样(upsampling)
    • 2、下采样(subsampled)
    • 3、插值方法
      • 1、最邻近插值
      • 2、 双线性插值
  • 四、直方图
    • 1、概念
    • 2、性质
    • 3、应用
    • 4、均衡化
      • 1、概念
      • 2、方法
      • 3、公式
  • 五、滤波和卷积
    • 1、概念
    • 2、滤波器/过滤器/卷积核/Kernel
    • 3、卷积
      • 1、步长/Stride
      • 2、填充/Pading
      • 3、多通道卷积

一、图像

1、像素

像素分辨率的单位。

像素是构成位图图像最基本的单元,每个像素都有自己的颜色。

2、图像分辨率

图像分辨率就是单位英寸内的像素点数

  • 单位为 PPI(Pixels Per Inch),通常叫做像素每英寸。
  • PPI 表示的是每英寸对角线上所拥有的的像素数目

P P I = ( 水平分辨率 ) 2 + ( 垂直分辨率 ) 2 对角线尺寸 PPI = \frac{\sqrt{(\text{水平分辨率})^2 + (\text{垂直分辨率})^2}}{\text{对角线尺寸}} PPI=对角线尺寸(水平分辨率)2+(垂直分辨率)2

  • 屏幕尺寸指的是对角线长度

3、RGB 模型

色彩三原色(CMYK):品红、黄、青

光学三原色(RGB):红、绿、蓝

在这里插入图片描述

RGB 颜色模型是三维直角坐标颜色系统中的一个单位正方体。

在正方体的主对角线上,各原色的量相等,产生由暗到亮的白色,即灰度

正方体的其它6个角点分别为红、黄、绿、青、蓝和品红。

在这里插入图片描述

4、灰度

表示图像像素明暗程度的数值,也就是黑白图像中点的颜色深度。

范围一般为0-255。白色为255,黑色为0。

5、通道

把图像分解成一个或多个颜色成分。

  • 单通道:一个像素点只需一个数值表示,只能表示灰度,0为黑色。(二值图&灰度图)

  • 三通道:RGB 模式,把图像分为红绿蓝三个通道,可以表示彩色,全0表示黑色。

  • 四通道:RGBA 模式,在 RGB 基础上加上 alpha 通道,表示透明度,alpha=0 表示全透明。

6、对比度

指不同颜色之间的差别。

对比度 = 最大灰度值/最小灰度值

7、RGB 转化为 Gray

  • 浮点算法:Gray = R3.0+G0.59+B0.11
  • 整数算法:Gray = (R30+G59+B11)/100
  • 移位算法:Gray = (R76+G151+B28)>>8
  • 平均算法:Gray = (R+G+B)/3
  • 仅取绿色:Gray = G

为什么很多图像识别将彩色图像灰度化?

对颜色不敏感的需求将彩色图像灰度化可以降低计算成本,减少复杂性,节省存储空间。

8、RGB 值转化为浮点数

浮点数运算结果更精确,整数运算中会因丢弃小数部分可能导致颜色值严重失真,计算过程越多越失真。

将 RGB 值转化为0-1的浮点数:x/255 即可。

9、二值化

将图像转换为只包含两个像素值的过程。

10、常用视觉库

opencv:安装使用 pip install opencv-python,使用时用 import cv2

matplotlib:安装使用 pip install matplotlib,使用时用 import matplotlib.pyplot as plt

skimage:安装使用 pip install scikit-image,使用时用 import skimage

注意:

opencv 对于读进来的图片的通道排列是 BGR,而不是主流的 RGB!

opencv 读入的矩阵是 BGR,转为 RGB 方法:

img = cv2.imread(‘1.jpg’)

img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)

代码实现

  • 图片灰度化(方式一)
import cv2  # 导入 OpenCV 库,用于图像处理
import numpy as np  # 导入 NumPy 库,用于数组操作

# 读取图片
img = cv2.imread("img/lenna.png")
# 获取图片的 high 和 wide
h, w = img.shape[:2]
# 创建一张和当前图片大小一样的单通道图片
img_gray = np.zeros([h, w], img.dtype)

# 遍历图片的每一个像素
for i in range(h):
    for j in range(w):
        # 取出当前 high 和 wide 中的 BGR 坐标
        m = img[i, j]  
        # 将 BGR 坐标转化为 gray 坐标并赋值给新图像
        img_gray[i, j] = int(m[0] * 0.11 + m[1] * 0.59 + m[2] * 0.3)

# 打印灰度图像的值
print(img_gray)
# [[162 162 162 ... 169 155 128]
#  [162 162 162 ... 169 155 128]
#  [162 162 162 ... 169 155 128]
#  ...
#  [ 42  42  49 ... 104 100  98]
#  [ 43  43  54 ... 103 105 108]
#  [ 43  43  54 ... 103 105 108]]

# 在窗口中显示灰度图像
cv2.imshow("image show gray", img_gray)
# 0 表示一直等待,直到按下任意键
cv2.waitKey(0)
# 关闭所有窗口
cv2.destroyAllWindows()
  • 图片灰度化(方式二)
import matplotlib.pyplot as plt  # 导入 Matplotlib 库,用于绘制图表
from skimage.color import rgb2gray  # 从 skimage.color 模块中导入 rgb2gray 函数
# import cv2  # 导入 OpenCV 库,用于图像处理

# 在2x2的图表中,选中第1个子图
plt.subplot(221)

# 读取图片并将其存储在变量 img 中
img = plt.imread("img/lenna.png")
# 也可以使用 OpenCV 库读取图片
# img = cv2.imread("img/lenna.png", False)

# 在当前选中的子图中显示图片
plt.imshow(img)

# 打印提示信息
print("---image lenna----")
# 打印图像的数值表示
print(img)
# [[[0.8862745  0.5372549  0.49019608]
#   [0.8862745  0.5372549  0.49019608]
#   [0.8745098  0.5372549  0.52156866]
#   ...
#   [0.9019608  0.5803922  0.47843137]
#   [0.8666667  0.50980395 0.43137255]
#   [0.78431374 0.3882353  0.3529412 ]]
#
#  [[0.8862745  0.5372549  0.49019608]
#   [0.8862745  0.5372549  0.49019608]
#   [0.8745098  0.5372549  0.52156866]
#   ...
#   [0.9019608  0.5803922  0.47843137]
#   [0.8666667  0.50980395 0.43137255]
#   [0.78431374 0.3882353  0.3529412 ]]
#
#  [[0.8862745  0.5372549  0.49019608]
#   [0.8862745  0.5372549  0.49019608]
#   [0.8745098  0.5372549  0.52156866]
#   ...
#   [0.9019608  0.5803922  0.47843137]
#   [0.8666667  0.50980395 0.43137255]
#   [0.78431374 0.3882353  0.3529412 ]]
#
#  ...
#
#  [[0.32941177 0.07058824 0.23529412]
#   [0.32941177 0.07058824 0.23529412]
#   [0.36078432 0.10588235 0.22745098]
#   ...
#   [0.6784314  0.28627452 0.32941177]
#   [0.6745098  0.26666668 0.29803923]
#   [0.69411767 0.24313726 0.30980393]]
#
#  [[0.32156864 0.08627451 0.22352941]
#   [0.32156864 0.08627451 0.22352941]
#   [0.3764706  0.1254902  0.24313726]
#   ...
#   [0.7019608  0.27450982 0.30980393]
#   [0.70980394 0.2784314  0.31764707]
#   [0.7254902  0.2901961  0.31764707]]
#
#  [[0.32156864 0.08627451 0.22352941]
#   [0.32156864 0.08627451 0.22352941]
#   [0.3764706  0.1254902  0.24313726]
#   ...
#   [0.7019608  0.27450982 0.30980393]
#   [0.70980394 0.2784314  0.31764707]
#   [0.7254902  0.2901961  0.31764707]]]

# 将彩色图像转换为灰度图像
img_gray = rgb2gray(img)
# 也可以使用 OpenCV 库的函数实现灰度转换
# img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 或者直接使用原始图像(未经灰度转换)
# img_gray = img

# 在2x2的图表中,选中第2个子图
plt.subplot(222)

# 在当前选中的子图中显示灰度图像,使用灰度的颜色映射(cmap)为 'gray'
plt.imshow(img_gray, cmap='gray')

# 打印提示信息
print("---image gray----")
# 打印灰度图像的数值表示
print(img_gray)
# [[0.60802865 0.60802865 0.60779065 ... 0.6413741  0.57998234 0.46985728]
#  [0.60802865 0.60802865 0.60779065 ... 0.6413741  0.57998234 0.46985728]
#  [0.60802865 0.60802865 0.60779065 ... 0.6413741  0.57998234 0.46985728]
#  ...
#  [0.13746354 0.13746354 0.16881412 ... 0.37271804 0.35559532 0.34377727]
#  [0.14617059 0.14617059 0.1873059  ... 0.36788785 0.3729255  0.3846753 ]
#  [0.14617059 0.14617059 0.1873059  ... 0.36788785 0.3729255  0.3846753 ]]

# 显示 Matplotlib 的图表
plt.show()

在这里插入图片描述

  • 图片二值化
from skimage.color import rgb2gray  # 从 skimage.color 模块中导入 rgb2gray 函数
import numpy as np  # 导入 NumPy 库,用于数组操作
import matplotlib.pyplot as plt  # 导入 Matplotlib 库,用于绘制图表

# 在2x2的图表中,选中第1个子图
plt.subplot(221)
# 读取图片
img = plt.imread("img/lenna.png")
# 也可以使用 OpenCV 库读取图像
# img = cv2.imread("img/lenna.png", False)

# 在当前选中的子图中显示图片
plt.imshow(img)

# 打印提示信息
print("---image lenna----")
# 打印图像的数值表示
print(img)
# [[[0.8862745  0.5372549  0.49019608]
#   [0.8862745  0.5372549  0.49019608]
#   [0.8745098  0.5372549  0.52156866]
#   ...
#   [0.9019608  0.5803922  0.47843137]
#   [0.8666667  0.50980395 0.43137255]
#   [0.78431374 0.3882353  0.3529412 ]]
#
#  [[0.8862745  0.5372549  0.49019608]
#   [0.8862745  0.5372549  0.49019608]
#   [0.8745098  0.5372549  0.52156866]
#   ...
#   [0.9019608  0.5803922  0.47843137]
#   [0.8666667  0.50980395 0.43137255]
#   [0.78431374 0.3882353  0.3529412 ]]
#
#  [[0.8862745  0.5372549  0.49019608]
#   [0.8862745  0.5372549  0.49019608]
#   [0.8745098  0.5372549  0.52156866]
#   ...
#   [0.9019608  0.5803922  0.47843137]
#   [0.8666667  0.50980395 0.43137255]
#   [0.78431374 0.3882353  0.3529412 ]]
#
#  ...
#
#  [[0.32941177 0.07058824 0.23529412]
#   [0.32941177 0.07058824 0.23529412]
#   [0.36078432 0.10588235 0.22745098]
#   ...
#   [0.6784314  0.28627452 0.32941177]
#   [0.6745098  0.26666668 0.29803923]
#   [0.69411767 0.24313726 0.30980393]]
#
#  [[0.32156864 0.08627451 0.22352941]
#   [0.32156864 0.08627451 0.22352941]
#   [0.3764706  0.1254902  0.24313726]
#   ...
#   [0.7019608  0.27450982 0.30980393]
#   [0.70980394 0.2784314  0.31764707]
#   [0.7254902  0.2901961  0.31764707]]
#
#  [[0.32156864 0.08627451 0.22352941]
#   [0.32156864 0.08627451 0.22352941]
#   [0.3764706  0.1254902  0.24313726]
#   ...
#   [0.7019608  0.27450982 0.30980393]
#   [0.70980394 0.2784314  0.31764707]
#   [0.7254902  0.2901961  0.31764707]]]

# 使用 rgb2gray 函数将彩色图像转换为灰度图像
img_gray = rgb2gray(img)

# rows, cols = img_gray.shape  # 获取灰度图的行数和列数
# for i in range(rows):  # 遍历灰度图的每一行
#     for j in range(cols):  # 遍历灰度图的每一列
#         if (img_gray[i, j] <= 0.5):  # 如果当前像素值小于等于0.5
#             img_gray[i, j] = 0  # 将像素值设为0
#         else:
#             img_gray[i, j] = 1  # 否则将像素值设为1

# 使用 NumPy 的 where 函数实现二值化,大于等于0.5的像素值设为1,小于0.5的像素值设为0
img_binary = np.where(img_gray >= 0.5, 1, 0)
# 打印提示信息
print("-----imge_binary------")
# 打印二值化后的图像数组
print(img_binary)
# [[1 1 1 ... 1 1 0]
#  [1 1 1 ... 1 1 0]
#  [1 1 1 ... 1 1 0]
#  ...
#  [0 0 0 ... 0 0 0]
#  [0 0 0 ... 0 0 0]
#  [0 0 0 ... 0 0 0]]

# 打印二值化后的图像形状
print(img_binary.shape)
# (512, 512)

# 在2x2的图表中,选中第2个子图
plt.subplot(222)
# 在当前选中的子图中显示二值化后的图像,使用灰度的颜色映射(cmap)为 'gray'
plt.imshow(img_binary, cmap='gray')
# 显示 Matplotlib 的图表
plt.show()

在这里插入图片描述

11、频率

灰度值变化剧烈程度的指标,是灰度在平面空间上的梯度(高频、低频)。

12、幅值

幅值是在一个周期内,交流电瞬时出现的最大绝对值,也是一个正弦波,波峰到波谷的距离的一半。

二、图像的取样与量化

1、数字图像

计算机保存的图像都是一个一个的像素点,称为数字图像

图像数字化过程由图像的取样与量化来完成。

2、取样

就是要用多少点来描述一幅图像,取样结果质量的高低就是用图像的分辨率来衡量的。

3、量化

是指要使用多大范围的数值来表示图像采样之后的一个点。

数字化坐标值称为取样,

数字化幅度值称为量化。

在这里插入图片描述

在取样时,若横向的像素数(列数)为 M ,纵向的像素数(行数)为 N,则图像总像素数为 M*N 个像素。

在这里插入图片描述

尺寸一致的情况下,像素点越多,图像越精致,像素点越少,图像越粗糙。

三、上采样与下采样

1、上采样(upsampling)

放大图像(或称为图像插值(interpolating))的主要目的是放大原图像,从而可以显示在更高分辨率的显示设备上。

原理:内插值

2、下采样(subsampled)

缩小图像(或称为降采样(downsampled))的主要目的有两个:1、使得图像符合显示区域的大小。2、生成对应图像的缩略图。

原理:(M/s)*(N/s)

3、插值方法

本质是求一个不存在的像素点的像素值,不一定是加或者减。

1、最邻近插值

(i,j),(i+1,j),(i,j+1),(i+1,j+1) 为原始图像上的四个像素点,如果想在这四个像素点中间插入一个像素点,插入的像素点落在 A,B,C,D 哪个区域内,那么,插入的像素点的像素值就等同于离得最近的那个像素点的像素值。

在这里插入图片描述

import cv2  # 导入 OpenCV 库,用于图像处理
import numpy as np  # 导入 NumPy 库,用于数组操作

def function(img):
    # 获取图像的高度、宽度和通道数
    height, width, channels = img.shape
    # 创建一个空白图像,尺寸为(800, 800),通道数与原图一致
    emptyImage = np.zeros((800, 800, channels), np.uint8)

    # 计算高度的缩放比例
    sh = 800 / height
    # 计算宽度的缩放比例
    sw = 800 / width

    # 遍历图片的每一个像素
    for i in range(800):
        for j in range(800):
            # 计算新图像中的横坐标 x,使用最近邻插值,int() 转为整型,+0.5 是为了实现向最近的整数四舍五入
            x = int(i / sh + 0.5)
            # 计算新图像中的纵坐标 y,使用最近邻插值,int() 转为整型,+0.5 是为了实现向最近的整数四舍五入
            y = int(j / sw + 0.5)
            # 对新图像进行像素赋值
            emptyImage[i, j] = img[x, y]

    # 返回缩放后的图像
    return emptyImage

# 读取图像
img = cv2.imread("img/lenna.png")
# 调用缩放函数进行图像缩放
zoom = function(img)
# 打印缩放后的图像数组
print(zoom)
# [[[125 137 226]
#   [125 137 226]
#   [125 137 226]
#   ...
#   [110 130 221]
#   [ 90  99 200]
#   [ 90  99 200]]
#
#  [[125 137 226]
#   [125 137 226]
#   [125 137 226]
#   ...
#   [110 130 221]
#   [ 90  99 200]
#   [ 90  99 200]]
#
#  [[125 137 226]
#   [125 137 226]
#   [125 137 226]
#   ...
#   [110 130 221]
#   [ 90  99 200]
#   [ 90  99 200]]
#
#  ...
#
#  [[ 57  22  82]
#   [ 57  22  82]
#   [ 57  22  82]
#   ...
#   [ 81  71 181]
#   [ 81  74 185]
#   [ 81  74 185]]
#
#  [[ 57  22  82]
#   [ 57  22  82]
#   [ 57  22  82]
#   ...
#   [ 81  71 181]
#   [ 81  74 185]
#   [ 81  74 185]]
#
#  [[ 57  22  82]
#   [ 57  22  82]
#   [ 57  22  82]
#   ...
#   [ 81  71 181]
#   [ 81  74 185]
#   [ 81  74 185]]]

# 打印缩放后的图像形状
print(zoom.shape)
# (800, 800, 3)
# 在窗口中显示缩放后的图像(最近邻插值)
cv2.imshow("nearest interp", zoom)
# 在窗口中显示原始图像
cv2.imshow("image", img)
# 等待按键事件
cv2.waitKey(0)

优点:简单粗暴易用。

缺点:精度不高。

对精度要求不高时可以使用。

2、 双线性插值

f(i+u, j+v) = (1-u) * (1-v) * f(i, j) + (1-u) * v * f(i, j+1) + u * (1-v) * f(i+1, j) + u * v * f(i+1, j+1)

在这里插入图片描述

  • 在两点之间插值

在这里插入图片描述

  • 在四点之间插值(双线性插值)

在这里插入图片描述

由于图像双线性插值只会用相邻的4个点,因此上述公式的分母都是1。

  • 存在的问题

要通过双线性插值的方法算出目标图像(dst)中的每一个像素点的像素值,是通过目标图像(dst)像素点的坐标对应到源图像(src)图像当中的坐标,然后通过双线性插值的方法算出源图像(src)中相应坐标的像素值。

按比例对应

SrcX =(dstX)*(srcWidth/dstWidth)

SrcY =(dstY)*(srcHeight/dstHeight)

如果源图像和目标图像的原点(0,0)均选择左上角,然后根据插值公式计算目标图像每点像素。

假设需要将一幅 5 * 5 的图像缩小成 3 * 3 的图像,那么源图像和目标图像各个像素点之间的对应关系如下:

在这里插入图片描述

这样会使右下角的图片信息缺失,所以,最好的方法就是,两个图像的几何中心重合,并且目标图像的每个像素之间都是等间隔的,都和两边有一定的边距。

SrcX + 0.5 =(dstX + 0.5)*(srcWidth/dstWidth)

SrcY + 0.5 =(dstY + 0.5)*(srcHeight/dstHeight)

在这里插入图片描述

证明几何中心对称重合为什么要加0.5

源图像有 M × M 个像素点,目标图像有 N × N 个像素点 目标图像在源图像坐标系位置为 ( x , y ) 源图像坐标为 ( x m , y m ) m = 0 , 1 , . . . , M − 1 几何中心点为 ( x M − 1 2 , y M − 1 2 ) 目标图像坐标为 ( x n , y n ) n = 0 , 1 , . . . , N − 1 几何中心点为 ( x N − 1 2 , y N − 1 2 ) x = n M N ⇒ 使几何中心相同 M − 1 2 + z = ( N − 1 2 + z ) M N z = 1 2 源图像有 M × M 个像素点,目标图像有 N × N 个像素点\\ 目标图像在源图像坐标系位置为(x,y)\\ 源图像坐标为(x_m,y_m)\quad m=0,1,...,M-1\quad几何中心点为(x_\frac{M-1}{2},y_\frac{M-1}{2})\\ 目标图像坐标为(x_n,y_n)\quad n=0,1,...,N-1\quad几何中心点为(x_\frac{N-1}{2},y_\frac{N-1}{2})\\ x=n\frac{M}{N} \Rightarrow 使几何中心相同\\ \frac{M-1}{2}+z=(\frac{N-1}{2}+z)\frac{M}{N}\\ z=\frac{1}{2} 源图像有M×M个像素点,目标图像有N×N个像素点目标图像在源图像坐标系位置为(x,y)源图像坐标为(xm,ym)m=0,1,...,M1几何中心点为(x2M1,y2M1)目标图像坐标为(xn,yn)n=0,1,...,N1几何中心点为(x2N1,y2N1)x=nNM使几何中心相同2M1+z=(2N1+z)NMz=21

import cv2  # 导入 OpenCV 库,用于图像处理
import numpy as np  # 导入 NumPy 库,用于数组操作

def bilinear_interpolation(img, out_dim):
    # 获取输入图像的高度、宽度和通道数
    src_h, src_w, channel = img.shape
    # 获取输出图像的高度和宽度
    dst_h, dst_w = out_dim[1], out_dim[0]
    # 打印输入图像的高度、宽度
    print("src_h, src_w = ", src_h, src_w)
    # src_h, src_w =  512 512
    # 打印输出图像的高度、宽度
    print("dst_h, dst_w = ", dst_h, dst_w)
    # dst_h, dst_w =  700 700

    # 如果输入图像和输出图像尺寸相同
    if src_h == dst_h and src_w == dst_w:
        # 则直接返回输入图像的副本
        return img.copy()

    # 创建一个空白的输出图像,尺寸为(dst_h, dst_w, 3)
    dst_img = np.zeros((dst_h, dst_w, 3), dtype=np.uint8)

    # 计算在水平和垂直方向上的缩放比例
    scale_x, scale_y = float(src_w) / dst_w, float(src_h) / dst_h

    # 对输出图像的每个通道进行插值
    for i in range(3):
        # 对输出图像的每个像素进行插值
        for dst_y in range(dst_h):
            for dst_x in range(dst_w):
                # 计算在输入图像中的对应位置
                src_x = (dst_x + 0.5) * scale_x - 0.5
                src_y = (dst_y + 0.5) * scale_y - 0.5

                # 计算在输入图像中的四个相邻像素的坐标
                src_x0 = int(np.floor(src_x))
                src_x1 = min(src_x0 + 1, src_w - 1)
                src_y0 = int(np.floor(src_y))
                src_y1 = min(src_y0 + 1, src_h - 1)

                # 使用双线性插值计算输出图像中当前像素的值
                temp0 = (src_x1 - src_x) * img[src_y0, src_x0, i] + (src_x - src_x0) * img[src_y0, src_x1, i]
                temp1 = (src_x1 - src_x) * img[src_y1, src_x0, i] + (src_x - src_x0) * img[src_y1, src_x1, i]
                dst_img[dst_y, dst_x, i] = int((src_y1 - src_y) * temp0 + (src_y - src_y0) * temp1)

    # 返回双线性插值后的输出图像
    return dst_img


if __name__ == '__main__':
    # 读取图像文件
    img = cv2.imread('img/lenna.png')
    # 对图像进行双线性插值,将尺寸调整为(700, 700)
    dst = bilinear_interpolation(img, (700, 700))
    # 显示双线性插值结果
    cv2.imshow('bilinear interp', dst)
    # 等待用户按任意键,如果没有这一句,窗口会立即关闭
    cv2.waitKey()

优点:精度高,图像看起来更光滑。

缺点:计算复杂,计算量较大。

四、直方图

1、概念

直方图是图像处理中一种用于描述图像像素值分布的工具。主要用于分析图像的亮度、对比度和颜色分布。包括颜色直方图、灰度直方图等。

灰度直方图

图像的灰度直方图就描述了图像中灰度分布情况,能够很直观的展示出图像中各个灰度级所占的多少。

图像的灰度直方图是灰度级的函数,描述的是图像中具有该灰度级的像素的个数。其中,横坐标是灰度级,纵坐标是该灰度级出现的频率。

2、性质

  • 直方图反映了图像中的灰度分布规律。它描述每个灰度级具有的像素个数,但不包含这些像素在图像中的位置信息。
  • 图像直方图不关心像素所处的空间位置,因此,不受图像旋转和平移变化的影响,可以作为图像的特征。
  • 任何一幅特定的图像都有唯一的直方图与之对应,但不同的图像可以有相同的直方图。
  • 如果一幅图像有两个不相连的区域组成,并且每个区域的直方图已知,则整幅图像的直方图是该两个区域的直方图之和。

3、应用

在这里插入图片描述

灰度图像的直方图

方法一:使用 Matplotlib 绘制直方图

import cv2  # 导入 OpenCV 库,用于图像处理
from matplotlib import pyplot as plt  # 从 Matplotlib 库导入绘图模块 plt

# 读取彩色图像
img = cv2.imread("img/lenna.png", 1)
# 将彩色图像转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 显示灰度图像
# cv2.imshow("image_gray", gray)

# 创建一个新的图像
plt.figure()
# 绘制直方图
plt.hist(gray.ravel(), 256)
# 显示直方图
plt.show()

在这里插入图片描述

方法二:使用 OpenCV 的 calcHist 函数绘制直方图

import cv2  # 导入 OpenCV 库,用于图像处理
from matplotlib import pyplot as plt  # 从 Matplotlib 库导入绘图模块 plt

'''
calcHist—计算图像直方图
函数原型:calcHist(images, channels, mask, histSize, ranges, hist=None, accumulate=None)
images:图像矩阵,例如:[image]
channels:通道数,例如:0
mask:掩膜,一般为:None
histSize:直方图大小,一般等于灰度级数
ranges:横轴范围
hist: 输出的直方图,默认为 None,创建一个空的直方图。如果传入一个已存在的直方图,会在已有的直方图上累积结果
accumulate: 默认为 None。如果设置为 True,函数会累积直方图的值。在处理多幅图像时,可以累积它们的直方图
'''

# 读取彩色图像
img = cv2.imread("img/lenna.png", 1)
# 将彩色图像转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 显示灰度图像
# cv2.imshow("image_gray", gray)

# 计算灰度图像的直方图
hist = cv2.calcHist([gray],[0],None,[256],[0,256])
# 创建一个新的图像
plt.figure()
# 设置图像标题
plt.title("Grayscale Histogram")
# 设置x轴标签
plt.xlabel("Bins")
# 设置y轴标签
plt.ylabel("# of Pixels")
# 绘制直方图
plt.plot(hist)
# 设置x轴范围
plt.xlim([0,256])
# 显示直方图
plt.show()

在这里插入图片描述

彩色图像的直方图

import cv2  # 导入 OpenCV 库,用于图像处理
from matplotlib import pyplot as plt  # 从 Matplotlib 库导入绘图模块 plt

# 读取彩色图像
image = cv2.imread("img/lenna.png")
# 显示原始彩色图像
cv2.imshow("Original",image)
# 等待用户按键
cv2.waitKey(0)

# 分离彩色图像的通道
chans = cv2.split(image)
colors = ("b","g","r")

# 创建一个新的图像
plt.figure()
# 设置图像标题
plt.title("Flattened Color Histogram")
# 设置x轴标签
plt.xlabel("Bins")
# 设置y轴标签
plt.ylabel("# of Pixels")

# 遍历彩色通道和对应颜色
for (chan,color) in zip(chans,colors):
    # 计算彩色通道的直方图
    hist = cv2.calcHist([chan],[0],None,[256],[0,256])
    # 绘制直方图
    plt.plot(hist,color = color)
    # 设置x轴范围
    plt.xlim([0,256])
# 显示直方图
plt.show()

在这里插入图片描述

4、均衡化

1、概念

直方图均衡化是将原图像的直方图通过变换函数变为均匀的直方图,然后按均匀直方图修改原图像,从而获得一幅灰度分布均匀的新图像。

直方图均衡化就是用一定的算法使直方图大致平和的方法。

直方图均衡化的作用图像增强

在这里插入图片描述

2、方法

为了将原图像的亮度范围进行扩展,需要一个映射函数,将原图像的像素值均衡映射到新直方图中,这个映射函数有两个条件:

1、为了不打乱原有的顺序,映射后亮、暗的大小关系不能改变。

2、映射后必须在原有的范围内,比如(0-255)。

步骤

1、依次扫描原始灰度图像的每一个像素,计算出图像的灰度直方图 H。

2、计算灰度直方图的累加直方图。

3、根据累加直方图和直方图均衡化原理得到输入与输出之间的映射关系。

4、最后根据映射关系得到结果:dst(x,y) = H’(src(x,y)) 进行图像变换。

3、公式

1、对于输入图像的任意一个像素 p, p∈[0,255], 总能在输出图像里有对应的像素 q, q∈[0,255] 使得下面等式成立(输入和输出的像素总量相等):

累加直方图公式 ∑ k = 0 p h i s t i n p u t ( k ) = ∑ k = 0 q h i s t i o u t ( k ) 累加直方图公式\\\sum_{k=0}^{p} hist_{input}(k) = \sum_{k=0}^{q} hist_{iout}(k) 累加直方图公式k=0phistinput(k)=k=0qhistiout(k)
2、其中,输出图像每个灰度级的个数:

h i s t i o u t ( k ) ≈ H × W 256 , k ∈ [ 0 , 255 ] hist_{iout}(k)\approx \frac{H \times W}{256} ,k \in [0,255] histiout(k)256H×W,k[0,255]
3、代入累加直方图公式:
∑ k = 0 p h i s t i n p u t ( k ) ≈ ( q + 1 ) H × W 256 ⇒ q ≈ ∑ k = 0 p h i s t i n p u t ( k ) H × W × 256 − 1 \sum_{k=0}^{p} hist_{input}(k)\approx (q+1)\frac{H \times W}{256}\Rightarrow q\approx\sum_{k=0}^{p}\frac{hist_{input}(k)}{H \times W}\times256-1 k=0phistinput(k)(q+1)256H×Wqk=0pH×Whistinput(k)×2561

在这里插入图片描述

灰度图像直方图均衡化

import cv2  # 导入 OpenCV 库,用于图像处理
import numpy as np  # 导入 NumPy 库,用于数组操作
from matplotlib import pyplot as plt  # 从 Matplotlib 库导入绘图模块 plt

'''
equalizeHist—直方图均衡化
函数原型: equalizeHist(src, dst=None)
src:图像矩阵(单通道图像)
dst:默认即可
'''

# 读取彩色图像
img = cv2.imread("img/lenna.png", 1)
# 将彩色图像转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 显示灰度图像
# cv2.imshow("image_gray", gray)

# 对灰度图像进行直方图均衡化
dst = cv2.equalizeHist(gray)

# 计算均衡化后图像的直方图
hist = cv2.calcHist([dst], [0], None, [256], [0, 256])

# 使用 Matplotlib 绘制均衡化后图像的直方图
plt.figure()
# 绘制直方图,ravel()将图像矩阵展平为一维数组
plt.hist(dst.ravel(), 256)
# 显示直方图
plt.show()

# 将原始灰度图像和均衡化后的图像水平拼接
combined_img = np.hstack([gray, dst])
# 显示均衡化后的图像和直方图拼接图像
cv2.imshow("Histogram Equalization", combined_img)
# 等待用户按键
cv2.waitKey(0)

在这里插入图片描述

在这里插入图片描述

彩色图像直方图均衡化

import cv2  # 导入 OpenCV 库,用于图像处理

'''
equalizeHist—直方图均衡化
函数原型: equalizeHist(src, dst=None)
src:图像矩阵(单通道图像)
dst:默认即可
'''

# 读取彩色图像
img = cv2.imread("img/lenna.png", 1)
# 显示原始彩色图像
cv2.imshow("src", img)

# 分离彩色图像的通道(蓝、绿、红)
(b, g, r) = cv2.split(img)
# 对每个通道进行直方图均衡化
bH = cv2.equalizeHist(b)
gH = cv2.equalizeHist(g)
rH = cv2.equalizeHist(r)

# 合并均衡化后的通道,得到最终的彩色图像
result = cv2.merge((bH, gH, rH))
# 显示均衡化后的彩色图像
cv2.imshow("dst_rgb", result)

# 等待用户按键
cv2.waitKey(0)

在这里插入图片描述

五、滤波和卷积

1、概念

滤波

滤波是一种信号处理技术,线性滤波可以说是图像处理最基本的方法,其目的是通过一定的操作来改变信号的频率特性或者减弱信号中的某些成分。

在图像处理中,滤波通常用于去除图像中的噪声、平滑图像、增强图像特定的特征等。滤波操作可以通过一系列数学运算来实现。

卷积

卷积是一种数学运算,常被用于信号处理和图像处理中。
( f ∗ g ) ( t ) = ∫ R f ( x ) g ( t − x )   d x (f * g)(t) = \int_{R} f(x)g(t-x) \, dx (fg)(t)=Rf(x)g(tx)dx

一般称 g 为作用在 f 上的 filter 或 kernel

在这里插入图片描述

2、滤波器/过滤器/卷积核/Kernel

对于滤波器规则要求:

  • 滤波器的大小应该是奇数,这样它才有中心和半径,例如 3x3,5x5 或者 7x7。5x5 大小的核的半径就是2。
  • 滤波器矩阵所有的元素之和应该要等于1,这是为了保证滤波前后图像的亮度保持不变。但这不是硬性要求。
  • 如果滤波器矩阵所有元素之和大于1,那么滤波后的图像就会比原图像更亮,反之,如果小于1,那么得到的图像就会变暗。如果和为0,图像不会变黑,但也会非常暗。
  • 对于滤波后的结构,可能会出现负数或者大于255的数值。对这种情况,将它们直接截断到0和255之间即可。对于负数,也可以取绝对值。

在具体应用中,往往有多个卷积核,可以认为,每个卷积核代表了一种图像模式。

如果某个图像块与此卷积核卷积出的值大,则认为此图像块十分接近于此卷积核。

例如,如果设计了6个卷积核,可以理解为:我们认为这个图像上有6种底层纹理模式,也就是我们用6种基础模式就能描绘出一副图像。

下图为一张黑白相间的图片

在这里插入图片描述

有 Gx 和 Gy 两个卷积核

G x = 1 0 − 1 1 0 − 1 1 0 − 1 G y = 1 1 1 0 0 0 − 1 − 1 − 1 G_x = \begin{array}{|c|c|c|} \hline 1 & 0 & -1 \\ \hline 1 & 0 & -1 \\ \hline 1 & 0 & -1 \\ \hline \end{array}\quad G_y = \begin{array}{|c|c|c|} \hline 1 & 1 & 1 \\ \hline 0 & 0 & 0 \\ \hline -1 & -1 & -1 \\ \hline \end{array} Gx=111000111Gy=101101101
用 Gx 来卷积这张图的话,就会在纵向中间黑白边界处获得比较大的值,可以找到纵向的边界位置。

用 Gy 来卷积这张图的话,就会在横向中间黑白边界处获得比较大的值,可以找到横向的边界位置。

应用

1、一个没有任何效果的卷积
0 0 0 0 1 0 0 0 0 \begin{array}{|c|c|c|} \hline 0 & 0 & 0 \\ \hline 0 & 1 & 0 \\ \hline 0 & 0 & 0 \\ \hline \end{array} 000010000

将原像素中间像素值乘1,其余全部乘0。 显然像素值不会发生任何变化。

2、平滑均值滤波
1 9 1 9 1 9 1 9 1 9 1 9 1 9 1 9 1 9 \begin{array}{|c|c|c|} \hline \frac{1}{9} & \frac{1}{9} & \frac{1}{9} \\ \hline \frac{1}{9} & \frac{1}{9} & \frac{1}{9} \\ \hline \frac{1}{9} & \frac{1}{9} & \frac{1}{9} \\ \hline \end{array} 919191919191919191

取九个值的平均值代替中间像素值。起到平滑的效果。

3、高斯平滑
1 16 2 16 1 16 2 16 2 16 2 16 1 16 2 16 1 16 \begin{array}{|c|c|c|} \hline \frac{1}{16} & \frac{2}{16} & \frac{1}{16} \\ \hline \frac{2}{16} & \frac{2}{16} & \frac{2}{16} \\ \hline \frac{1}{16} & \frac{2}{16} & \frac{1}{16} \\ \hline \end{array} 161162161162162162161162161

高斯平滑水平和垂直方向呈现高斯分布,更突出了中心点在像素平滑后的权重,相比于均值滤波而言, 有着更好的平滑效果。

4、图像锐化
− 1 − 1 − 1 − 1 9 − 1 − 1 − 1 − 1 \begin{array}{|c|c|c|} \hline -1 & -1 & -1 \\ \hline -1 & 9 & -1 \\ \hline -1 & -1 & -1 \\ \hline \end{array} 111191111

0 − 1 0 − 1 5 − 1 0 − 1 0 \begin{array}{|c|c|c|} \hline 0 & -1 & 0 \\ \hline -1 & 5 & -1 \\ \hline 0 & -1 & 0 \\ \hline \end{array} 010151010

图像锐化使用的是拉普拉斯变换核函数。

5、Soble 边缘检测
水平梯度卷积核 − 1 0 1 − 2 0 2 − 1 0 1 水平梯度卷积核\\ \begin{array}{|c|c|c|} \hline -1 & 0 & 1 \\ \hline -2 & 0 & 2 \\ \hline -1 & 0 & 1 \\ \hline \end{array} 水平梯度卷积核121000121

垂直梯度卷积核 − 1 − 2 − 1 0 0 0 1 2 1 垂直梯度卷积核\\ \begin{array}{|c|c|c|} \hline -1 & -2 & -1 \\ \hline 0 & 0 & 0 \\ \hline 1 & 2 & 1 \\ \hline \end{array} 垂直梯度卷积核101202101

Soble 更强调了和边缘相邻的像素点对边缘的影响。

在这里插入图片描述

3、卷积

卷积负责提取图像中的局部特征

1、步长/Stride

如果用 (f, f) 的过滤器来卷积一张 (h, w) 大小的图片,每次移动一个像素的话,那么得出的结果就是 (h-f+1, w-f+1) 的输出结果。

f 是过滤器大小,h 和 w 分别是图片的高宽。 如果每次不止移动一个像素,而是 s 个像素,那么结果就会变为:
( h − f s + 1 , w − f s + 1 ) (\frac{h-f}{s}+1,\frac{w-f}{s}+1) (shf+1,swf+1)
这个 s 就叫做步长。

存在的问题:

只要是 f 或 s 的值比1要大的话,那么每次卷积之后结果的长宽,要比卷积前小一些。

丢失信息。

2、填充/Pading

在这里插入图片描述

有了填充之后,每次卷积之后的图像大小:

( h − f s + 1 , w − f s + 1 ) ⇒ ( h − f + 2 p s + 1 , w − f + 2 p s + 1 ) (\frac{h-f}{s}+1,\frac{w-f}{s}+1) \Rightarrow (\frac{h-f+2p}{s}+1,\frac{w-f+2p}{s}+1) (shf+1,swf+1)(shf+2p+1,swf+2p+1)
此时如果想让高(宽)不变:

h − f + 2 p s + 1 = h ⇒ p = s ( h − 1 ) − h + f 2 \frac{h-f+2p}{s}+1=h \Rightarrow p=\frac{s(h-1)-h+f}{2} shf+2p+1=hp=2s(h1)h+f
假设步长 s=1:

p = f − 1 2 p=\frac{f-1}{2} p=2f1

填充模式

橙色部分为 image, 蓝色部分为 filter。

  • full 模式

在这里插入图片描述

从 filter 和 image 刚相交开始做卷积。

  • same 模式

在这里插入图片描述

filter 中心 (K) 与 image 的边角重合时, 开始做卷积。

  • valid 模式

在这里插入图片描述

filter 全部在 image 里面时,进行卷积。

注意:

这里的 same 还有一个意思,卷积之后输出的 feature map 尺寸保持不变(相对于输入图片)。

当然,same 模式不代表完全输入输出尺寸一样,也跟卷积核的步长有关系。

same 模式也是最常见的模式,因为这种模式可以在卷积过程中让图的大小保持不变。

3、多通道卷积

在这里插入图片描述

  • 卷积核数量(W0、W1,一共2个卷积核)= 输出通道数

  • 每个卷积核代表一种特征,想要几种特征就设置几个卷积核

  • 每个卷积核的通道数(W0 和 W1 都分别有3个通道)= 输入通道数(已知)

  • Bias:偏置(可以理解为加快收敛速度的一个参数),y=kx+b(bias)

记录学习过程,欢迎讨论交流,尊重原创,转载请注明出处~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

测试计划、测试方案、测试策略、测试用例的区别

一 测试计划 测试计划是指描述了要进行的测试活动的范围、方法、资源和进度的文档。它主要包括测试项、被测特性、各阶段的测试任务、时间进度安排&#xff0c;谁执行任务和风险控制等&#xff0c;可以包括测试策略。 二 测试方案 测试方案是指描述需要测试的特性、测试的方…

jvm-jvm七款经典垃圾收集器

一&#xff0c;七款经典垃圾收集器 七款经典的垃圾收集器&#xff1a; 收集器串行、并行or并发新生代/老年代算法目标适用场景Serial串行新生代复制算法响应速度优先单CPU环境下的Client模式Serial Old串行老年代标记-整理响应速度优先单CPU环境下的Client模式、CMS的后备预案…

基于Java SSM框架实现艺诚美业管理系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现艺诚美业管理系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&a…

SpringBoot实现缓存预热的几种常用方案

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&…

visual studio2019怎么修改字体

点击菜单栏中的“工具” “选项” 环境 -> 字体和颜色 Consolas是vscode的默认字体&#xff0c;还挺好用的。

【自然语言处理-二-attention注意力 是什么】

自然语言处理二-attention 注意力机制 自然语言处理二-attention 注意力记忆能力回顾下RNN&#xff08;也包括LSTM GRU&#xff09;解决memory问题改进后基于attention注意力的modelmatch操作softmax操作softmax值与hidder layer的值做weight sum 计算和将计算出来的和作为memo…

linux服务器tomcat日志中文出现问号乱码

目录 一、场景二、排查三、原因四、解决 一、场景 tomcat日志的中文出现问号乱码 乱码示例 ??[377995738417729536]????????? ac??????????????message:二、排查 1、使用locale命令查看服务器当前使用的语言包 发现只用的语言包为utf-8&#xff0…

CSDN付费阅读需要哪些条件?

文章目录 前言一、博客等级要求二、原力等级要求结束语 前言 hello&#xff0c;大家好&#xff01;我是BoBo仔吖&#xff0c;祝大家元宵节快乐呀~ csdn是一个善良的网站&#xff0c;它提供了各种功能空间&#xff0c;比如InsCode运行代码&#xff0c;还有鼓舞大家努力发文的勋…

iOS调用系统已安装地图及内置地图实现

info.plist要添加scheme: 1.地图列表: NSArray *mapKeys=[[NSArray alloc] initWithObjects:@"com.autonavi.minimap",@"com.baidu.BaiduMap",@"com.google.android.apps.maps",@"com.tencent.map", nil]; NSArray *mapSchemes=[[NS…

为什么前端开发变得越来越复杂了?这可能是我们的错

前端训练营&#xff1a;1v1私教&#xff0c;终身辅导计划&#xff0c;帮你拿到满意的 offer。 已帮助数百位同学拿到了中大厂 offer。欢迎来撩~~~~~~~~ Hello&#xff0c;大家好&#xff0c;我是 Sunday。 最近有很多同学来问我&#xff1a;“Sunday 老师&#xff0c;前端学起…

编译GreatSQL with RocksDB引擎

GreatSQL里也能用上RocksDB引擎 1. 前言 RocksDB 是基于Facebook 开源的一种支持事务的、高度可压缩、高性能的MyRocks存储引擎&#xff0c;特别适用于高度压缩和大容量的数据。以下是一些关键特点&#xff1a; 高性能&#xff1a; LSM 树结构使得RocksDB在写入密集型负载下表现…

提示工程(Prompt Engineering)、微调(Fine-tuning) 和 嵌入(Embedding)

主要参考资料&#xff1a; 还没搞懂嵌入&#xff08;Embedding&#xff09;、微调&#xff08;Fine-tuning&#xff09;和提示工程&#xff08;Prompt Engineering&#xff09;&#xff1f;: https://blog.csdn.net/DynmicResource/article/details/133638079 B站Up主Nenly同学…

达梦数据库搭建和连接(详解一文看懂)

达梦数据库搭建和连接 一、数据库搭建1.安装前准备2.下载 Docker3.导入安装包4.启动docker版docker-compose版 5.启动/停止数据库 二、数据库连接1、下载DBeaver2、下载驱动3、DBeaver新建驱动数据库-驱动管理器新建驱动创建新驱动设置创建驱动-库驱动类确定和关闭 4、连接 博主…

Stable Diffusion 绘画入门教程(webui)-ControlNet(IP2P)

上篇文章介绍了深度Depth&#xff0c;这篇文章介绍下IP2P&#xff08;InstructP2P&#xff09;, 通俗理解就是图生图&#xff0c;给原有图加一些效果,比如下图&#xff0c;左边为原图&#xff0c;右边为增加了效果的图&#xff1a; 文章目录 一、选大模型二、写提示词三、基础参…

真Unity3D编辑器Editor二次开发

IMGUI Editor Label 改变颜色 分享一个很神奇的颜色 一开始这么写&#xff0c;以为不行的&#xff0c; private void OnGUI()(){GUILayout.Label("<colorred>name:</color>ffdasilufoi");//。。。。 } 结果这么写又好了&#xff0c; private GUIStyle m…

分布式事务Seata的使用详解

一、事务概述 事务指的就是一个操作单元&#xff0c;在这个操作单元中的所有操作最终要保持一致的行为&#xff0c;要么所有操作 都成功&#xff0c;要么所有的操作都被撤销。简单地说&#xff0c;事务提供一种“要么什么都不做&#xff0c;要么做全套”机制。 1.1.本地事物 …

Stable Diffusion 3 发布,AI生图效果,再次到达全新里程碑!

AI生图效果&#xff0c;再次到达全新里程碑&#xff01; Prompt&#xff1a;Epic anime artwork of a wizard atop a mountain at night casting a cosmic spell into the dark sky that says "Stable Diffusion 3" made out of colorful energy 提示&#xff08;意译…

【LLM入门实践】简便快捷获取Hugging Face模型

前言 好久没有更新博客了&#xff0c;由于AI 大模型技术经过2023年的狂飙&#xff0c;2024年迎来大量的应用的落地&#xff0c;作为一个技术人&#xff0c;我也对此有了浓厚的兴趣&#xff0c;买了很多本书&#xff0c;然后试图找到一个学习大模型的速成模式&#xff0c;遗憾的…

【开源】SpringBoot框架开发婚恋交友网站

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 会员管理模块2.3 新闻管理模块2.4 相亲大会管理模块2.5 留言管理模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 会员信息表3.2.2 新闻表3.2.3 相亲大会表3.2.4 留言表 四、系统展示五、核心代码5.…

【k8s资源调度-StatefulSet】

1、部署对象StatefulSet资源&#xff08;无状态应用&#xff09; StatefulSet针对的是有状态应用&#xff0c;有状态应用会对我们的当前pod的网络、文件系统等有关联。 2、配置文件如下 StatefulSet资源的配置文件粗略如下&#xff0c;如下的配置信息包含了数据卷&#xff0c;…