OrangePi AIpro 快速上手初体验——接口、样例和目标检测

一、 开发板简介

OrangePi AIpro开发板是香橙派联合华为精心打造的高性能 AI 开发板,其搭载了昇腾 AI 处理器,可提供 8TOPS INT8 的计算能力,内存提供了 8GB 和 16GB两种版本。可以实现图像、视频等多种数据分析与推理计算,可广泛用于教育、机器人、无人机等场景。

下面让我们来具体看看吧:

1.1 开发板全貌

首先看一眼产品的全貌:

img

img

(不得不说,颜值还是很nice的)

1.2 硬件规格

(1)昇腾AI处理器: 搭配华为昇腾310NPU,4核64位Arm处理器+AI处理器
(2)AI算力: 支持半精度(FP16):4 TFLOPS 和 整数精度(INT8):8 TOPS
(3)内存: 8GB 或 16GB
(4)存储: 板载32MB的SPI Flash,提供Micro SD卡插槽和eMMC插座
(5)接口: 接口配置丰富,包括USB3.0、USB Type-C 3.0、Micro USB、HDMI、CSI、DSI、以太网口、40pin扩展口等多种接口
(6)Wifi+蓝牙: 支持2.4G和5G双频WIFI、BT 4.2
(7)操作系统: 支持Ubuntu 22.04 和 openEuler 22.03
(8)外观规格: 107*68mm、82kg
输入npu-smi info 再看一下芯片信息:

img

1.3 接口和引脚图

接口:

img

引脚:

img

二、 开机和使用

2.1 开机准备

由于获得的Orange Pi AIpro 已经烧录了ubuntu的镜像到TF卡,因此选择使用TF卡的启动方式。
而开发板支持从 TF 卡、eMMC 和 SSD(支持 NVMe SSD 和 SATA SSD)启动,通过背面的两个拨码开关(BOOT1和BOOT2)控制

img

两个拨码开关都支持左右两种设置状态,共4种状态,目前开发板使用了其中的3种,不同状态的启动设备如下表所示:

拨码开关BOOT1拨码开关BOOT2对应的启动设备
未使用
SATA SSD和NVMe SSD
eMMC
TF卡

SATA SSD和NVMe SSD通过M2_TYPE引脚的电平自动区分。此外切换拨码开关需要重新插拔电源使启动设备选项生效,复位键不行。

2.2 开机

使用 HDMI0 连接屏幕,如图所示,上电等待一会儿后即自动开机,输入密码:Mind@123进入 ubuntu 系统

img

(注意:使用时天线不能贴到板子上,同时天线的导电布也不能挨着板子,否则可能会烧坏板子)

img

鼠标和键盘插到两个usb 3.0的接口中都可以正常使用!

2.3 SSH连接

直接用MobaXterm软件连接,下面VNC也是,填上ip地址,username可以填HwHiAiUser或者root,密码:Mind@123

img

登陆成功后显示:

img

2.4 VNC远程连接

由于我的屏幕有点拉闸,所以我选择vnc远程连接

输入ip和 端口,默认是5901,密码:Mind@123

img

说明:

打开终端后,请执行bash激活命令行。

在命令行开启远程桌面服务(执行以后重启,可使用VNC登录图形桌面。默认已开启,无需配置。)

sudo systemctl enable vncserver@1.service
reboot
在命令行关闭远程桌面服务(执行以后重启,不可使用VNC登录图形桌面,且系统会释放相应内存。)

sudo systemctl disable vncserver@1.service
reboot
在命令行修改VNC登录密码(修改密码之后,使用VNC登陆远程桌面需要重新输入密码。)

vncpasswd
成功连接后桌面:

img

若始终无法连接:

如果你不小心重新配置了vnc,导致始终无法连接的话,可以参考以下方法。

(1)输入 cd ~/.vnc进入 .vnc 文件夹

(2)输入 gedit xstartup修改 xstartup 文件

#!/bin/sh
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
exec startxfce4
(3)输入 chmod u+x ~/.vnc/xstartup 加上权限

