Opencv-C++笔记 (7) : opencv-文件操作XML和YMAL文件

news2024/11/20 1:28:31

文章目录

  • 一、概述
  • 二、文件操作
  • 三、打开文件
  • 四、写入
  • 五、读写个人源码

一、概述

除了图像数据之外,有时程序中的尺寸较小的Mat类矩阵、字符串、数组等

数据也需要进行保存,这些数据通常保存成XML文件或者YAML文件。本小节中将介绍如何利用OpenCV
4中的函数将数据保存成XML文件或者YAML文件以及如何读取这两种文件中的数据。

XML是一种元标记语言,所谓元标记就是使用者可以根据自身需求定义自己的标记,例如可以用、等标记来定义数据的含义,例如用24来表示age数据的数值为24。XML是一种结构化的语言,通过XML语言可以知道数据之间的隶属关系,例如100150表示在color数据中含有两个名为red和blue的数据,两者的数值分别是100和150。通过标记的方式,无论以任何形式保存数据,只要文件满足XML格式,那么读取出来的数据就不会出现混淆和歧义。XML文件的扩展名是“.xml”。

YMAL是一种以数据为中心的语言,通过“变量:数值”的形式来表示每个数据的数值,通过不同的缩进来表示不同数据之间的结构和隶属关系。YMAL可读性高,常用来表达资料序列的格式,它参考了多种语言,包括XML、C语言、Python、Perl等。YMAL文件的扩展名是“.ymal”或者“.yml”。

OpenCV
4中提供了用于生成和读取XML文件和YMAL文件的FileStorage类,类中定义了初始化类、写入数据和读取数据等方法。我们在使用该FileStorage类时首先需要对其进行初始化,初始化可以理解为声明需要操作的文件和操作类型。OpenCV
4提供了两种初始化的方法,分别是不输入任何参数的初始化(可以理解为只定义,并未初始化)和输入文件名称和操作类型的初始化。后者初始化构造函数的函数原型在代码清单2-35中给出。

二、文件操作

cv::FileStorage::FileStorage(const String & filename,
                                  int  flags,
                                  const String & encoding = String()
                                  )
  • filename:打开的文件名称。
  • flags:对文件进行的操作类型标志,常用参数及含义在表2-8给出。
  • encodin:编码格式,目前不支持UTF-16 XML编码,需要使用UTF-8 XML编码

在这里插入图片描述

该函数是FileStorage类的构造函数,用于声明打开的文件名称和操作的类型。函数第一个参数是打开的文件名称,参数是字符串类型,文件的扩展名是“.xml”、“.ymal”或者“.yml”。打开得文件可以已经存在或者未存在,但是当对文件进行读取操作时需要是已经存在的文件。第二个参数是对文件进行的操作类型标志,例如对文件进行读取操作、写入操作等,常用参数及含义在表2-8给出,由于该标志量在FileStorage类中,因此在使用时需要加上类名作为前缀,例如“FileStorage::WRITE”。最后一个参数是文件的编码格式,目前不支持UTF-16XML编码,需要使用UTF-8 XML编码,通常情况下使用该参数的默认值即可。

三、打开文件

打开文件后,可以通过FileStorage类中的isOpened()函数判断是否成功打开文件,如果成功打开文件,该函数返回true,如果打开文件失败,则该函数返回false。

FileStorage类中默认构造函数没有任何参数,因此没有声明打开的文件和操作的类型,此时需要通过FileStorage类中的open()函数单独进行声明。

virtual bool cv::FileStorage::open(const String & filename,
                                         int  flags,
                                         const String & encoding = String()
                                         )
  • filename:打开的文件名称。
  • flags:对文件进行的操作类型标志,常用参数及含义在表2-8给出。
  • encodin:编码格式,目前不支持UTF-16 XML编码,需要使用UTF-8 XML编码。

该函数补充了默认构造函数没有声明打开文件的缺点,函数可以指定FileStorage类打开的文件,如果成功打开文件,则返回值为true,否则为false。函数中所有的参数及含义都与代码清单2-35中的相同,因此这里不再进行赘述。同样,通过该函数打开文件后仍然可以通过FileStorage类中的isOpened()函数判断是否成功打开文件。

