OrangePi AIpro学习3 —— vscode开发昇腾DVPP程序

目录

一、VScode配置

1.1 下载和安装

1.2 安装和配置需要的插件

二、构建项目

2.1 项目架构

2.2 解决代码高亮显示

2.3 测试编译

2.4 总结出最简单的代码

2.5 vscode报错找不到头文件解决方法

三、代码简单讲解

3.1 初始化部分

3.2 拷贝数据到NPU显存中

3.3 准备裁剪区域

3.4 准备输入输出描述符

3.5 收尾阶段

3.6 其他历程如何学习


一、VScode配置

1.1 下载和安装

1. 下载地址

Download Visual Studio Code - Mac, Linux, Windows

2. 安装中要注意的内容

1.2 安装和配置需要的插件

1. chinese中文

如果在这里安装不了的话,可以使用离线安装的方式,首先进入官网下载离线插件包:Extensions for Visual Studio family of products | Visual Studio Marketplace

然后在vscode中这么操作,选择刚刚下载的VSIX文件即可完成安装

2. 安装远程工具Remote

设置要连接的主机,然后两次回车

重启vscode

选择linux然后再输入密码

进入文件夹

二、构建项目

2.1 项目架构

data数据是从上面的crop中拿过来的 

cmake_minimum_required(VERSION 3.22)# 设置变量PROJECT_NAME的值为crop
set(PROJECT_NAME "crop")# 设置项目名称为crop
project(${PROJECT_NAME})# 添加dvpp需要的宏定义,相当于在代码中: #define ENABLE_DVPP_INTERFACE
add_definitions(-DENABLE_DVPP_INTERFACE)# 设置头文件目录
include_directories($ENV{INSTALL_DIR}/runtime/include/./include
)# 设置动态库目录
link_directories($ENV{INSTALL_DIR}/runtime/lib64/stub
)# 查找所有CMakeLists.txt所在目录下,src目录中的.c和.cpp文件
file(GLOB_RECURSE CPP_FILES     ${PROJECT_SOURCE_DIR}/src/*.c${PROJECT_SOURCE_DIR}/src/*.cpp
)# 编译输出的文件名为: crop  依赖src目录下的所有.c和.cpp结尾的文件
add_executable(${PROJECT_NAME}${CPP_FILES}
)target_link_libraries(${PROJECT_NAME}ascendcl acl_dvppstdc++
)

2.2 解决代码高亮显示

CMake或者cpp没有高亮显示就上传vsix文件到香橙派

然后在vscode中安装 

2.3 测试编译

提示要增加一行内容,这行内容指定了CMake需要的最小版本

 然后./crop就能运行程程序了

2.4 总结出最简单的代码

#include <iostream>
#include <memory>
#include <dirent.h>
#include <fstream>
#include "acl/acl.h"
#include "acl/ops/acl_dvpp.h"using namespace std;typedef struct PicDesc {string picName;int left;int top;int width;int height;
} PicDesc;aclrtContext g_context;
aclrtStream g_stream;
aclrtRunMode g_runMode;
acldvppChannelDesc * g_dvppChannelDesc;char * ReadBinFile(string fileName, uint32_t &fileSize)
{ifstream binFile(fileName, ifstream::binary);if (binFile.is_open() == false) {printf("Open file %s failed.\n", fileName.c_str());return nullptr;}binFile.seekg(0, binFile.end);uint32_t binFileBufferLen = binFile.tellg();if (binFileBufferLen == 0) {printf("Binfile is empty, filename is %s.\n", fileName.c_str());binFile.close();return nullptr;}binFile.seekg(0, binFile.beg);char * binFileBufferData = new(nothrow) char[binFileBufferLen];if (binFileBufferData == nullptr) {printf("Malloc binFileBufferData failed.\n");binFile.close();return nullptr;}binFile.read(binFileBufferData, binFileBufferLen);binFile.close();fileSize = binFileBufferLen;return binFileBufferData;
}uint32_t AlignmentHelper(uint32_t origSize, uint32_t alignment)
{if (alignment == 0) {return 0;}uint32_t alignmentH = alignment - 1;return (origSize + alignmentH) / alignment * alignment;
}uint32_t SaveDvppOutputData(const char *fileName, const void *devPtr, uint32_t dataSize)
{FILE * outFileFp = fopen(fileName, "wb+");if (g_runMode == ACL_HOST) {void * hostPtr = nullptr;aclrtMallocHost(&hostPtr, dataSize);aclrtMemcpy(hostPtr, dataSize, devPtr, dataSize, ACL_MEMCPY_DEVICE_TO_HOST);fwrite(hostPtr, sizeof(char), dataSize, outFileFp);(void)aclrtFreeHost(hostPtr);} else {fwrite(devPtr, sizeof(char), dataSize, outFileFp);}fflush(outFileFp);fclose(outFileFp);return 0;
}int main()
{/* * 初始化device、context、stream和dvppChannelDesc,并获取运行模式*/aclInit("./src/acl.json");aclrtSetDevice(0);aclrtCreateContext(&g_context, 0);aclrtCreateStream(&g_stream);g_dvppChannelDesc = acldvppCreateChannelDesc(); // 创建图像数据处理通道时的通道描述信息 g_dvppChannelDesc is acldvppChannelDesc typeacldvppCreateChannel(g_dvppChannelDesc);        // 创建图像数据处理通道aclrtGetRunMode(&g_runMode);/* * 读取输入和输出*/// 下面设置输入图片的信息,图片的left和top参数用不到PicDesc inPicDesc  = { "./data/wood_rabbit_1024_1068_nv12.yuv", 0, 0, 1024, 1068 };// 下面设置输出图片的信息PicDesc outPicDesc = { "./output/dvpp_rabbit_224_224_nv12.yuv", 350, 280, 224, 224 };/* * 根据输入读取文件到NPU显存中*/uint32_t buffSize = 0;// inPicDesc.picName:要读取的文件名     buffSize:得到的文件大小    inputBuff:得到的文件的内容char * inputBuff = ReadBinFile(inPicDesc.picName, buffSize);void * inputBufferDev = nullptr;acldvppMalloc(&inputBufferDev, buffSize);     // 在NPU上分配显存if (g_runMode == ACL_HOST)  // 如果本段C++代码是运行在CPU上{                           // 把数据从内存条拷贝到NPU的显存中aclrtMemcpy(inputBufferDev, buffSize, inputBuff, buffSize, ACL_MEMCPY_HOST_TO_DEVICE);} else                        // 如果本段C++代码是运行在NPU中的CPU上{                           // 把数据从NPU显存拷贝到NPU显存中aclrtMemcpy(inputBufferDev, buffSize, inputBuff, buffSize, ACL_MEMCPY_DEVICE_TO_DEVICE);}delete[] inputBuff;/** 设置要裁剪输入图片的区域*/uint32_t outputWidth = AlignmentHelper(outPicDesc.width, 16);   // 输入图片宽度按照16字节对其,例如输入17,得到的outputWidth就是32uint32_t outputHeight = AlignmentHelper(outPicDesc.height, 2);  // 输入图片高度按照2字节对其,例如输入13,得到的outputHeight就是2uint32_t cropLeftOffset = outPicDesc.left;                      // must 偶数uint32_t cropRightOffset = cropLeftOffset + outputWidth - 1;    // must 奇数uint32_t cropTopOffset = outPicDesc.top;                        // must 偶数uint32_t cropBottomOffset = cropTopOffset + outputHeight - 1;   // must 奇数acldvppRoiConfig * cropArea_ = acldvppCreateRoiConfig(cropLeftOffset, cropRightOffset, cropTopOffset, cropBottomOffset);/** 创建输入图片描述符,并填写输入图片信息*/uint32_t inputWidthStride = AlignmentHelper(inPicDesc.width, 16);uint32_t inputHeightStride = AlignmentHelper(inPicDesc.height, 2);uint32_t inputBufferSize = inputWidthStride * inputHeightStride * 3 / 2;acldvppPicDesc * vpcInputDesc_ = acldvppCreatePicDesc();            // 创建入参数描述符acldvppSetPicDescData(vpcInputDesc_, inputBufferDev);               // 原图片存放位置的指针acldvppSetPicDescFormat(vpcInputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420);    // 原图片的格式acldvppSetPicDescWidth(vpcInputDesc_, inPicDesc.width);             // 原图片宽度acldvppSetPicDescHeight(vpcInputDesc_, inPicDesc.height);           // 原图片高度acldvppSetPicDescWidthStride(vpcInputDesc_, inputWidthStride);      // 原图片对齐后的宽度acldvppSetPicDescHeightStride(vpcInputDesc_, inputHeightStride);    // 原图片对齐后的高度acldvppSetPicDescSize(vpcInputDesc_, inputBufferSize);              // 输入图片的图片大小/** 创建输出图片描述符,并填写输出图片信息*/void * outputBufferDev = nullptr;uint32_t outputBufferSize = outputWidth * outputHeight * 3 / 2;acldvppMalloc(&outputBufferDev, outputBufferSize);                  // 在NPU上给输出图片分配内存acldvppPicDesc * vpcOutputDesc_ = acldvppCreatePicDesc();acldvppSetPicDescData(vpcOutputDesc_, outputBufferDev);             // 输出图片存放位置指针acldvppSetPicDescFormat(vpcOutputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420);   // 输出图片的格式cout << outPicDesc.width << " " << outPicDesc.height << " " << outputWidth << " " << outputHeight << endl;acldvppSetPicDescWidth(vpcOutputDesc_, outPicDesc.width);           // 输出图片的宽acldvppSetPicDescHeight(vpcOutputDesc_, outPicDesc.height);         // 输出图片的高acldvppSetPicDescWidthStride(vpcOutputDesc_, outputWidth);          // 输出图片对齐后的宽acldvppSetPicDescHeightStride(vpcOutputDesc_, outputHeight);        // 输出图片对齐后的高acldvppSetPicDescSize(vpcOutputDesc_, outputBufferSize);/** 图片的处理和保存*/// 由g_stream这个流水线来运行处理程序acldvppVpcCropAsync(g_dvppChannelDesc, vpcInputDesc_, vpcOutputDesc_, cropArea_, g_stream);// 等待g_stream流水线完成aclrtSynchronizeStream(g_stream);// 保存图片SaveDvppOutputData(outPicDesc.picName.c_str(), outputBufferDev, outputBufferSize);/** 释放占用的资源*/acldvppFree(outputBufferDev);acldvppDestroyRoiConfig(cropArea_);acldvppDestroyPicDesc(vpcInputDesc_);acldvppDestroyPicDesc(vpcOutputDesc_);aclrtDestroyStream(g_stream);aclrtDestroyContext(g_context);aclrtResetDevice(0);aclFinalize();return 0;
}