(4)最后输入:

vncserver -localhost no
vncserver
即可启动vnc,此时再重新连接

三、 样例和接口测试

3.1 登录Jupyter Lab

(1)首先进入保存AI样例的目录:

cd samples/notebooks/
(2)执行 start_notebook.sh 启动Jupyter Lab

./start_notebook.sh
(3)进入Jupyter Lab的网址,直接用火狐浏览器打开即可

img

进入后,即可看到左侧有9个样例的文件夹

img

下面我就挑选几个样例进行测试一下!

3.2 yolov5目标检测

到 样例运行 部分,可以切换视频、图片和摄像头三种模式运行

infer_mode = 'video'
infer_mode = 'image'
infer_mode = 'camera'

img

img

img

def infer_frame_with_vis(image, model, labels_dict, cfg, bgr2rgb=True):
    # 数据预处理
    img, scale_ratio, pad_size = preprocess_image(image, cfg, bgr2rgb)
    # 模型推理
    output = model.infer([img])[0]

    output = torch.tensor(output)
    # 非极大值抑制
    boxout = nms(output, conf_thres=cfg["conf_thres"], iou_thres=cfg["iou_thres"])
    pred_all = boxout[0].numpy()
    # 预测坐标转换
    scale_coords(cfg['input_shape'], pred_all[:, :4], image.shape, ratio_pad=(scale_ratio, pad_size))
    # 图片预测结果可视化
    img_vis = draw_bbox(pred_all, image, (0, 255, 0), 2, labels_dict)
    return img_vis

从这里的推理框架来看,我们可以将我们训练好模型转换成om模型后直接使用,只要对数据的输入做好前处理,推理后,再做好后处理即可。

img

3.3 卡通图像生成

该样例使用 cartoonGAN 模型对输入图片进行卡通化处理。在样例中已经包含转换后的om模型和测试图片。

main 如下:

def main():
    MODEL_PATH = "cartoonization.om"
    MODEL_WIDTH = 256
    MODEL_HEIGHT = 256

    acl_resource = AclLiteResource()  # 初始化acl资源
    acl_resource.init()
    
    # instantiation Cartoonization object
    cartoonization = Cartoonization(MODEL_PATH, MODEL_WIDTH, MODEL_HEIGHT)  # 构造模型对象
    
    # init
    ret = cartoonization.init()  # 初始化模型类变量
    utils.check_ret("Cartoonization.init ", ret)  
    
    image_file = 'img.jpg'
    # read image
    image = AclLiteImage(image_file)  # 构造 AclLiteImage ,方便利用 dvpp 进行前处理
    
    print('===================')
    print(image)
    # preprocess
    crop_and_paste_image = cartoonization.pre_process(image)  # 前处理
    # inference
    result = cartoonization.inference([crop_and_paste_image, ])  # 推理
    # postprocess
    cartoonization.post_process(result, image_file, image)  # 后处理

从代码逻辑中可以看到,整套代码:

(1)首先初始化了acl资源,也就是对AscendCL进行初始化;
(2)创建了一个Cartoonization类,定义了前处理、推理和后处理函数;
(3)构造AclLiteImage类型数据,并转换图片格式为模型需要的yuv格式;
(4)进行推理;
(5)进行后处理。

img

img

3.4 人像分割与背景替换

该样例使用了PortraitNet模型进行人像分割。

main函数如下所示:

def main():
    """推理主函数"""
    os.makedirs(OUTPUT_DIR, exist_ok=True)
    os.makedirs(MASK_DIR, exist_ok=True)

    acl_resource = AclLiteResource()
    acl_resource.init()

    seg = Seg(MODEL_PATH, MODEL_WIDTH, MODEL_HEIGHT)
    ret = seg.init()
    utils.check_ret("seg.init ", ret)

    images_list = [os.path.join(IMAGE_DIR, img)
                   for img in os.listdir(IMAGE_DIR)
                   if os.path.splitext(img)[1] in const.IMG_EXT]

    for image_file in images_list:
        image_name = os.path.basename(image_file)
        if image_name != 'background.jpg':
            print('====' + image_name + '====')
            # read image
            image = AclLiteImage(image_file)
            # Preprocess the picture
            resized_image = seg.pre_process(image)
            # Inference
            result = seg.inference([resized_image, ])
            # Post-processing
            mask = seg.post_process(result, image_name)
            # Fusion of segmented portrait and background image
            background_replace(os.path.join(IMAGE_DIR, 'background.jpg'), \
                                        image_file, os.path.join(MASK_DIR, image_name))