四、写入

打开文件后,类似C++中创建的数据流,可以通过“<<”操作符将数据写入文件中,或者通过“>>”操作符从文件中读取数据。除此之外,还可以通过FileStorage类中的write()函数将数据写入文件中,该函数的函数原型在代码清单2-37中给出

void cv::FileStorage::write(const String & name,
                   int  val
              )
  • name:写入文件中的变量名称。
  • val:变量值。

该函数能够将不同数据类型的变量名称和变量值写入到文件中。该函数的第一个参数是写入文件中的变量名称。第二个参数是变量值,代码清单2-37中的变量值是int类型,但是在FileStorage类中提供了write()函数的多个重载函数,分别用于实现将double、String、Mat、vector类型的变量值写入到文件中。

使用操作符向文件中写入数据时与write()函数类似,都需要声明变量名和变量值,例如变量名为“age”,变量值为“24”,可以通过“file<<”age”<<24”来实现。如果某个变量的数据是一个数组,可以用“[]”将属于同一个变量的变量值标记出来,例如“file<<”age”<<“[”<<24<<25<<”]””。如果某些变量隶属于某个变量,可以用“{}”表示变量之间的隶属关系,例如“file<<”age”<<“{”<<”Xiaoming”<<24<<”Wanghua”<<25<<”}””。

读取文件中的数据时,只需要通过变量名就可以读取变量值。例如“file [“x”] >> xRead”是读取变量名为x的变量值。但是,当某个变量中含有多个数据或者含有子变量时,就需要通过FileNode节点类型和迭代器FileNodeIterator进行读取,例如某个变量的变量值是一个数组,首先需要定义一个file [“age”]的FileNode节点类型变量,之后通过迭代器遍历其中的数据。另外一种方法可以不使用迭代器,通过在变量后边添加“[]”地址的形式读取数据,例如FileNode[0]表示数组变量中的第一个数据,FileNode[“Xiaoming”]表示“age”变量中的“Xiaoming”变量的数据,依次向后添加“[]”地址实现多节点数据的读取。

为了了解如何生成和读取XML文件和YMAL文件,在代码清单2-38中给出了实现文件写入和读取的示例程序。程序中使用write()函数和“<<”操作符两种方式向文件中写入数据,使用迭代器和“[]”地址两种方式从文件中读取数据。数据的写入和读取方法在前面已经介绍,在代码清单2-38中需要重点了解如何通过程序实现写入与读取。程序生成的XML文件和YMAL文件中的数据在图2-10给出,读取文件数据的结果在图2-9给出。

#include<iostream>
#include<vector>
#include<string>
//#include <stdio.h>
#include <opencv2/opencv.hpp>
#include "opencv/highgui.h"

using namespace std;
using namespace cv;

