O3.1 opencv高阶

一·扩展代码

逻辑

1. match.py
  • 功能 :实现模板匹配功能,且支持对旋转后的模板进行匹配。
    • 读取原始图像(image.jpg)和模板图像(tem.jpg),将原始图像转为灰度图。
    • 对模板进行两种旋转处理(顺时针 90 度、逆时针 90 度)。
    • 定义findtemp函数:使用cv2.matchTemplate进行模板匹配,通过阈值(0.9)筛选匹配区域,并在原始图像上用红色矩形标记匹配结果。
    • 分别对原始模板、两种旋转后的模板执行匹配,最终显示标记后的图像。
2. 旋转.py
  • 功能 :展示图像旋转的两种实现方法,用于生成旋转后的图像(可作为模板匹配的辅助工具)。
    • 方法一 :使用np.rot90旋转图像,k=-1表示顺时针 90 度,k=1表示逆时针 90 度。
    • 方法二 :使用cv2.rotate旋转图像,支持顺时针 90 度、逆时针 90 度、180 度三种旋转方式。
    • 显示原始图像和旋转后的图像,便于直观查看旋转效果。
3. 模板与多个对象匹配.py
  • 功能 :基础的多对象模板匹配实现,展示模板匹配的核心逻辑。
    • 读取原始图像和模板图像,将原始图像转为灰度图。
    • 使用cv2.matchTemplate进行匹配,通过阈值筛选匹配区域,并用红色矩形标记所有符合条件的区域。
    • 包含辅助代码:解释np.where的返回值格式(用于获取匹配点坐标)和zip函数的打包 / 解包用法(用于处理坐标数据)。

三个文件的搭配关系

  1. 技术互补

    • 旋转.py提供图像旋转的基础方法(两种旋转函数),为match.py中 "对模板进行旋转后再匹配" 的功能提供了技术支持(match.py中正是使用了np.rot90实现模板旋转)。
    • 模板与多个对象匹配.py展示了模板匹配的核心流程(读取图像、匹配、筛选、标记),而match.py在此基础上扩展了 "旋转模板匹配" 的功能,是对基础模板匹配的进阶应用。
  2. 功能递进

    • 先通过旋转.py掌握图像旋转的方法,再通过模板与多个对象匹配.py理解基础模板匹配逻辑,最后结合两者,通过match.py实现更复杂的 "多方向模板匹配"(应对模板可能旋转的场景)。
  3. 代码复用

    • match.py中的模板匹配逻辑(如cv2.matchTemplate、阈值筛选、矩形标记)与模板与多个对象匹配.py的核心代码高度相似,前者复用了后者的基础框架。
    • match.py中的旋转逻辑(np.rot90)直接来自旋转.py的 "方法一",体现了代码功能的模块化复用。

综上,三个文件围绕 "图像旋转" 和 "模板匹配" 两个核心技术,从基础方法(旋转.py模板与多个对象匹配.py)到综合应用(match.py),形成了递进式的功能搭配。

把我的样本图和第二张图进行比对找到和样本图相对的地方,进行采样

代码

match

python 复制代码
import cv2
import numpy as np

img_rgb = cv2.imread("image.jpg")
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread("tem.jpg", 0)
h,w = template.shape[:2]
# 旋转模板图像
# 旋转 90 度,k=-1 表示顺时针旋转 90 度
rotated_image1 = np.rot90(template, k=-1)
# 旋转 90 度,k=1 表示逆时针旋转 90 度
rotated_image2 = np.rot90(template, k=1)
def findtemp(temp):
    # 使用模板匹配方法 cv2.matchTemplate 进行模板匹配
    res = cv2.matchTemplate(img_gray, temp, cv2.TM_CCOEFF_NORMED)
    # 设定匹配阈值
    threshold = 0.9
    # 获取匹配结果中所有符合阈值的点的坐标
    loc = np.where(res >= threshold)
    # 遍历所有匹配点
    for pt in zip(*loc[::-1]):
        # 在原图上绘制匹配区域的矩形框
        cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 1)