可以看出代码逻辑都相似:
(1)首先初始化了acl资源;
(2)创建Seg类对象,定义前处理、推理和后处理函数;
(3)构造AclLiteImage类型图像数据并进行前处理;
(4)推理后得到分割结果进行后处理获得mask;
(5)进行背景替换

img

3.5 摄像头测试

上面开机时我们接了HDMI0的屏幕和USB的键盘、鼠标,都可以正常使用,下面我们试一下USB摄像头。

在昇腾论坛上,找到一位楼主部署了一个贪吃蛇的小游戏,我打算用这个案例来测试一下:
https://www.hiascend.com/forum/thread-0265146309613977027-1-1.html

首先接入摄像头,将代码下载到系统中,该套代码使用的Google的开源的MediaPipe手势识别库;
其次要安装相关的库:

pip install mediapipe
pip install cvzone

随后就可以直接运行代码:

python main.py

img

可见,效果还是不错的,但是有一点,用vnc连接时默认是虚拟桌面,虚拟桌面好像无法识别到摄像头,所以我还是用的hdmi屏幕。

3.6 串口通信测试

既然是开发板,那么有时候避免不了使用串口的吧,嘿嘿,所以这里对串口也进行了测试。
根据用户手册,可以找到uart设备节点和uart的对应关系:

img

也可以输入 ls /dev/ttyAMA* 来查看,需要注意的是,uart0默认设置为了调试串口功能

img

再看一下引脚图:

img

于是我用一个CH340 USB转TTL模块,选择使用uart7,也就是ttyAMA2TXGPIO7_07RXGPIO7_02

img

这里使用python的一个库:pyserial

输入:pip install pyserial 安装

import serial
import serial.tools.list_ports

ports_list = list(serial.tools.list_ports.comports())  # 获得所有串口设备实例
for port in ports_list:  # 输出所有串口号
    print(list(port)[0], list(port)[1])  #/dev/ttyAMA1、/dev/ttyAMA2

ser = serial.Serial("/dev/ttyAMA2", 115200)  # 打开串口,设置波特率
if ser.isOpen():  # 判断是否打开
    print("open success")
    print(ser.name)  # 输出串口号
else:
    print("failed")
write_len = ser.write("OrangePi AIpro".encode('utf-8'))
ser.close()  # 关闭串口
if ser.isOpen():  # 判断是否关闭串口
    print("no closed")
else:
    print("closed")

运行: sudo python3 serial_test.py,串口ttyAMA2发送 “OrangePi AIpro”,pc端通过串口调试助手也接收到了发送过来的数据

img

img

四、AscendCL快速入门

下面我们具体来看一下AscendCL要如何应用?

AscendCL(Ascend Computing Language)是一套用于在昇腾平台上开发深度神经网络应用的C语言API库,提供运行资源管理、内存管理、模型加载与执行、算子加载与执行、媒体数据处理等API,能够实现利用昇腾硬件计算资源、在昇腾CANN平台上进行深度学习推理计算、图形图像预处理、单算子加速计算等能力。简单来说,就是统一的API框架,实现对所有资源的调用。

而AscendCL结构调用的基本流程可分下面几个步骤:
(1)AscendCL初始化
(2)运行管理资源申请
(3)模型推理/单算子调用/媒体数据处理
(4)运行管理资源释放
(5)AscendCL去初始化

下面我们通过一个简单的狗狗图像分类的应用快速了解AscendCL接口(C语言接口)开发应用的基本过程:

img