下图代表裁剪成功了

注意:在使用上面代码的时候,我修改了其他的尺寸,输出的图片就是不正确的,所以我现在也没完全搞懂库函数要求传入的尺寸。但是后续我会持续更新,分享我的学习心得

2.5 vscode报错找不到头文件解决方法

按 F1 或 Ctrl+Shift+p 在弹出的备选选项中选择 C/C++:Edit Configurations (JSON),自动打开c_cpp_properties.json配置文件

 在.vscode中添加头文件搜索路径的json,然后配置路径

三、代码简单讲解

下面的内容全是我个人的理解,如果有佬,感谢在评论区指正,我会及时修改文章

3.1 初始化部分

88行:aclInit("./src/acl.json");

● 一个进程内只能调用一次aclInit接口。使用AscendCL接口开发应用时,必须先调用aclInit接口,否则可能会导致后续系统内部资源初始化出错,进而导致其它业务异常。

● 输入参数是一个json格式的文件。json里面可以写啥,这个可以看官方文档:

aclInit-系统配置-AscendCL API(C&C++)-应用开发接口-CANN社区版8.0.RC2.alpha001开发文档-昇腾社区 (hiascend.com)

目前我是在json里面就写了一对花括号:{}

● 在进程的最后,要成对的使用 aclFinalize(); 函数,做收尾工作