# 对原始模板和旋转后的模板进行匹配
findtemp(template)
findtemp(rotated_image1)
findtemp(rotated_image2)
# 显示最终结果图像
cv2.imshow('res.png', img_rgb)
cv2.waitKey(0)

模板与多个对象匹配

python 复制代码
import cv2
import numpy as np
# 读取原始图像和模板图像
img_rgb = cv2.imread('image.jpg')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread('tem.jpg', 0)
h, w = template.shape[:2]
# 使用模板匹配方法 cv2.matchTemplate 进行模板匹配
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
# 设定匹配阈值
threshold = 0.9
# 获取匹配结果中所有符合阈值的点的坐标
loc = np.where(res >= threshold)  # (符合条件的行,符合条件的列)
# 遍历所有匹配点
for pt in zip(*loc[::-1]):
    # 在原图上绘制匹配区域的矩形框
    cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 1)
cv2.imshow('', img_rgb)
cv2.waitKey(0)



# a = np.array([2,4,6,8,10])
# # b= np.array([[2,4,6,8,10]])
# # c= np.array([[2,4,6,8,10],[6,5,6,5,4]])
# d=np.where(a > 5)
# print(d)

# zip(*iterables) 将多个可迭代对象(如列表、元组等)进行"解压"操作
# a = [1, 2, 3]
# b = [4, 5, 6]
# # 使用 zip 将它们按位置进行配对
# zipped = zip(a, b)
# print(list(zipped))  # 输出:[(1, 4), (2, 5), (3, 6)]
# #
# # # 假设我们已经有了一个打包好的 zip 对象
# zipped = zip(a, b)
# # 使用 * 运算符解包,得到转置的结果
# unzipped = zip(*zipped)
# print(list(unzipped))  # 输出:[(1, 2, 3), (4, 5, 6)]

旋转

python 复制代码
import cv2
import numpy as np
# 方法一
img = cv2.imread('../kele.png')
# 旋转 90 度,k=-1 表示顺时针旋转 90 度
rotated_image1 = np.rot90(img, k=-1)
# 旋转 90 度,k=1 表示逆时针旋转 90 度
rotated_image2 = np.rot90(img, k=1)

cv2.imshow('yuantu', img)
cv2.imshow('rotated_image1', rotated_image1)
cv2.imshow('rotated_image2', rotated_image2)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 方法二
rotated_image = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)  # 顺时针90度
rotated_image1 = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)  # 逆时针90度
rotated_image2 = cv2.rotate(img, cv2.ROTATE_180)  # 旋转180度
cv2.imshow('shun90', rotated_image)
cv2.imshow('ni90', rotated_image1)
cv2.imshow('180', rotated_image2)
cv2.waitKey(0)

效果

二·图像金字塔

图像金字塔是什么?

是由一幅图像的多个不同分辨率的子图构成的图像集合。是通过一个图像不断的降低采样率产生的,最小的图像可能仅仅有一个像素点。图像金字塔的底部是待处理的高分辨率图像(原始图像),而顶部则为其低分辨率的近似图像。

向下采样

向金字塔顶部移动时,图像的尺寸和分辨率都不断地降低。通常情况下,每向上移动一级,图像的宽和高都降低为原来的1/2。 做法:

1、高斯滤波

2、删除其偶数行和偶数列

OpenCV函数cv2.pyrDown()

图像金字塔作用

· 特征点提取(SIFT、HOG、ORB等) · 模板匹配 · 光流跟踪

逻辑

1. 高斯金字塔的下采样(图像缩小)

python 复制代码
face = cv2.imread('face.png', cv2.IMREAD_GRAYSCALE)  # 读取灰度图作为原始图像(G0)
face_down_1 = cv2.pyrDown(face)  # 对G0下采样得到G1
face_down_2 = cv2.pyrDown(face_down_1)  # 对G1下采样得到G2
  • 下采样逻辑
    cv2.pyrDown() 是高斯金字塔的核心操作,步骤为:
    1. 对原始图像进行高斯模糊(去除高频信息,避免下采样时的锯齿);
    2. 去掉图像中所有偶数行和偶数列,使图像尺寸变为原来的 1/2(宽和高各减半)。
  • 效果
    每一次下采样,图像尺寸缩小一半(如 G0→G1→G2),但会丢失部分细节,图像逐渐模糊。