首先进入到昇腾开源仓库:Ascend: 昇腾万里,让智能无所不及

进入samples\cplusplus\level2_simple_inference\1_classification\resnet50_firstapp目录下:

(1)直接git整个仓库,或者下载zip,将resnet50_firstapp上传到ubuntu中

(2)下载测试需要的文件:

a)下载模型文件(*.prototxt):

https://obs-9be7.obs.cn-east-2.myhuaweicloud.com/003_Atc_Models/AE/ATC%20Model/resnet50/resnet50.prototxt

放到resnet50_firstapp/model目录下

b)下载权重文件(*.caffemodel):

https://obs-9be7.obs.cn-east-2.myhuaweicloud.com/003_Atc_Models/AE/ATC%20Model/resnet50/resnet50.caffemodel

放到resnet50_firstapp/model目录下

c)下载测试的输入图片:

https://obs-9be7.obs.cn-east-2.myhuaweicloud.com/models/aclsample/dog1_1024_683.jpg

放到resnet50_firstapp/data目录下

img

(3)使用ATC工具进行模型转换

执行:

atc --model=model/resnet50.prototxt --weight=model/resnet50.caffemodel --framework=0 --output=model/resnet50 --soc_version=Ascend310B4

将原始模型转换为昇腾AI处理器能识别的*.om模型文件

● --model: ResNet-50网络的模型文件(.prototxt)的路径。
● --weight: ResNet-50网络的预训练模型文件(
.caffemodel)的路径。
● --framework: 原始框架类型。0:Caffe; 1:MindSpore; 3:Tensorflow; 5:Onnx
● --output: resnet50.om模型文件的路径。请注意,记录保存该om模型文件的路径,后续开发应用时需要使用。
● --soc_version: 昇腾AI处理器的版本。进入“CANN软件安装目录/compiler/data/platform_config”目录,".ini"文件的文件名即为昇腾AI处理器的版本,请根据实际情况选择。实际目录可参考:
./usr/local/Ascend/ascend-toolkit/7.0.0/compiler/data/platform_config
(我这里是通过命令npu-smi info指令这里选择的版本)

img

成功后输出如下所示:

img

转模型有可能会遇到下述问题:

img

img

在昇腾论坛找到了解决方案https://www.hiascend.com/forum/thread-0239142592318174023-1-1.html,总结就是:

转om模型时内存不足,开发板cpu核数较少,atc过程中使用的最大并行进程数默认是服务器的配置,可以使用环境变量减少atc过程中的进程数来减少内存消耗。

·减小算子最大并行编译进程数

export TE_PARALLEL_COMPILER=1

·减少图编译时可用的CPU核数

export MAX_COMPILE_CORE_NUMBER=1

之后就可以成功转换om模型啦!

(4)对输入图片进行处理

由于我们下载的.jpg的输入图片,与模型要求的输入不同,模型要求输入图片是rgb的且大小为224*224,所以这里直接运行resnet50_firstapp/script/transferPic.py脚本进行数据处理

(5)执行编译脚本

给编译脚本执行权限:

chmod +x sample_build.sh

添加环境变量:

export APP_SOURCE_PATH=/home/HwHiAiUser/samples/test_samples/resnet50_firstapp
export DDK_PATH=/usr/local/Ascend/ascend-toolkit/latest
export NPU_HOST_LIB=${DDK_PATH}/runtime/lib64/stub

执行编译脚本:

./sample_build.sh

img

此时在out/main下生成了一个名为main的可执行文件

(6)执行运行脚本

给执行脚本执行权限:

chmod +x sample_run.sh

执行运行脚本:

./sampele_run.sh

img

因为模型使用imagenet数据集训练的,所以标签可以imagenet数据集处查看:https://blog.csdn.net/u013491950/article/details/83927968

img

五、YOLOv8移植测试

虽然官方提供了yolov5的样例,但还是想体验一把部署自己模型的感觉,恰恰又对yolo系列更为熟悉,所以就还是选择以yolov8为例啦

5.1 模型转换