89行:aclrtSetDevice(0);

● 我的理解就是类似于下图(下图是我的猜想,并不来自于官方),一个板子上面有3块昇腾310B的芯片。上面的代码就是设置本线程(一个进程有一个主线程)使用索引为0的昇腾芯片

● 查看官方的device操作发现,aclrtGetDeviceCount函数可以获取Device的数量,我觉得如果有3个昇腾芯片,可能返回的是3。目前香橙派上面只有一个昇腾310B芯片,返回的就是1

hiascend.com/doc_center/source/zh/canncommercial/601/inferapplicationdev/aclcppdevg/aclcppdevg_03_0019.html

● 在线程的最后,要成对的使用aclrtResetDevice(0);函数,做收尾工作

90行:aclrtCreateContext(&g_context, 0);

● 香橙派上是昇腾310B芯片,昇腾310B芯片device中默认存在1个context,本程序中可创建也可不创建context

● 猜测context可能不是一个实体的内容,而是stream集合的一个概念,几个stream组成一个context

● 本程序就是在昇腾设备0上创建了一个context。创建好了以后,本线程就默认使用新创建的context。当前线程在同一时刻内只能使用其中一个Context

●  在线程的最后,要成对的使用aclrtDestroyContext(g_context);函数,做收尾工作

91行:aclrtCreateStream(&g_stream);

