python-桌面软件自动化(一)(实战微信发消息)

embedded/2024/9/20 7:36:39/ 标签: 自动化, 运维

什么是pywinauto

pywinauto是一组用于自动化Microsoft Windows GUI的python模块。 最简单的是,它允许您将鼠标和键盘操作发送到窗口对话框和控件。

pywinauto安装和启动

1.安装pywinauto

在 Pycharm 底部的终端(Terminal)窗口中输入

pip install pywinauto

提示success即安装成功了。

2.backend选择

我们安装好Pywinauto之后,首先要确定哪种可访问性技术(backend)可以用于我们的应用程序,在windows上受支持的有两种:
        Win32 API ( backend= "win32" ) -默认的backend
                MFC, VB6, VCL, 简单的WinForms控件和大多数旧的遗留应用程序
        MS UI Automation ( backend="uia" )
                WinForms, WPF, Store apps, Qt5, 浏览器
如果不能确定程序到底适用于那种backend,可以借助于GUI对象检查工具来查看,常用的检查工具有Inspect.exe,Spy++ 。如果使用Inspect的UIA模式,可见的控件和属性更多的话,backend可选uia,反之,backend可选win32。

3.控件查看工具-inspect

        inspect.exe下载链接:https://z4gvregzdz.feishu.cn/file/boxcnoI8GOg5aIWaYTVXL8hyWzc

        spy++下载链接:https://z4gvregzdz.feishu.cn/file/boxcnqlUR6yBhvuJsoFiUVrVGpe
        将inspect左上角的下拉列表中切换到“UI Automation”,然后鼠标点一下你需要测试的程序窗体,inspect就会显示相关信息,如下图所示。说明backend为uia
        程序里面的任意一个部位其实都是控件,在inspect的控件树中都可以找到,是一层一层分级别的,可以一个个点开所有控件

完整操作步骤

通过pywinauto操作控件需要以下几个步骤:
        第一步 :创建实例化对象,得到的app是Application对象
        第二步 :选择窗口 ,得到的窗口是WindowSpecification对象
        第三步:基于WindowSpecification对象使用其方法再往下查找,定位到具体的控件
        第四步:使用控件的方法属性执行我们需要的操作
        接下来我们先看一个实例代码,(确保电脑版微信已经登录且前台显示在桌面上)按照上面的步骤获取微信的搜索文本框并点击,代码如下:

from pywinauto import Application
# 第一步连接已有微信进程创建实例化对象
PID = 12345 #进程PID在 任务管理器-详细信息 可以查看后修改该值
微信主程序 = Application(backend='uia').connect(process=PID)
# 第二步拿到微信主窗口
主窗口 = 微信主程序.window(class_name='WeChatMainWndForPC')
# 第三步通过child_window方法查找搜索文本框
搜索 = 主窗口.child_window(control_type='Edit', title='搜索')
# 第四步进行操作点击控件
搜索.click_input()

        执行后就会自动点击微信左上角搜索文本框了(注意:由于PID是变化的,以上代码一定能执行成功后面会详细介绍)。
        接下来,我们按照上面四步逐步讲解,

1.创建实例化对象

以微信为例,这里介绍下常用的两种获取实例化程序的方式:

启动

start()用于还没有启动软件的情况。timeout为超时参数(可选),若软件启动所需的时间较长可选
timeout,默认超时时间为5s。
start(self, cmd_line, timeout=app_start_timeout)
先确定微信是关闭的,我们通过上面方法启动微信并获取对应的实例化对象,如下:

from pywinauto import Application
# 启动微信进程(注意路径中特殊字符的转义,/和\)
app = Application(backend="uia").start(r'"E:\Program Files
(x86)\Tencent\WeChat\WeChat.exe"')
print(app)

运行后将会自动启动微信。

连接

connect()用于连接已经启动的程序。connect()方式有多种:
process:进程id(PID)

app = Application().connect(process=12580)

handle:应用程序的窗口句柄

app = Application().connect(handle=0x234f1b)

path:进程的执行路径(GetModuleFileNameEx 模块会查找进程的每一个路径并与我们传入的
路径去做比较)

app = Application().connect(path=“c:\windows\system32\notepad.exe”)

参数组合(传递给pywinauto.findwindows.find_elements()这个函数)
app = Application().connect(title_re=".*Notepad", class_name=“Notepad”)
下面我们介绍下通过进程PID与窗口句柄两种链接已启动程序的方式:
PID在 任务管理器-详细信息 可以查看。如下图,WeChat.exe的PID就是18364。

from pywinauto.application import Application
# 通过PID连接已有微信进程
微信主程序 = Application(backend='uia').connect(process=18364)
print(微信主程序)

在星球中也有提到运利用第三方库psutil根据进程名获取进程PID的文章,有兴趣可以去看看,这里就不做详细介绍了。
在前面游戏自动化中学习了如何获取窗口的句柄,所以我们本着偷懒的原则还可以通过pywin32模块根据窗口名获取窗口句柄,然后获取根据句柄来获得主程序,代码如下:

import win32gui
from pywinauto.application import Application
句柄 = win32gui.FindWindow(None, '微信')
微信主程序 = Application(backend='uia').connect(handle=句柄)
print(微信主程序)

注意:应用程序必须先准备就绪,才能使用connect0)。当应用程序start()后没有超时和重连的机制,在pywinauto外启动启动应用程序,则需要睡眠或编程等待循环以等待应用程序完全启动。

实例对象app的常用方法

通过查看pywinauto的源码中application.py文件,可以看到app的所有属性方法,下面列举常用方
法:
app.top_window()
返回应用程序当前顶部窗口,是WindowSpecification对象,可以继续使用对象的方法往下继续
查找控件
如:app.top_window().child_window(title='搜索', control_type='Edit')

app.window(kwargs)
根据筛选条件,返回一个窗口, 是WindowSpecification对象,可以继续适用对象的方法往下继
续查找控件
微信主界面: app.window(class_name='WeChatMainWndForPC')

app.windows(kwargs)
根据筛选条件返回一个窗口列表,无条件默认全部,列表项为wrapped对象,可以使用wrapped
对象的方法,注意不是WindowSpecification对象

[<uiawrapper.UIAWrapper - '微信', Dialog, -5995915281609806513>]

app.kill(soft=False)
强制关闭程序

app.cpu_usage()
返回CPU使用率百分比

app.wait_cpu_usage_lower(threshold=2.5, timeout=None, usage_interval=None)
等待进程CPU使用率百分比小于指定的阈值threshold

app.is64bit()
判断操作的进程是否是64-bit

简单演示下。

from pywinauto.application import Application
# 通过PID连接已有微信进程
微信主程序 = Application(backend='uia').connect(process=18364)
print(微信主程序.top_window())
print(微信主程序.window())
print(微信主程序.windows())
print(微信主程序.cpu_usage())
print(微信主程序.is64bit())

2.选择窗口

pywinauto选择窗口有三种方式,获取到的窗口为 WindowSpecification对象。

dlg = app.window(title="your title", classname="your class", ...)
dlg = app.YourDialogTitle
dlg = app['Your Dialog Title']

以微信主界面窗口为例,先通过inspect工具查看微信的窗体信息:

按照上面的方法我们有三种方式来选取窗口,代码如下:

from pywinauto.application import Application
import win32gui
# 根据应用程序窗口名获得句柄
句柄 = win32gui.FindWindow(None, '微信')
# 通过句柄连接已有微信进程
app = Application(backend='uia').connect(handle=句柄)
dlg = app.window(class_name='WeChatMainWndForPC')
# dlg = app.微信
# dlg = app['微信']
dlg.restore() # 将窗口恢复为正常大小,比如最小化的让他正常显示在桌面
dlg.draw_outline(colour='red') # 控件外围画框,便于查看,支持'red', 'green', 'blue'

前面我们一直是手动先将微信窗口放置到前台,上面代码中我们利用restore()方法就可以自动将窗口恢复到正常大小了,利用draw outline可以将控件外围画框,非常便于调试。

窗口的常用方法

下面则是窗口平时常用的一些方法:

# 以下几个只支持窗口模式的控件
dlg.close() # 关闭界面
dlg.minimize() # 最小化界面
dlg.maximize() # 最大化界面
dlg.restore() # 将窗口恢复为正常大小,比如最小化的让他正常显示在桌面
dlg.get_show_state() # 正常0,最大化1,最小化2
dlg.exists(timeout=None, retry_interval=None) # 判断是否存在
#timeout:等待时间,一般默认5s
#retry_interval:timeout内重试时间
dlg.wait(wait_for, timeout=None, retry_interval=None) # 等待窗口处于特定状态
dlg.wait_not(wait_for_not, timeout=None, retry_interval=None) # 等待窗口不处于特定状
态,即等待消失
# wait_for/wait_for_not:
# * 'exists' means that the window is a valid handle
# * 'visible' means that the window is not hidden
# * 'enabled' means that the window is not disabled
# * 'ready' means that the window is visible and enabled
# * 'active' means that the window is active
# timeout:等待多久
# retry_interval:timeout内重试时间# eg: dlg.wait('ready')

代码如下:

import time
from pywinauto.application import Application
import win32gui
# 根据应用程序窗口名获得句柄
句柄 = win32gui.FindWindow(None, '微信')
# 通过句柄连接已有微信进程
微信主程序 = Application(backend='uia').connect(handle=句柄)
微信窗口 = 微信主程序.window(class_name='WeChatMainWndForPC')
微信窗口.restore() # 将窗口恢复为正常大小,比如最小化的让他正常显示在桌面
微信窗口.draw_outline(colour='red') # 控件外围画框,便于查看,支持'red', 'green', 'blue'
time.sleep(2)
微信窗口.maximize() # 最小化界面
print(微信窗口.get_show_state())
time.sleep(2)
微信窗口.minimize() # 最大化界面
print(微信窗口.get_show_state())
time.sleep(2)
微信窗口.restore() # 将窗口恢复为正常大小,比如最小化的让他正常显示在桌面
print(微信窗口.get_show_state())

3.定位控件

最常用查找控件方法

找到窗口后,我们就需要查找我们所需要的控件了。在pywinauto中查找控件都是通过层级查找的,我们通过inspect工具也可以看到一个窗口中所有控件都是成树状的层级结构的。
最常用的查找控件的方法就是child_window(**kwargs)

child_window(**kwargs) # 可以不管层级的找后代中某个符合条件的元素,最常用

kwargs为筛选条件,最常用的就是class_name、title和control_type。

class_name=None, # 类名,对应ClassName
title=None, # 控件的标题文字,对应inspect中Name字段
control_type=None, # 控件类型,inspect界面LocalizedControlType字段的英文名

kwargs筛选条件可以设置一个或多个。

这里有个小技巧,inspect界面LocalizedControlType字段的英文名对应其实就是ControlType字段去除 UIA_和 ControlTypeId 。
class_name在选择微信窗口时已经使用过了,这里就不再介绍了。下面介绍下另外两种筛选参数。

title参数的使用如下:

from pywinauto.application import Application
import win32gui
# 根据应用程序窗口名获得句柄
句柄 = win32gui.FindWindow(None, '微信')
# 通过句柄连接已有微信进程
微信主程序 = Application(backend='uia').connect(handle=句柄)
# 拿到微信主窗口
微信窗口 = 微信主程序.window(class_name='WeChatMainWndForPC')
微信窗口.restore()
搜索 = 微信窗口.child_window(title='搜索')
print(搜索.window_text())
搜索.draw_outline(colour='red')

draw outline不仅对窗口有用,对窗口中的控件也是可用的。

但是这里会有一个问题,当你的窗口中出现多个标题为 搜索 的控件时,我们上面的代码就会报错
ElementAmbiguousError,所以我们还需要添加更多的筛选条件,比如上面所说的control_type。