首先我们git下yolov8的源码:https://github.com/ultralytics/ultralytics,下载一下模型,这里我选择下载yolov8s.pt

img

用简单的代码转换成onnx模型:

from ultralytics import YOLO

model = YOLO('yolov8s.pt', 'detect')
model.export(format='onnx', opset=11, simplify=True)

输出如下:

img

注意这里的输出是三个尺度的。

将onnx模型放到ubuntu系统中后,转换成om模型:

export TE_PARALLEL_COMPILER=1
export MAX_COMPILE_CORE_NUMBER=1
atc --model=yolov8s.onnx --framework=5 --output=yolov8s --soc_version=Ascend310B4 --input_format=NCHW --input_shape="images:1,3,640,640" --output_type="FP32" --log=error

–framework: 选择5,即onnx模型
–output: 为输出的名称,会自动加上.om后缀
–input_format: 输入格式为NCHW
–input_shape: 设置模型输入的格式
–output_type: 输出格式,可以自己选择

img

我这里为了方便后处理直接使用一个转换好的onnx模型,输出为(1, 84, 8400),大家可以自取,免费下载:https://download.csdn.net/download/qq_47941078/89365245

5.2 推理

获得om模型后,就要自己写一下推理代码啦

import cv2
import torch
import numpy as np
import time
import random
from numpy import ndarray
from typing import List, Tuple
from ais_bench.infer.interface import InferSession

# 类别名称
CLASS_NAMES = ('person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus',
               'train', 'truck', 'boat', 'traffic light', 'fire hydrant',
               'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog',
               'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe',
               'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee',
               'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat',
               'baseball glove', 'skateboard', 'surfboard', 'tennis racket',
               'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl',
               'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot',
               'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch',
               'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop',
               'mouse', 'remote', 'keyboard', 'cell phone', 'microwave',
               'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock',
               'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush')

# 预处理
def preprocess_warpAffine(image, dst_width=640, dst_height=640):
    scale = min((dst_width / image.shape[1], dst_height / image.shape[0]))
    ox = (dst_width  - scale * image.shape[1]) / 2
    oy = (dst_height - scale * image.shape[0]) / 2
    M = np.array([
        [scale, 0, ox],
        [0, scale, oy]
    ], dtype=np.float32)
    
    img_pre = cv2.warpAffine(image, M, (dst_width, dst_height), flags=cv2.INTER_LINEAR,
                             borderMode=cv2.BORDER_CONSTANT, borderValue=(114, 114, 114))
    IM = cv2.invertAffineTransform(M)

    img_pre = (img_pre[...,::-1] / 255.0).astype(np.float32)
    img_pre = img_pre.transpose(2, 0, 1)[None]
    img_pre = torch.from_numpy(img_pre)
    return img_pre, IM

# 计算iou
def iou(box1, box2):
    def area_box(box):
        return (box[2] - box[0]) * (box[3] - box[1])

    left   = max(box1[0], box2[0])
    top    = max(box1[1], box2[1])
    right  = min(box1[2], box2[2])
    bottom = min(box1[3], box2[3])
    cross  = max((right-left), 0) * max((bottom-top), 0)
    union  = area_box(box1) + area_box(box2) - cross
    if cross == 0 or union == 0:
        return 0
    return cross / union

# nms非极大值抑制
def NMS(boxes, iou_thres):
    remove_flags = [False] * len(boxes)

    keep_boxes = []
    for i, ibox in enumerate(boxes):
        if remove_flags[i]:
            continue

        keep_boxes.append(ibox)
        for j in range(i + 1, len(boxes)):
            if remove_flags[j]:
                continue

            jbox = boxes[j]
            if(ibox[5] != jbox[5]):
                continue
            if iou(ibox, jbox) > iou_thres:
                remove_flags[j] = True
    return keep_boxes