● 用于给当前context创建一个stream。上图昇腾310B芯片硬件资源最多支持1024个stream

● 一个stream可以用于执行一个任务,本案例中使用这个stream执行了图片裁剪指定区域的任务。多个stream可以用于同时执行多个任务

● 在线程的最后,要成对的使用aclrtDestroyStream(g_stream);函数,做收尾工作

92、93行:g_dvppChannelDesc = acldvppCreateChannelDesc();
acldvppCreateChannel(g_dvppChannelDesc);

● 这个是图片处理函数必须要的一个参数,在这里提前准备一下
acldvppVpcCropAsync(g_dvppChannelDesc, vpcInputDesc_, vpcOutputDesc_, cropArea_, g_stream);

94行:aclrtGetRunMode(&g_runMode);

● 获取当前程序的运行模式

ACL_DEVICE:昇腾AI软件栈运行在Device的Control CPU或板端环境上
ACL_HOST:昇腾AI软件栈运行在Host CPU上

如果是ACL_DEVICE,就代表当前程序是在AI CPU上运行的;如果是ACL_HOST,就代表当前程序是在CPU上运行的,如下图红色圆圈中写的那样

3.2 拷贝数据到NPU显存中

● 111行,通过ReadBinFile函数读取数据到内存中。如果程序是运行在CPU上,那么读取到的数据就放在运行内存上;如果程序是运行在昇腾芯片的AI CPU上,那么读取到的数据就放在昇腾芯片的内存,也就是显存上

● 114行,在显存上分配一段文件大小的内存。

● 115行,做判断。如果程序是运行在CPU上,就要把数据从运行内存上搬运到显存上;如果程序是运行在昇腾芯片的AI CPU上,那么就把数据从显存搬运到显存上(这里也可以不搬运,主要是为了后面写库的时候使用)

在运行内存上对应的就是HOST,到显存上对应的就是TO_DEVICE;在显存上对应的就是DEVICE,到显存上对应的就是TO_DEVICE。同理也可以HOST_TO_HOST,DEVICE_TO_HOST。

补充一下:如果程序运行在昇腾AI CPU上,想要在运行内存中分配内存,就需要使用aclrtMallocHost来申请内存

3.3 准备裁剪区域

上面分别生成了cropArea_,用于告诉下面的函数,输入图片要裁剪的范围

acldvppVpcCropAsync(g_dvppChannelDesc, vpcInputDesc_, vpcOutputDesc_, cropArea_, g_stream);

因为我对芯片对齐等不是很了解,也不清楚这里到底怎么实现随便裁剪,这里我就不分析了,等后面学习完了再来补充,有知道怎么使用的佬也可以在评论区帮助我一下

3.4 准备输入输出描述符

● 143行,为什么输入的图片大小是这么计算的呢

首先是对齐后图片的宽高如下图,前面这部分很好理解:

● 后面的*3,是因为存储图片的是YUV 3个通道,这样就需要 对齐后的宽高*3得到需要的字节数,

● 那么为什么要除2呢?这是因为YUV本来YUV三个通道都应该有4个信息,也就是8bit来表示Y,8bit来表示U,8bit来表示V。YUV420图片,有8bit来表示Y,只有4bit来表述U,0bit来表示V,字节数自然就少了一半,所以要/2