2. 高斯金字塔的上采样(图像放大)

python 复制代码
face_up_1 = cv2.pyrUp(face)  # 对G0上采样得到G1'
face_up_2 = cv2.pyrUp(face_up_1)  # 对G1'上采样得到G2'
  • 上采样逻辑
    cv2.pyrUp() 是下采样的逆操作,步骤为:
    1. 在图像的偶数行和偶数列之间插入 0 值,使图像尺寸变为原来的 2 倍;
    2. 用与下采样相同的高斯核进行卷积(平滑处理),填充插入的 0 值。
  • 注意
    上采样只是单纯放大尺寸,无法恢复下采样时丢失的细节,因此放大后的图像会比原始图像模糊。

3. 下采样后再上采样的局限性

python 复制代码
# 对下采样后的图像(G1、G2)进行上采样
face_down_1_up = cv2.pyrUp(face_down_1)  # G1上采样得到G1_up
face_down_2_up = cv2.pyrUp(face_down_2)  # G2上采样得到G2_up
  • 问题
    下采样时丢失的高频信息(细节)无法通过上采样恢复,因此 face_down_1_up 虽然尺寸与原始图像 face 相近,但会更模糊,无法复原原始图像。

4. 拉普拉斯金字塔(图像细节保留与复原)

为了保留下采样丢失的细节,引入拉普拉斯金字塔:

python 复制代码
# 计算拉普拉斯金字塔层(L0、L1)
L0 = face - face_down_1_up[:face.shape[0], :face.shape[1]]  # 原始图 - G1上采样(裁剪后)
L1 = face_down_1 - face_down_2_up[:face_down_1.shape[0], :face_down_1.shape[1]]  # G1 - G2上采样(裁剪后)
  • 拉普拉斯逻辑
    拉普拉斯金字塔的每一层(Li)是通过 当前高斯层(Gi)减去上一层高斯层上采样后的结果(Gi+1_up) 得到的,它记录了下采样过程中丢失的细节(高频信息)。
    • 裁剪操作([:shape])是为了处理上采样后尺寸可能与原始层不匹配的问题(确保像素尺寸一致)。
python 复制代码
# 利用拉普拉斯金字塔复原图像
fuyuan = face_down_1_up[:face.shape[0], :face.shape[1]] + L0  # G1_up(裁剪) + L0 = 原始图
  • 复原逻辑
    拉普拉斯层(L0)记录了 G0 到 G1 丢失的细节,因此将上采样后的 G1(G1_up)与 L0 相加,即可恢复原始图像 G0。这验证了拉普拉斯金字塔对细节的保留能力。

总结

代码的核心逻辑是:

  1. 高斯金字塔通过下采样缩小图像(丢失细节)和上采样放大图像(无法恢复细节);
  2. 拉普拉斯金字塔通过记录下采样丢失的细节(高频信息),实现了基于高斯金字塔的图像复原。

这种金字塔结构常用于图像融合、多分辨率分析等场景,通过不同尺度的图像信息进行处理。

代码

python 复制代码
import cv2  #opencv读取的格式是BGR
import matplotlib.pyplot as plt  #matplotlib读取的格式与opencv不同
import numpy as np              # pip install numpy==1.26.4 -i
'''----------------高斯金字塔操作中的向下采样------------------'''
#  下采样 是一种减小图像尺寸的方法,它通常涉及到降低图像的分辨率,即减少图像中像素的数量,从而使图像看起来更小。
#  上采样 是一种增大图像尺寸的方法,它通过插值和滤波技术来恢复图像的分辨率和细节,通常用于图像放大或者与下采样后的图像进行比较。
#  resize函数  是一种通用的图像尺寸调整方法,它可以按照指定的目标尺寸来缩放图像,不涉及金字塔结构或者特定的滤波操作。

# dst = cv2.pyrDown(src [,dst, dstsize [, borderType] ])
# dst:目标图像
# src:原始图像
# dstsize:目标图像的大小