# 后处理
def postprocess(pred, IM=[], conf_thres=0.25, iou_thres=0.45):
    # 1,8400,84 [cx,cy,w,h,class*80]
    boxes = []
    for item in pred[0]:
        cx, cy, w, h = item[:4]
        label = item[4:].argmax()
        confidence = item[4 + label]
        if confidence < conf_thres:
            continue
        left    = cx - w * 0.5
        top     = cy - h * 0.5
        right   = cx + w * 0.5
        bottom  = cy + h * 0.5
        boxes.append([left, top, right, bottom, confidence, label])

    boxes = np.array(boxes)
    lr = boxes[:,[0, 2]]
    tb = boxes[:,[1, 3]]
    boxes[:,[0,2]] = IM[0][0] * lr + IM[0][2]
    boxes[:,[1,3]] = IM[1][1] * tb + IM[1][2]
    boxes = sorted(boxes.tolist(), key=lambda x:x[4], reverse=True)
    
    return NMS(boxes, iou_thres)

def hsv2bgr(h, s, v):
    h_i = int(h * 6)
    f = h * 6 - h_i
    p = v * (1 - s)
    q = v * (1 - f * s)
    t = v * (1 - (1 - f) * s)
    
    r, g, b = 0, 0, 0

    if h_i == 0:
        r, g, b = v, t, p
    elif h_i == 1:
        r, g, b = q, v, p
    elif h_i == 2:
        r, g, b = p, v, t
    elif h_i == 3:
        r, g, b = p, q, v
    elif h_i == 4:
        r, g, b = t, p, v
    elif h_i == 5:
        r, g, b = v, p, q

    return int(b * 255), int(g * 255), int(r * 255)

def random_color(id):
    h_plane = (((id << 2) ^ 0x937151) % 100) / 100.0
    s_plane = (((id << 3) ^ 0x315793) % 100) / 100.0
    return hsv2bgr(h_plane, s_plane, 1)

if __name__ == "__main__":
    model_path = 'yolov8s2.om'
    img_path = 'bus.jpg'
    
    img = cv2.imread(img_path)
    img22 = cv2.imread(img_path)

    # img_pre = preprocess_letterbox(img)
    img_pre, IM = preprocess_warpAffine(img)

    # model  = AutoBackend(weights="yolov8s.pt")
    model = InferSession(0, model_path)
    # names  = model.names
    names = CLASS_NAMES
    
    img = np.ascontiguousarray(img_pre, dtype=np.float32)
    start = time.perf_counter()
    result = model.infer([img])
    print(np.array(result).shape) # (1, 1, 84, 8400)
    result = np.array(result).transpose(0, 1, 3, 2).squeeze()[np.newaxis]
    print(np.array(result).shape) # (1, 8400, 84)
    result = list(result)
    end = time.perf_counter()
    print(f'Inference FPS: {1 / (end - start)}')

    boxes  = postprocess(result, IM)

    for obj in boxes:
        left, top, right, bottom = int(obj[0]), int(obj[1]), int(obj[2]), int(obj[3])
        confidence = obj[4]
        label = int(obj[5])
        color = random_color(label)
        cv2.rectangle(img22, (left, top), (right, bottom), color=color ,thickness=2, lineType=cv2.LINE_AA)
        caption = f"{names[label]} {confidence:.2f}"
        print("caption:", caption)
        w, h = cv2.getTextSize(caption, 0, 1, 2)[0]
        cv2.rectangle(img22, (left - 3, top - 33), (left + w + 10, top), color, -1)
        cv2.putText(img22, caption, (left, top - 5), 0, 1, (0, 0, 0), 2, 16)
	
    print("img22:", img22.shape)
    #img22 = img22.transpose(1, 2, 0)
    #print("img22:", img22.shape)
    cv2.imwrite("infer.jpg", img22)
    print("save done")

输出打印:

img

保存的推理图片为:

img

可以看到精度还是很高的,并没有什么损失!

六、 总结

OrangePi AIpro是一款非常优秀的AI开发板,结合了高性能处理器和专用的AI加速硬件,算力强大,完全满足正常的视频流推理的需求,又有着非常丰富的接口,给开发者提供了一个强大且灵活的平台来用于深度学习、目标检测等AI应用的开发和部署。