int main(int argc,char** argv) {
    cout<<"OpenCv Version: "<<CV_VERSION<<endl;
    string filename="datas.yaml";//文件的名称
    FileStorage fwriter(filename,FileStorage::WRITE);

    //存入矩阵Mat类型的数据
    Mat mat=Mat::eye(3,3,CV_8U);
    fwriter.write("mat",mat);//使用write()函数写入数据
    //存入浮点型数据,节点名称为x
    float x=100;
    fwriter<<"x"<<x;
    //存入字符串类型数据,节点名称为str
    string str="Learn OpenCV 4";
    fwriter<<"str"<<str;
    //存入数组,节点名称为number_array
    fwriter<<"number_array"<<"["<<4<<5<<6<<"]";
    //存入多node节点数据,主名为multi_nodes
    fwriter<<"multi_nodes"<<"{"<<"month"<<8<<"day"<<28<<"year"
           <<2020<<"time"<<"["<<0<<1<<2<<3<<"]"<<"}";
    //关闭文件
    fwriter.release();

    //以读取的模式打开文件
    FileStorage fread(filename,FileStorage::READ);
    //判断是否打开成功
    if(!fread.isOpened()){
        cout<<"打开文件失败,请确认文件名称是否正确!"<<endl;
        return -1;
    }
    //读取文件中的数据
    float xRead;
    fread["x"]>>xRead;//读取浮点型数据
    cout<<"x = "<<xRead<<endl;

    //读取字符串数据
    string strRead;
    fread["str"]>>strRead;
    cout<<"str = "<<strRead<<endl;

    //读取含多个number_array节点
    FileNode fileNode=fread["number_array"];
    cout<<"number_array = [";
    for(FileNodeIterator i=fileNode.begin();i!=fileNode.end();++i){
        float a;
        *i>>a;
        cout<<a<<" ";
    }
    cout<<"]"<<endl;

    //读取Mat类型数据
    Mat matRead;
    fread["mat="]>>matRead;
    cout<<"mat = "<<mat<<endl;

    //读取含有多个子节点的节点数据,不使用fileNode和迭代器进行读取
    FileNode fileNode1=fread["multi_nodes"];
    int month=(int)fileNode1["month"];
    int day=(int)fileNode1["day"];
    int year=(int)fileNode1["year"];
    cout<<"multi_nodes:"<<endl;
    cout<<" month = "<<month<<" day = "<<day<<" year = "<<year<<" time=[";
    for(int i=0;i<4;++i){
        int a=(int)fileNode1["time"][i];
        cout<<a<<" ";
    }
    cout<<"]"<<endl;

    //关闭文件
    fread.release();
    return 0;
}

在这里插入图片描述
在这里插入图片描述

五、读写个人源码