face = cv2.imread('face.png',cv2.IMREAD_GRAYSCALE)#G0
cv2.imshow('face',face)
cv2.waitKey(0)
face_down_1 = cv2.pyrDown(face)#下采样G1
cv2.imshow('down_1',face_down_1)
cv2.waitKey(0)
face_down_2 = cv2.pyrDown(face_down_1)#G2
cv2.imshow('down_2',face_down_2)
cv2.waitKey(0)
#高斯金字塔操作中的向上采样
# dst = cv2.pyrUp(src [,dst, dstsize [, borderType] ])
# dst:目标图像
# src:原始图像
# dstsize:目标图像的大小
face_up_1 = cv2.pyrUp(face)
cv2.imshow('up_1',face_up_1)#G1'
cv2.waitKey(0)
face_up_2 = cv2.pyrUp(face_up_1)
cv2.imshow('up_2',face_up_2)#G2'
cv2.waitKey(0)
# # #对下采用后图像进行上采样,图像变模糊,无法复原
face_down_1_up = cv2.pyrUp(face_down_1)#下采样G1
face_down_2_up = cv2.pyrUp(face_down_2)#下采样G2

cv2.imshow('down_1_up',face_down_1_up)
cv2.imshow('down_2_up',face_down_2_up)
cv2.waitKey(0)

# # # 拉普拉斯金字塔
L0 = face - face_down_1_up
L1 = face_down_1 - face_down_2_up

fuyuan = face_down_1_up + L0
cv2.imshow('L0',L0)
cv2.imshow('L1',L1)
cv2.waitKey(0)
cv2.imshow('fuyuan',fuyuan)
cv2.waitKey(0)

下采样效果

上采样

向上采样

通常将图像的宽度和高度都变为原来的2倍。这意味着,向上采样的结果图像的大小是原始图像的4倍。因此,要在结果图像中补充大量的像素点。对新生成的像素点进行赋值的行为,称为插值。 做法:

1、插值

2、高斯滤波

通过以上分析可知,向上采样和向下采样是相反的两种操作。但是,由于向下采样会丢失像素值,所以这两种操作并不是可逆的。也就是说,对一幅图像先向上采样、再向下采样,是无法恢复其原始状态的;同样,对一幅图像先向下采样、再向上采样也无法恢复到原始状态

拉普拉斯金字塔

为了在向上采样是能够恢复具有较高分辨率的原始图像,就要获取在采样过程中所丢失的信息,这些丢失的信息就构成了拉普拉斯金字塔。 也是拉普拉斯金字塔是有向下采样时丢失的信息构成。

拉普拉斯金字塔的定义:

还是刚才的代码

python 复制代码
# 拉普拉斯金字塔 - 修正形状不匹配问题
# 将上采样后的图像裁剪到与原始图像相同的尺寸
L0 = face - face_down_1_up[:face.shape[0], :face.shape[1]]
# 将上采样后的图像裁剪到与face_down_1相同的尺寸
L1 = face_down_1 - face_down_2_up[:face_down_1.shape[0], :face_down_1.shape[1]]

fuyuan = face_down_1_up[:face.shape[0], :face.shape[1]] + L0  # 同样需要裁剪上采样图像
cv2.imshow('L0', L0)
cv2.imshow('L1', L1)
cv2.waitKey(0)
cv2.imshow('fuyuan', fuyuan)
cv2.waitKey(0)

图像直立方图

直方图:是图像中像素强度分布的图形表达方式。

直方图的作用:例如视频中。通过标记帧和帧之间显著的边缘和颜色的统计变化,来检测视频中场景的变换。

灰度值在0 - 255范围之间总共 256 个值,可以将我们的范围划分为子部分(称为bins),例如:

逻辑

1. 灰度图直方图(基础)

python 复制代码
phone = cv2.imread('phone.png', cv2.IMREAD_GRAYSCALE)  # 读取灰度图
a = phone.ravel()  # 将二维图像转为一维数组(每个元素是像素的灰度值)
plt.hist(a, bins=256)  # 用matplotlib绘制直方图
plt.show()
  • 逻辑:先读取灰度图像,将其扁平化为一维数组(便于统计),然后用plt.hist统计每个灰度值(0-255)的像素数量,生成 256 个条柱的直方图,直观展示图像的明暗分布。