同时,关于OrangePi AIpro有着非常丰富详细的资料,用户手册完全可以帮助快速使用开发板;各种开源仓库也提供了多种测试样例;此外还有论坛,用户活跃,各种问题都可以相互讨论交流,比如我在模型转换时遇到了内存不足的问题便是在论坛上找到的解决方案;社区还提供了详细的开发指南,从文本教程到视频教程,对开发者都是非常有帮助的,本次体验非常愉快!

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

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

相关文章

合约之间调用-如何实现函数静态调用?

合约之间的函数调用 EOA&#xff0c;external owned account&#xff0c;外部账号&#xff0c;例如metamask调用最终总是由EOA发起的合约之间的调用使得一次完整的调用成为一个调用链条 合约间调用过程 调用者须持有被调用合约的地址得到被调用合约的信息将地址重载为被调用合…

母亲的爱与妻子的爱,同为“爱“。不同感受!

母亲的爱与妻子的爱&#xff0c;虽然都是一个女人给予男人的爱&#xff0c;却有着本质的不同&#xff01; 天下父母对儿女的爱大多相同。在母亲眼中&#xff0c;儿女无论是多大年龄&#xff0c;无论你是否长大成人&#xff0c;也无论你做多大的官&#xff0c;有多么大的成就&am…

【深度学习】吸烟行为检测软件系统

往期文章列表&#xff1a; 【YOLO深度学习系列】图像分类、物体检测、实例分割、物体追踪、姿态估计、定向边框检测演示系统【含源码】【深度学习】YOLOV8数据标注及模型训练方法整体流程介绍及演示【深度学习】行人跌倒行为检测软件系统【深度学习】火灾检测软件系统【深度学…

KDD 2024|基于隐空间因果推断的微服务系统根因定位

简介&#xff1a;本文介绍了由清华大学、南开大学、eBay、微软、中国科学院计算机网络信息中心等单位共同合作的论文《基于隐空间因果推断的受限可观测性场景的微服务系统根因定位》。该论文已被KDD 2024会议录用。 论文标题&#xff1a;Microservice Root Cause Analysis Wit…

数据与结构——红黑树

目录 红黑树的概念 性质 结点的定义 插入 验证 查找 删除 红黑树与AVL树的比较 红黑树的概念 红黑树是一种自平衡二叉搜索树&#xff08;Binary Search Tree, BST&#xff09;&#xff0c;其每个节点带有颜色属性&#xff0c;可以是红色或黑色。红黑树通过约束节点颜色…

盲盒小程序开发,为市场带来的新机遇

近年来&#xff0c;盲盒市场一直处于热门行业中&#xff0c;发展非常快速。在互联网的支持下&#xff0c;也衍生出了线上盲盒小程序&#xff0c;实现了线上线下双发展的态势。 盲盒小程序作为一种新的盲盒购物方式&#xff0c;受到了盲盒消费者的喜爱&#xff0c;为盲盒行业的…

Matlab 结构光相移法(单频多相)

文章目录 一、简介1、基于点的测距2、基于条纹的测距二、条纹编码2.1 二进制编码2.2相移法三、实现代码参考文献一、简介 在介绍相移法之前,我们需要先了解一下为啥会有相移法,了解了其来龙去脉,则更容易去应用它。 1、基于点的测距 首先我们从点的测距开始,这有点类似于立…

香港优才计划找中介是否是智商税,靠谱中介又该如何找?

关于香港优才计划的申请&#xff0c;找中介帮助还是自己DIY&#xff0c;网络上充斥的声音太多&#xff0c;对不了解的人来说&#xff0c;难以抉择的同时还怕上当受骗。 这其中很容易误导人的关键在于——信息差&#xff01; 今天这篇文章的目的就是想让大家看清一些中介和DIY…

2024-05-29 blue-VH-driver-对外接口的并行调用-设计与思考