from pywinauto.application import Application
import win32gui
# 根据应用程序窗口名获得句柄
句柄 = win32gui.FindWindow(None, '微信')
# 通过句柄连接已有微信进程
微信主程序 = Application(backend='uia').connect(handle=句柄)
# 拿到微信主窗口
微信窗口 = 微信主程序.window(class_name='WeChatMainWndForPC')
微信窗口.restore()
搜索 = 微信窗口.child_window(control_type='Edit', title='搜索') # 多个筛选条件确定唯一的
控件
print(搜索.window_text())
搜索.draw_outline(colour='red)

当然也可以通过添加筛选参数found index来取多个筛选结果中的一个,使用方法如下:
found index=0,取值从0开始

from pywinauto.application import Application
import win32gui

# 根据应用程序窗口名获得句柄
句柄 = win32gui.FindWindow(None, '微信')
# 通过句柄连接已有微信进程
微信主程序 = Application(backend='uia').connect(handle=句柄)
# 拿到微信主窗口
微信窗口 = 微信主程序.window(class_name='WeChatMainWndForPC')
微信窗口.restore()
搜索 = 微信窗口.child_window(title='搜索', found_index=0)
print(搜索.window_text())
搜索.draw_outline(colour='red')

这里还需要注意一点,当要查找的控件不存在时,程序会报错ElementNotFoundError(kwargs)

4.操作控件

定位到控件后就是对控件进行操作了,下面介绍几种最常用的方法与属性。

控件常用的方法属性

ctrl.click_input() # 最常用的点击方法,一切点击操作的基本方法(底层调用只是参数不同),左键单
击,使用时一般都使用默认不需要带参数
# 键盘输入
ctrl.type_keys(keys, pause = None, with_spaces = False,)
# keys:要输入的文字内容
# pause:每输入一个字符后等待时间,默认0.01就行
# with_spaces:是否保留keys中的所有空格,默认去除0
# 调试经常用的属性与方法
ctrl.window_text() # 控件的标题文字,对应inspect中Name字段
ctrl.draw_outline(colour='green') # 控件外围画框,便于查看,支持'red', 'green', 'blue'

上面几种方法属性我们基本上就用到了,我们来看看输入的使用。

from pywinauto import Application
import win32gui
# 根据应用程序窗口名获得句柄
句柄 = win32gui.FindWindow(None, '微信')
# 通过句柄连接已有微信进程
微信主程序 = Application(backend='uia').connect(handle=句柄)
# 第二步拿到微信主窗口
主窗口 = 微信主程序.window(class_name='WeChatMainWndForPC')
# 第三步通过child_window方法查找搜索文本框
搜索 = 主窗口.child_window(control_type='Edit', title='搜索')
# 第四步进行操作点击控件
搜索.click_input()
搜索.type_keys("有霸夫") # 输入 有霸夫

实战 给微信好友发送消息

前面我们已经学会了pywinauto操作的基本步骤,那接下来我们进入实战。
实战要求如下:在微信搜索框中搜索 文件传输助手, 发一条 学Python,要努力
步骤拆解如下:
1.连接已有微信进程创建实例化对象
2.拿到微信主窗口
3.查找搜索文本框
4.搜索框输中入文件传输助手
5.查找到联系人中文件传输助手
6.点击有文件传输助手
7.查找消息输入框
8.点击发送按钮。
我们通过inspect可以定位到这些控件的,实现代码如下:

from pywinauto import Application, ElementNotFoundError
import win32gui
import time联系人 = '文件传输助手'  # 可以改成联系人名称
消息 = '学Python,要努力'try:句柄 = win32gui.FindWindow(None, '微信')if 句柄 == 0:raise RuntimeError("无法找到微信窗口")微信主程序 = Application(backend='uia').connect(handle=句柄)微信窗口 = 微信主程序.window(class_name='WeChatMainWndForPC')微信窗口.restore()搜索 = 微信窗口.child_window(control_type='Edit', title='搜索')搜索.click_input()搜索.type_keys(联系人)time.sleep(2)  # 等待搜索结果加载搜索结果 = 微信窗口.child_window(control_type='List', title='@str:IDS_FAV_SEARCH_RESULT:3780')联系人项列表 = 搜索结果.children(control_type='ListItem', title=联系人)if not 联系人项列表:raise RuntimeError("没有找到匹配的联系人项")# 选择第一个匹配的项联系人项 = 联系人项列表[0]联系人项.click_input()消息输入框 = 微信窗口.child_window(control_type='Edit', title='文件传输助手')消息输入框.click_input()消息输入框.type_keys(消息)发送 = 微信窗口.child_window(control_type='Button', title='发送(S)')发送.click_input()except ElementNotFoundError as e:print(f"未找到指定的控件: {e}")
except RuntimeError as e:print(f"运行时错误: {e}")
except Exception as e:print(f"发生了一个错误: {e}")

课程总结

本节课程我们学习了inspect工具的使用,pywinauto操作控件的完整步骤。只要熟练掌握实例对象,选择窗口,定位控件,以及操作控件的相关方法,我们就已经可以开始编写一些软件自动化功能了。任何复杂的操作其实都可以化繁为简的,掌握基础,多写多练,相信同学们都能写出自己想要的功能的。


课后习题

模仿实战案例,搜索 有一个自己所在群聊 ,在群聊里发送一句 你好 的文本和一个得意 的表情。


http://www.ppmy.cn/embedded/111581.html

相关文章

ArcGIS Pro SDK (十四)地图探索 4 书签

ArcGIS Pro SDK (十四)地图探索 4 书签 文章目录 ArcGIS Pro SDK (十四)地图探索 4 书签1 书签1.1 使用活动地图视图创建新书签1.2 从联昌国际添加新书签1.3 获取项目的书签集合1.4 获取地图书签1.5 将书签移到顶部1.6 重命名书签1.7 删除具有给定名称的书签1.8 更改书签的…

Linux 自主 shell 编写(C 语言实现)

Linux 自主 shell 编写&#xff08;C 语言实现&#xff09; 效果主要步骤打印命令行提示符获取用户命令字符串切割用户命令字符串执行命令循环 至此源码&#xff08;简易半成品&#xff09;细节内建命令问题cd 退出码问题echo 查看退出码 完整源码makefilemyshell.c 效果 效果…

根据NVeloDocx Word模板引擎生成Word(一)

自从我们基于免费开放的E6开发平台&#xff0c;实现了根据Word模版生成Word文档的模版引擎后&#xff0c;也实实在在帮助到了一些有需要的朋友。但是由于制作Word模版的过程对于很多人会是一个很头疼的事情&#xff0c;虽然提供了详细的文档&#xff0c;但是我们也经常接到他们…

HCIA--实验十:路由的递归特性

递归路由的理解 一、实验内容 1.需求/要求&#xff1a; 使用4台路由器&#xff0c;在AR1和AR4上分别配置一个LOOPBACK接口&#xff0c;根据路由的递归特性&#xff0c;写一系列的静态路由实现让1.1.1.1和4.4.4.4的双向通信。 二、实验过程 1.拓扑图&#xff1a; 2.步骤&am…

【网络安全】服务基础第二阶段——第二节:Linux系统管理基础----Linux统计,高阶命令

目录 一、Linux高阶命令 1.1 管道符的基本原理 1.2 重定向 1.2.1 输出重定向 1.2.2 输入重定向 1.2.3 wc命令基本用法 1.3 别名 1.3.1 which命令基本语法 1.3.2 alias命令基本语法 1.4 压缩归档tar 1.4.1 第一种&#xff1a;gzip压缩 1.4.2 第二种&#xff1a;bzip…

Docker续9:使用docker-compose部署nmt项目,在haproxy中代理mysql负载均衡

为之前的docker-compose添加mysql负载均衡 [rootlocalhost pes]# rm -rf mysql/data/auto.cnf //先删除这个文件&#xff0c;这个文件保证了mysql的唯一&#xff0c;因为待会要做一个从mysql&#xff0c;所以必须删除 [rootlocalhost pes]# cp -r mysql/data mysql/data0…

【使用CPU和使用GPU的算法】

一、通常耗费 GPU 的算法 想象 GPU 是一个有很多小工人同时干活的工厂。像训练很厉害的图像识别网络、处理复杂的图形渲染以及处理大量数据的时候&#xff0c;就好比有很多相似的任务可以同时进行。比如&#xff0c;图像识别的时候要对很多像素点同时做计算&#xff0c;这就很…

大数据Flink(一百一十六):Flink SQL的时间属性

文章目录 Flink SQL的时间属性 一、Flink 三种时间属性简介 二、Flink 三种时间属性的应用场景 三、​​​​​​​SQL 指定时间属性的两种方式 四、​​​​​​​​​​​​​​SQL 处理时间DDL定义 五、​​​​​​​​​​​​​​SQL 事件时间DDL定义 Flink SQL的时…

7项国家机密被境外公司窃取!

近日&#xff0c;国家安全机关公开了一起境外公司非法搜集窃取我稀土领域国家秘密案。该公司通过利诱中方人员&#xff0c;窃取7项机密级国家秘密。 “内鬼”为51万美元泄露国家7项机密&#xff01; 该公司中方雇员叶某某在外籍员工指挥下&#xff0c;利诱收买国内某稀土公司…

基于STM32设计的水渠闸门远程控制系统(华为云IOT)(226)

文章目录 一、前言1.1 项目介绍【1】开发背景【2】项目实现的功能【3】项目硬件模块组成【4】ESP8266工作模式配置1.2 设计思路【1】整体设计思路【2】整体构架【3】上位机开发思路1.3 项目开发背景【1】选题的意义【2】可行性分析【3】参考文献【4】摘要1.4 开发工具的选择【1…

应急响应实战---是谁修改了我的密码?

前言&#xff1a;此次应急响应为真实案例&#xff0c;客户反馈无法通过密码登录服务器&#xff0c;疑似服务器被入侵 0x01 如何找回密码&#xff1f; 客户服务器为windows server2019&#xff0c;运维平台为PVE平台&#xff1b;实际上无论是windows系统或者是linux系统&#…

语言中的溢出

任何数据类型都有最大和最小值&#xff0c;会溢出是由物理特性决定。编译型静态类型语言一般明确定义类型大小&#xff0c;这种定义出来的类型都存在溢出的可能。有的动态语言为减少程序员溢出的困扰&#xff0c;尽可能为数据扩展成更大的数值&#xff0c;Python就是这样的好人…

js 笔记

javascript编程语言 html css标记语言 html定义网页内容 css是描述网页布局 js控制网页行为 Livescript 为了确保不同浏览器上运行的javascript标准一致&#xff0c;所以几个公司共同制定了js的标准…

Modbus通信

Modbus是一种经典的工业通信协议&#xff0c;由Modicon&#xff08;现为施耐德电气&#xff09;在1979年首次发布。它广泛应用于各种工业自动化系统中&#xff0c;尤其是在PLC&#xff08;可编程逻辑控制器&#xff09;与其他设备之间的通信。Modbus的主要特点是其简单性和开放…

API 架构(RPC风格、RESTful风格)

API 架构风格笔记 RPC风格的接口案例RESTful风格的接口案例两者比对 RPC风格的接口案例 RPC&#xff08;Remote Procedure Call&#xff0c;远程过程调用&#xff09;风格的接口设计主要关注于远程服务调用的过程&#xff0c;而不强调资源的表述。以下是RPC风格接口的一个案例…

FAT32文件系统详细分析 (格式化SD nandSD卡)

FAT32 文件系统详细分析 (格式化 SD nand/SD 卡) 目录 FAT32 文件系统详细分析 (格式化 SD nand/SD 卡)1. 前言2.格式化 SD nand/SD 卡3.FAT32 文件系统分析3.1 保留区分析3.1.1 BPB(BIOS Parameter Block) 及 BS 区分析3.1.2 FSInfo 结构扇区分析3.1.3 引导扇区剩余扇区3.1.4 …

为什么自动驾驶技术的实现离不开4G+5G多卡聚合?

如今&#xff0c;汽车制造商和零部件巨头都在研究自动驾驶相关技术。要实现汽车的自动驾驶&#xff0c;不乏相关技术与道路环境的结合和变化。但要实现这一目标&#xff0c;最重要的环节无疑是建设网络。 在4G时代&#xff0c;随着网络带宽和速度的提高&#xff0c;可以实现实…

佰朔资本:未来钢铁行业产业格局有望稳中趋好

组织指出&#xff0c;未来钢铁作业工业格式有望稳中趋好&#xff0c;叠加当时部分公司已经处于价值小看区域&#xff0c;现阶段仍具结构性出资机会&#xff0c;尤其是拥有较高毛利率水平的优特钢企业和本钱管控力度强、具有规划效应的龙头钢企&#xff0c;未来存在估值修改的机…

zabbix6.4连接钉钉发出警告

zabbix6.4配置钉钉告警 注册钉钉 建一个内部群 添加自定义机器人 配置zabbix服务端 打开脚本告警的配置 # vim /etc/zabbix/zabbix_server.conf AlertScriptsPath/usr/lib/zabbix/alertscripts 准备脚本 安装一个依赖包 # dnf -y install python3-requests # vim /usr/li…

PCB电路板缺陷-目标检测数据集(包括VOC格式、YOLO格式)

PCB电路板缺陷-目标检测数据集&#xff08;包括VOC格式、YOLO格式&#xff09; 数据集&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1tm2ArZc4_rBA5NGFNDUPgA?pwd19iz 提取码&#xff1a;19iz 数据集信息介绍&#xff1a; 共有 1232 张图像和一一对应的标注文件 …