2. 用 OpenCV 计算直方图(灰度图)

python

运行

python 复制代码
phone_hist = cv2.calcHist([phone], [0], None, [16], [0, 256])  # 计算直方图
plt.plot(phone_hist)  # 绘制曲线形式的直方图
plt.show()
  • cv2.calcHist参数解析:
    • [phone]:输入图像(需用列表包裹)
    • [0]:通道索引(灰度图只有 1 个通道,索引为 0)
    • None:不使用掩膜(统计整幅图)
    • [16]:将 0-255 分为 16 个区间(BINS=16)
    • [0, 256]:像素值范围
  • 逻辑:将灰度值范围压缩为 16 个区间,用曲线展示每个区间的像素数量分布,比 256 个条柱更简洁。

3. 彩色图的三通道直方图

python 复制代码
img = cv2.imread('phone.png')  # 读取彩色图(BGR格式)
color = ('b', 'g', 'r')  # 对应BGR通道
for i, col in enumerate(color):
    histr = cv2.calcHist([img], [i], None, [256], [0, 256])  # 分别计算每个通道的直方图
    plt.plot(histr, color=col)  # 用对应颜色绘制曲线
plt.show()
  • 逻辑:彩色图有 B、G、R 三个通道,循环计算每个通道的直方图(256 个区间),并以对应颜色绘制曲线,展示三通道的像素分布差异(例如红色通道亮部多说明图像偏红)。

4. 掩膜(mask)的应用

  • 掩膜逻辑:
    • 掩膜是一张与原图同尺寸的黑白图,白色区域(255)表示 "需要统计的区域",黑色区域(0)表示 "忽略的区域"。
    • 通过cv2.bitwise_and可直观看到掩膜截取的效果(只保留中间区域)。
    • 计算直方图时传入 mask 参数,最终结果仅反映掩膜白色区域的像素分布,用于分析图像局部的明暗特征。

总结

代码从基础到进阶展示了直方图的用法:

  1. 全图灰度分布 → 2. 压缩区间的简洁展示 → 3. 彩色图的通道差异 → 4. 局部区域的精准分析(掩膜)。
    直方图的核心作用是反映像素值的分布规律,可用于图像增强、阈值分割等后续处理的依据。

直立图代码

python 复制代码
#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
'''------------------直方图---------------------------------'''
# cv2.calcHist(images,channels,mask,histSize,ranges)  计算图像的直方图,用于表示图像中像素灰度级别的分布情况.
# images: 原图像图像格式为 uint8 或 float32。当传入函数时应 用中括号 [] 括来例如[img]
# channels: 表示传入的图像通道数。如果输入图像是灰度图它的值就是 [0]。
#           如果是彩色图像 的传入的参数可以是 [0][1][2] 它们分别对应着 BGR。
# mask: 掩模图像。统计整幅图像的直方图就把它为None。但是如果你想统计图像某一部分的直方图,你就制作一个掩模图像并使用它。
# histSize:BINS的数目。也需用中括号括来   (分成多少个区间)
#   BINS :上面的直方图显示了每个像素值的像素数,即从0到255。即您需要256个值才能显示上述直方图。
#       但是请考虑一下,如果您不需要单独查找所有像素值的像素数,而是在像素值间隔内查找像素数,
#       该怎么办?例如,您需要找到介于 0 到 15 之间的像素数,然后是 16 到 31、32到47...、240 到 255。
#       您只需要 16 个值来表示直方图。
# 因此,只需将整个直方图拆分为 16 个子部分,每个子部分的值就是其中所有像素计数的总和。
# 这每个子部分都称为"BIN"。在第一种情况下,条柱数为256(每个像素一个),而在第二种情况下,它只有16。BINS 在 OpenCV 文档中由术语histSize表示。
# ranges: 像素值范围常为 [0 256]
phone = cv2.imread('phone.png',cv2.IMREAD_GRAYSCALE)
# 将图像转换为一维数组
a = phone.ravel()
# 这里使用了 numpy 的 ravel 函数,将多维数组拉成一维数组。
# 绘制直方图
plt.hist(a, bins=256)  # 使用 matplotlib 的 hist 函数绘制直方图。
# 参数解释:
# - a:一维数组,即图像的像素值组成的数组。
# - bins=256:指定直方图的条数,即灰度级的数量。
plt.show()
# # 显示直方图
phone_hist = cv2.calcHist([phone],[0],None,[16],[0,256])
plt.plot(phone_hist)#使用calcHist的值绘制曲线图
plt.show()