bool    Percipio_mindCalibrationThread::SaveCalibOfflinexml(QVector<QPair< QStringList,TY_CAMERA_CALIB_INFO>>  Datainfo )
 {
     show_message("------------Start Save offline Calibfile-----------------");
        FolderFile_Getfiles->CreateFolder_F(Datainfo[0].first[2]);
    QString  path=Datainfo[0].first[2]+"/CalibrationOffline.xml";

     FolderFile_Getfiles->CreateFile_F(path);
     cv::FileStorage  fwrite(path.toStdString(),cv::FileStorage::WRITE);



        // getALlXYZtSetting.QVerctorQstringList.push_back({{XYZSN,"depth",Path2D+"/3D"},percipioCamera->Get_DepthCalibData()});
     show_message("Save Sn and fodlerpath");

         std::string  sntype="";
         show_message(Datainfo[0].first[2]+"/3D/");
         qDebug()<<Datainfo[0].first[2]+"/3D/"<<endl;
std::vector<std::string>  XYZimagefiles=FolderFile_Getfiles->GetFloderAllFile(Datainfo[0].first[2]+"/3D/");
if(XYZimagefiles.size()>3)
{

    std::pair<std::string,std::string>  ImageReslotion=FolderFile_Getfiles->GetImageResolution(Datainfo[0].first[2]+"/3D/");
             sntype="DepthCamera";
             fwrite<<sntype<<"{:";
             fwrite<<"SN"<<(Datainfo[0].first[0]).toStdString();

             fwrite<<"Width"<< atoi(ImageReslotion.first.c_str());
             fwrite<<"Height"<<atoi(ImageReslotion.second.c_str());

      cv::Mat Depthintrinsic=cv::Mat(3, 3, CV_32F,Datainfo[0].second.intrinsic.data);
      cv::Mat Depthextrinsic=cv::Mat(4, 4, CV_32F,Datainfo[0].second.extrinsic.data);
      cv::Mat Depthdistortion=cv::Mat(12, 1, CV_32F,Datainfo[0].second.distortion.data);
     fwrite<<"IntrinsicWidth"<<atoi(std::to_string(Datainfo[0].second.intrinsicWidth).c_str());
     fwrite<<"IntrinsicHeight"<<atoi(std::to_string(Datainfo[0].second.intrinsicHeight).c_str());;
     fwrite<<"Intrinsic"<<Depthintrinsic;
     fwrite<<"Extrinsic"<<Depthextrinsic;
     fwrite<<"Distortion"<<Depthdistortion;
     fwrite<<"}";
     fwrite<<"FilePath"<<(Datainfo[0].first[2]).toStdString();
//      int  Patternwidth;
//      int Patternheight;
//     float Sqyaresize;
       fwrite<<"Patternwidth"<<Patternwidth;
        fwrite<<"Patternheight"<<Patternheight;
         fwrite<<"Sqyaresize"<<Sqyaresize;
     fwrite.release();
  show_message("offline xml save OK");
       return true;
}
else

{

    show_message("Please checke the folder files path setting and mustbe three  images  at least,offline xml save NG");
         return false;
 }
 }
 std::vector<cameraInfo>    Percipio_mindCalibrationThread::ReadCalibOfflinexml(QString Path )
 {
     std::vector<cameraInfo>  offlineVector;
     show_message("----------------Read offline configxml--------------------");
   cv::FileStorage fs(Path.toStdString()+"/CalibrationOffline.xml",cv::FileStorage::READ);
if(!fs.isOpened())
{
    show_message("Cannot open the CalibrationOffline.xml,NG");
    return offlineVector;

}

cameraInfo  cameraDepth;
cameraInfo  cameraColor;
std::string Filepath;
fs["FilePath"]>>Filepath;
cv::FileNode  node=fs["DepthCamera"];


std::vector<std::string>  filepath=FolderFile_Getfiles->GetFloderAllFile(Path+"/3D/");

TY_CAMERA_CALIB_INFO calibDepth;
calibDepth.intrinsicWidth=(int)node["IntrinsicWidth"];
calibDepth.intrinsicHeight=(int)node["IntrinsicHeight"];
 show_message("Get Patter info");
  fs["Patternwidth"]>>     Patternwidth;
  fs["Patternheight"]>>   Patternheight;
   fs["Sqyaresize"]>>   Sqyaresize;
    show_message("Patternwidth: "+QString::number(Patternwidth));
       show_message("Patternheight: "+QString::number(Patternheight));
          show_message("Sqyaresize: "+QString::number(Sqyaresize));
cv::Mat Depthintrinsic(3, 3, CV_32F,cv::Scalar(0));
cv::Mat Depthextrinsic(4, 4, CV_32F,cv::Scalar(0));
cv::Mat Depthdistortion(12, 1, CV_32F,cv::Scalar(0));
 show_message("Read Depth info");
node["Intrinsic"]>>Depthintrinsic;
node["Extrinsic"]>>Depthextrinsic;
node["Distortion"]>>Depthdistortion;
//calibDepth.intrinsic.data
memcpy((uchar*)calibDepth.intrinsic.data,Depthintrinsic.data,sizeof (float) * 9);
memcpy((uchar*)calibDepth.extrinsic.data,Depthextrinsic.data,sizeof (float) * 16);
memcpy((uchar*)calibDepth.distortion.data,Depthdistortion.data,sizeof (float) * 12);
/*
int Intrinsicindex=0;
int Extrinsicindex=0;
int Distortionindex=0;
for(int i=0;i<3;i++)
{
    for(int j=0;j<3;j++)
    {
     calibDepth.intrinsic.data[Intrinsicindex] = Depthintrinsic.at<float>(i,j);
     show_message(QString::number(calibDepth.intrinsic.data[Intrinsicindex] ));
       Intrinsicindex++;
    }
}
for(int i=0;i<4;i++)
{
    for(int j=0;j<4;j++)
    {
     calibDepth.extrinsic.data[Extrinsicindex] = Depthextrinsic.at<float>(i,j);
     show_message(QString::number(calibDepth.extrinsic.data[Extrinsicindex]));
       Extrinsicindex++;
    }
}
for(int i=0;i<12;i++)
{
    for(int j=0;j<1;j++)
    {
     calibDepth.distortion.data[Distortionindex] = Depthintrinsic.at<float>(i,j);
    show_message(QString::number(calibDepth.distortion.data[Distortionindex]));
       Distortionindex++;
    }
}
*/
 cameraDepth.sn=(std::string)node["SN"];
show_message("Read Depth sn:"+QString::fromStdString(cameraDepth.sn));
cameraDepth.files=filepath;
cameraDepth.compont="depth";
cameraDepth.cali=calibDepth;
offlineVector.push_back(cameraDepth);


    std::vector<std::string>  colorfilepath=FolderFile_Getfiles->GetFloderAllFile(Path+"/2D/");
    TY_CAMERA_CALIB_INFO calibcolor;
    cameraColor.sn=FolderFile_Getfiles->GetCameraSn(Path+"/2D/");
    show_message("Read Color sn:"+QString::fromStdString(cameraColor.sn));
    cameraColor.files=colorfilepath;
    cameraColor.compont="color";
    cameraColor.cali=calibcolor;
offlineVector.push_back(cameraColor);
 show_message("Read  config.xml  complete");
     return  offlineVector;


 }

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

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