3.5 收尾阶段

在图片处理的过程中,程序可以异步的去执行了,但是176行,我们任然阻塞的去等待图片处理结果。等到图片处理结束,就将输出的图片指针和大小传入181行的函数中,函数会将图片保存到picName指定的位置去

做完上面这些任务,就释放掉占用的资源

3.6 其他历程如何学习

把其他历程中的关键内容抽取出来,写成像上面那种形式的程序,多动手练习。自己依次分析函数的功能等。

上面代码过程分为这几步:初始化->读取要处理的图片->创建输入图片描述符->创建输出图片描述符->acl处理图片->把输出的图片保存->释放占用的资源

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python-报数(赛氪OJ)

[题目描述] 有 n 人围成一圈&#xff0c;顺序排号。 从第 1 个人开始报数&#xff08;从 1 到 3 报数&#xff09;&#xff0c;凡是报到 3 的人退出圈子&#xff0c;问最后留下的是原来的第几号的那位。输入格式&#xff1a; 初始人数 n 。输出格式&#xff1a; 最后一人的初始…

python游戏开发之五子棋游戏制作

五子棋是一种源自中国的传统棋类游戏&#xff0c;起源可以追溯到古代。它是一种两人对弈的游戏&#xff0c;使用棋盘和棋子进行。棋盘通常是一个 1515 的网格&#xff0c;棋子分为黑白两色&#xff0c;双方轮流在棋盘上落子。游戏的目标是通过在棋盘上落子&#xff0c;使自己的…

【深度学习】基于YOLOV5模型的图像识别-目标检测的性能指标详解与计算方法

目标检测是计算机视觉中的重要任务&#xff0c;主要目的是在图像中识别并定位特定的物体。YOLO&#xff08;You Only Look Once&#xff09;系列模型作为目标检测领域的代表性方法之一&#xff0c;凭借其高效和准确的特点&#xff0c;广泛应用于实际场景中。本文通过详细介绍目…

C++之移动语义与左值右值深入学习:从入门到精通!

简介 本文详细阐述了 C 中关于移动语义、左值右值等技术的基本概念和常用技巧。 问题的产生 每一项技术的诞生都是为了解决某一个问题&#xff0c;移动语义、左值右值也是一样&#xff0c;因此我们先来看看问题产生的背景。 先来看一段代码&#xff1a; #include <iost…

JavaEE: Thread类

Thread的常见构造方法 Thread的常见属性 ID 是线程的唯一标识,不同线程不会重复名称是在使用各种调试工具时会用到的状态表示线程当前所处的情况优先级高的线程理论上来说更容易被调度到关于后台线程,需要记住:JVM会在一个进程的所有非后台线程结束后,才会结束运行是否存活,即r…

社交及时通讯平台完整版源码,uniapp技术,可打包成app

源码简介&#xff1a; 全原生&#xff0c;从底层开始结构就完全不一样&#xff0c;mongodb的库&#xff0c;uniapp混编手端&#xff0c;二开难度要比视酷或者酷信容易很多。全开源&#xff0c;带开发文档。前端用的是uniapp技术&#xff0c;所以是多端合一&#xff0c;可以做h…

【JVM基础14】——垃圾回收-强引用、软引用、弱引用、虚引用的区别

目录 1- 引言&#xff1a;为什么分多种引用类型2- ⭐核心&#xff1a;2-1 强引用2-2 软引用2-3 弱引用2-4 虚引用 3- 小结&#xff1a;3-1 强引用、软引用、弱引用、虚引用的区别&#xff1f; 1- 引言&#xff1a;为什么分多种引用类型 在 Java 中&#xff0c;引用类型被分为强…

linux 6.10.0 CXL/reg.c 详解

文章目录 前言Ref正文1. cxl_setup_regs2. cxl_probe_regs()3. cxl_probe_component_regs()4. cxl_probe_device_regs()5. cxl_map_device_regs()6. cxl_count_regblock() / cxl_find_regblock_instance() 前言 CXL 是一个比较新的技术&#xff0c;内核版本迭代太快&#xff0…

python爬虫预备知识三-多进程