img=cv2.imread('phone.png')
color=('b','g','r')
for i,col in enumerate(color):
    histr=cv2.calcHist([img],[i],None,[256],[0,256])
    plt.plot(histr,color=col)
plt.show()

# #
# # #什么是mask?掩膜,ps pr
# # #mask参数如何使用? mask为掩模图像,先来看一下mask效果
phone = cv2.imread('phone.png',cv2.IMREAD_GRAYSCALE)
cv2.imshow('phone',phone)
cv2.waitKey(0)
mask = np.zeros(phone.shape[:2],np.uint8)   #创建黑白图像,用于制作mask
mask[50:350,100:470] = 255
cv2.imshow('mask',mask)
cv2.waitKey(0)
# #
# # cv2.bitwise_and():对图像(灰度图像或彩色图像均可)每个像素值进行二进制"与"操作,1&1=1,1&0=0,0&1=0,0&0=0
# # bitwise_and(src1, src2, dst=None, mask=None)参数:
# # src1、src2:为输入图像或标量,标src1和src2相与。
# # dst:可选输出变量,如果需要使用非None则要先定义,且其大小与输入变量相同
# # mask:图像掩膜,可选参数,用于指定要更改的输出图像数组的元素,mask为0的值,src1和src2相与的值都为0.
# #       非0的值,为src1和src2相与的值。
Phone_mask = cv2.bitwise_and(phone,phone,mask=mask)
cv2.imshow('phone_mask',Phone_mask)
cv2.waitKey(0)
# # # # #
phone_hist_mask = cv2.calcHist([phone],[0],mask,[256],[0,256])
plt.plot(phone_hist_mask)#使用calcHist的值绘制曲线图
plt.show()

当进行两个图像的按位与操作时,实际上是将这两个图像在同一位置上的像素值视为二进制数,然后进行按位与运算。在你的例子中,图像1某个区域的像素值为25,图像2同一区域的像素值为36,我们需要将这两个值转换为二进制形式,然后进行按位与运算。

具体步骤如下:

  1. 转换为二进制: - 25的二进制表示为:00011001 - 36的二进制表示为:00100100

  2. 按位与运算: - 对应位上的值如果都是1,则结果位上的值为1;否则为0。 - 对于25和36,按位与运算的结果为:00000000(每个位上至少有一个0)

  3. 转换回十进制: - 按位与运算的结果00000000转换为十进制为:0 因此,图像1的像素值为25与图像2的像素值为36进行按位与操作的结果为0。

相关推荐
fai厅的秃头姐!15 分钟前
OpenCV实战篇
人工智能·opencv·计算机视觉
TMT星球1 小时前
虎鲸文娱春苗编剧计划全面升级,已孵化20个剧本进入前期开发
大数据·人工智能
奔跑中的小象7 小时前
统信UOS + 天数AI卡部署SGLang服务手册
人工智能·uos·sglang·天数智芯
DevSecOps选型指南7 小时前
中国版Mythos,为何是悬镜安全灵脉CodeAI?
人工智能·安全
Shockang8 小时前
LangGraph 状态机实战
人工智能
刹那芳华19928 小时前
循环神经网络的从零开始实现(RNN)
人工智能·rnn·深度学习
阿童木写作8 小时前
跨境图片翻译工具多合一,批量图片视频字幕翻译加智能抠图
人工智能·python·音视频·语音识别
科技之门8 小时前
AI3D从建模到贴图、绑骨和动画的完整流程怎么做?V2Fun完整工作流指南
人工智能·3d·贴图
宇宙第一小趴菜8 小时前
二、机器学习的应用领域和发展史
人工智能·机器学习
前端开发江鸟8 小时前
我写过 MCP Server,却一直以为 MCP 只有 Tool
人工智能