相关文章

前端实现消息推送、即时通信、http简介

信息推送 服务端主动向客户端推送消息&#xff0c;使客户端能够即时接收到信息。 场景 页面接收到点赞&#xff0c;消息提醒聊天功能弹幕功能实时更新数据功能 实现即时通讯方式 短轮询 浏览器&#xff08;客户端&#xff09;每隔一段时间向服务器发送http请求&#xff0c;…

Google为TensorFlow设计的专用集成电路TPU3.0图片

Widrow也是在Minsky的影响下进入AI领域的&#xff0c;后来加入斯坦福大学任教。他在1960年提出了自适应线性单元&#xff08;Adaline&#xff09;&#xff0c;一种和感知器类似的单层神经网络&#xff0c;用求导数方法来调整权重&#xff0c;所以说有“三十年神经网络经验”并不…

CI/CD 流水线 (FREE)

流水线是持续集成、交付和部署的顶级组件。 流水线包括&#xff1a; 工作&#xff0c;定义做什么。例如&#xff0c;编译或测试代码的作业。阶段&#xff0c;定义何时运行作业。例如&#xff0c;在编译代码的阶段之后运行测试的阶段。 作业由 runners 执行。如果有足够多的并…

Qt编写视频监控系统79-四种界面导航栏的设计

一、前言 最初视频监控系统按照二级菜单的设计思路&#xff0c;顶部标题栏一级菜单&#xff0c;左侧对应二级菜单&#xff0c;最初采用图片在上面&#xff0c;文字在下面的按钮方式展示&#xff0c;随着功能的增加&#xff0c;二级菜单越来越多&#xff0c;如果都是这个图文上…

openGauss数据库安装,配置连接 完整版Centos7

服务器版本&#xff1a;Centos7.6 || 7.9 数据库版本&#xff1a;openGauss-5.0.0-CentOS-64bit.tar.bz2 极简版 目录 修改系统参数安装环境安装openGauss数据库配置连接数据库使用navicat连接数据库 修改系统参数 ##修改 /etc/selinux/config 文件中的“SELINUX”值为“disa…

【网络安全】成功上岸深信服,这套面试题你肯定需要!!!

时间过得很快&#xff0c;回想起去年的这个时候&#xff0c;我也正在准备秋招&#xff0c;今天的我刚刚结束培训。 我的个人情况就读于某双非大学&#xff0c;信息与计算科学&#xff08;大数据方向&#xff0c;校企合作&#xff0c;一个介于数学与计算机之间的专业&#xff0…

移动端H5使用window.open跳转,IOS不生效解决

移动端H5使用window.open跳转&#xff0c;IOS不生效解决 navigator navigator对象&#xff0c;用于提供当前浏览器及操作系统等信息&#xff0c;这些信息都放在navigator的各个属性中。navigator对象也是window对象的成员。 打印navigator对象 userAgent在安卓和IOS的打印结…

Opencv-C++笔记 (9) : opencv-多通道分离和合并

文章目录 一、概论二、多通道分离函数split()三、多通道合并函数merge()四、图像多通道分离与合并例程 一、概论 在图像颜色模型中不同的分量存放在不同的通道中&#xff0c;如果我们只需要颜色模型的某一个分量&#xff0c;例如只需要处理RGB图像中的红色通道&#xff0c;可以…

科技云报道:大模型时代,SaaS元年才真的到来了?

科技云报道原创。 ChatGPT席卷全球后&#xff0c;如果有人问AI大模型影响最大的会是哪个行业&#xff1f;SaaS领域肯定是不二之选。 目前全球各大科技公司已宣称要用大模型触及、整合所有产品。 其中&#xff0c;微软率先为其办公家族装配上了各类copilot&#xff0c;开发者…