摘要: VH的driver的对外接口, 要做到可以并行&#xff0c;也就是两个不同的线程&#xff0c;分别调用&#xff0c;不能互相阻塞。 本文记录对其的思考和设计。 上下文: 2024-05-28 blue-VH-driver-需求分析及问题分析-CSDN博客 2024-05-27 blue-vh-问题点-CSDN博客 2024-05…

【开发利器】使用OpenCV算子工作流高效开发

学习《人工智能应用软件开发》&#xff0c;学会所有OpenCV技能就这么简单&#xff01; 做真正的OpenCV开发者&#xff0c;从入门到入职&#xff0c;一步到位&#xff01; OpenCV实验大师Python SDK 基于OpenCV实验大师v1.02版本提供的Python SDK 实现工作流导出与第三方应用集…

革新风暴来袭:报事报修系统小程序如何重塑报事报修体验?

随着数字化、智能化的发展&#xff0c;已经应用在我们日常生活和工作的方方面面。那么&#xff0c;你还在为物业报修而头疼吗&#xff1f;想象一下&#xff0c;家里的水管突然爆裂&#xff0c;你急忙联系物业&#xff0c;时常面临物业电话忙音、接听后才进行登记繁琐的报修单、…

Sytem.getenv的作用和意义介绍

Sytem.getenv的作用和意义介绍&#xff01;在实际的项目开发中&#xff0c;我们经常需要获取一些系统自身的环境变量&#xff0c;为此&#xff0c;java官方提供的这个系统环境变量&#xff0c;自带了一个方法&#xff0c;就可以直接拿到系统的环境变量值了。 下面是一个简单的…

一个全面了解Xilinx FPGA IP核的窗口:《Xilinx系列FPGA芯片IP核详解》(可下载)

随着摩尔定律的逐渐放缓&#xff0c;传统的芯片设计方法面临着越来越多的挑战。而FPGA以其并行处理能力和可编程性&#xff0c;为解决复杂问题提供了新的途径。它允许设计者在同一个芯片上实现多种不同的功能模块&#xff0c;极大地提高了资源的利用率和系统的综合性能。 FPGA…

Python 之微信指数小程序数据抓取

Fiddler安装和设置 安装 Fiddler 安装包可以从这里获取&#xff0c;如果失效了可以自己网上找一个安装。 链接&#xff1a;https://pan.baidu.com/s/1N30BoDWm2_dBL8i8GRzK5g?pwd1znv 提取码&#xff1a;1znv 然后就是点击安装就好了&#xff0c;没什么好多说的。 启用…

NoSQL是什么?NoSQL数据库存在SQL注入攻击?

一、NoSQL是什么&#xff1f; NoSQL&#xff08;Not Only SQL&#xff09;是一种非关系型数据库的概念。与传统的关系型数据库不同&#xff0c;NoSQL数据库使用不同的数据模型来存储和检索数据。NOSQL数据库通常更适合处理大规模的非结构化和半结构化数据&#xff0c;且能够…

赛事赞助|威波力赞助2024年首届中国大学生跳绳锦标赛

2024年5月26日由厦门威波力品牌赞助的“2024年首届中国大学生跳绳锦标赛”在上海财经大学体育馆圆满落幕。作为本次大赛的赞助方之一&#xff0c;威波力为比赛全程提供支持&#xff0c;与大家一起见证了一场场精彩纷呈的比赛。 此次比赛&#xff0c;昆明学院的李中芸&#xff0…

【YOLOv5/v7改进系列】引入AKConv——即插即用的卷积块

一、导言 介绍了一种名为AKConv&#xff08;Alterable Kernel Convolution&#xff09;的新型卷积操作&#xff0c;旨在解决标准卷积操作存在的两个根本性问题。首先&#xff0c;标准卷积操作受限于局部窗口&#xff0c;无法捕获来自其他位置的信息&#xff0c;且其采样形状固…

【Java SE】 String、StringBuff和StringBuilder

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 1. 字符串不可变性1.1 设计不可变1.2 修改字符串创建新对象1.3 为什么字符串不可变1.4 String类设计不可变的…

【易生支付官网注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…