python实现多进程的方法&#xff1a;fork、multiprocessing模块创建多进程。 os.fork方法 os.fork方法只适合于unix/linux系统&#xff0c;不支持windows系统。 fork方法调用一次会返回两次&#xff0c;原因在于操作系统将当前进程&#xff08;父进程&#xff09;复制出一份…

在Linux中,什么叫做线程

在Linux中&#xff0c;什么叫做线程&#xff1f; CPU调度的基本单位。 在Linux中&#xff0c;什么叫做进程&#xff1f; 内核视角&#xff1a; 承担分配系统资源的基本实体。 一个进程内部可以有多个执行流。 task_struct可以理解为轻量级进程。 线程是进程内部的一个分支…

【python】Python中位运算算法详细解析与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

数据库扩展新篇章:主流分库分表中间件全解析

摘要&#xff1a; 随着企业数据量的激增&#xff0c;传统的单体数据库架构已经无法满足日益增长的性能需求和数据管理复杂性。分库分表技术作为解决这一问题的有效手段&#xff0c;通过将数据水平或垂直地分散到多个数据库中&#xff0c;提高了系统的扩展性和处理能力。本文将详…

LISA: Reasoning Segmentation via Large Language Model

发表时间&#xff1a;CVPR 2024 论文链接&#xff1a;https://openaccess.thecvf.com/content/CVPR2024/papers/Lai_LISA_Reasoning_Segmentation_via_Large_Language_Model_CVPR_2024_paper.pdf 作者单位&#xff1a;CUHK Motivation&#xff1a;尽管感知系统近年来取得了显…

鸡舍环控系统

在现代养殖业中&#xff0c;科技的进步正悄然改变着传统模式&#xff0c;其中&#xff0c;基于物联网和自动控制技术的鸡舍环控系统正逐渐成为行业内的新宠。这套系统不仅能够集成温湿度、光照度等参数的监测与控制&#xff0c;还能通过智能化手段减轻日常养殖工作量&#xff0…

探索 Python 异步通信的奥秘:WebSockets 库的神奇之旅

文章目录 探索 Python 异步通信的奥秘&#xff1a;WebSockets 库的神奇之旅背景&#xff1a;为何选择 WebSockets&#xff1f;什么是 websockets 库&#xff1f;安装 websockets 库5个简单的库函数使用方法场景应用示例常见问题与解决方案总结 探索 Python 异步通信的奥秘&…

sqli-labs-php7-master\Less-1

1&#xff0c;进入mysql数据库 mysql -u root -p 接着&#xff1a; show databases; use security; select * from where id1 LIMIT 0,1; 函数的基本用法 system_user() #当前系统用户 user() #当前登录用户 current_user() #当前登录用…

electron-updater实现electron全量更新和增量更新——主进程部分

同学们可以私信我加入学习群&#xff01; 正文开始 前言更新功能所有文章汇总一、更新插件选择二、在main.js中引入我们的更新模块三、更新模块UpdateController.js暴露的方法checkUpdate四、更新模块UpdateController.js中的监听4.1监听是否有新版本需要更新&#xff1f;4.2 监…

怎样配置虚拟机IP

目录&#xff08;三步走&#xff09; 配置本机IP 配置虚拟机外部IP 配置虚拟机内部IP 参考链接&#xff1a; 配置本机IP 打开“网络和共享中心”——>更改“适配器设置” 找到“VMnet8”&#xff0c;然后右键“属性”&#xff0c;弹出下列窗口 输入本机IP&#xff08;你…

浅谈操作系统

我们前面谈到了一个可执行程序首先会到内存进行预先加载~而在我们的计算机中第一个被加载的软件就是操作系统~ 操作系统的主要工作就是对软硬件资源进行管理~ 这里我们先从操作系统下层开始讲起~ 我们把操作系统类比为校长&#xff0c;驱动程序类比为辅导员&#xff0c;底层硬件…

【学术会议征稿】第四届电气工程与计算机技术国际学术会议(ICEECT2024)

第四届电气工程与计算机技术国际学术会议&#xff08;ICEECT2024&#xff09; 2024 4th International Conference on Electrical Engineering and Computer Technology 第四届电气工程与计算机技术国际学术会议&#xff08;ICEECT2024&#xff09;将于9月27日-29日在哈尔滨举…