Opencv-C++笔记 (10) : opencv-图像像素计算

文章目录 一、概率寻找图像像素的最大值和最小值计算图像的均值和标准方差 一、概率 我们可以将数字图像理解成一定尺寸的矩阵&#xff0c;矩阵中每个元素的大小表示了图像中每个像素的亮暗程度&#xff0c;因此统计矩阵中的最大值&#xff0c;就是寻找图像中灰度值最大的像素…

人机交互学习-7 可视化设计

可视化设计 窗口与菜单窗口窗口Window窗口界面类型 菜单注意事项 对话框模态对话框非模态对话框属性对话框功能对话框进度对话框公告对话框错误对话框消除错误信息 警告对话框确认对话框消除确认对话框 管理对话框内容标签对话框扩展对话框级联对话框 对话框设计原则 控件工具栏…

内部类~~

1&#xff1a;一个类中再定义一个类 2&#xff1a;内部类的使用场景&#xff0c;作用 当一个事物的内部&#xff0c;还有一个部分需要一个完整的结构进行描述&#xff0c;而这个内部的完整结构又只为外部事物提供服务&#xff0c;那么整个内部的完整结构可以选择使用内部类来设…

“大龄”码农的“中年危机”:35岁之后,IT计算机的出路在哪?

前言 对于一个工作不下于4年的人来说&#xff0c;我觉得我有一定的思考沉淀来回答这个问题。 说说我的一些经历吧。 普通一本毕业&#xff0c;专业是自动化&#xff0c;大学由于挂科太多没拿到学位证到上海找实习&#xff0c;一开始做的是开发&#xff0c;从16年到19年都是做…

警惕冒充“数字人民币”诈骗案!

现在大家越来越习惯使用电子支付的方式&#xff0c;数字支付方式的需求也在不断增长。然而一些犯罪嫌疑人却看到了可乘之机&#xff0c;近日&#xff0c;山东菏泽曹县警方破获了一起利用数字人民币&#xff0c;实施诈骗的案件&#xff0c;受骗群众高达上万人。 家住山东菏泽曹…

基于浏览器渲染的组件测试

目录 为什么需要自动化测试 测试的类型 组件测试的方式 白盒测试 黑盒测试 灰盒测试 推荐的方案 Playwright 组件测试案例 Playwright 简介 playwright 架构图 BrowserContext 组件测试原理 组件引入 模型封装 组件渲染测试 组件 Props 测试 组件 Events 测试…

运维数字化转型:用数字化思维重塑运维体系(文末送书五本)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

基于Java学生请假系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

FDTD 时域有限差分数值模拟方法与应用,COMSOL 多场耦合仿真技术与应用

专题一&#xff1a;COMSOL多物理场耦合 &#xff08;一&#xff09;案列应用实操教学&#xff1a; 案例一 光子晶体能带分析、能谱计算、光纤模态计算、微腔腔膜求解 案例二 类比凝聚态领域魔角石墨烯的moir 光子晶体建模以及物理分析 案例三 传播表面等离激元和表面等离…

Cat.4网络DTU,稳定快速的数据传输神器

好兄弟们&#xff01;你们有没有遇到过&#xff0c;半夜在家睡得正香&#xff0c;突然领导一个电话干过来告诉你设备数据传输中断了&#xff0c;让你赶紧看看怎么回事的情况。简直让人崩溃&#xff01; 在现代工业和物联网应用中&#xff0c;数据传输的稳定性和速度对于设备的运…

Python-Inspect.exe-uiautomation-基本操作-获取微信群成员信息

文章目录 1.Inspect.exe2.uiautomation使用2.1.简介和安装2.2.获取微信群成员昵称2.3.常用控件类型2.4.比较通用的属性2.4.窗口常见操作2.5.常见鼠标和键盘操作3.总结1.Inspect.exe 检查 (Inspect.exe) 是一种基于 Windows 的工具,可以选择任何 UI 元素并查看其辅助功能数据。…