YOLO入门教程(二)——OpenVINO™部署YOLO模型与异步推理实现150FPS+实时检测【含教程源码 + 环境配置】

news2024/11/29 22:37:01

目录

  • 引言
  • 前期准备
  • Step1 pt格式转onnx格式
  • Step2 创建工程文件并安装NuGet Package
  • Step3 预处理API部署YOLO模型
  • Step4 前处理+推理+后处理
  • Step5 异步推理
  • 参考博客

引言

YOLO(You Only Look Once)作为一个目标检测算法,其调用的接口为Python语言,通常情况下需要将YOLO模型的pt格式转换为其他格式的模型供其他编程语言调用,常见的onnx格式可以支持C++/C#/Python等多种语言的调用,本文教程主要介绍如何将YOLO模型的pt格式转换为onnx格式,并在Window环境下用C#调用模型并推理。

前期准备

开始前建议先下载参考项目源码,在Anaconda中重新配置一个Python3.10的环境,安装的PackageList如下:
在这里插入图片描述

CUDA版本相同的情况下这里建议直接参考作者所配置的环境版本

在这里插入图片描述

Step1 pt格式转onnx格式

from ultralytics import YOLO
import os

# Model Path
save_model_name = os.path.normpath(os.path.join(os.getcwd(), './yolov10-main/runs/detect/mytrain_0806/weights/best.pt'))

# Load model
model = YOLO(os.path.normpath(save_model_name))

# Create 'best.onnx'
model.export(format="onnx", dynamic=False, simplify=True)

save_model_name替换成pt文件所在的路径
生成的onnx文件也在save_model_name路径下

Step2 创建工程文件并安装NuGet Package

在这里插入图片描述

Step3 预处理API部署YOLO模型

将OpenVINO部署方式封装为接口,只需要实例化Predictor类即可加载模型

using OpenCvSharp.Dnn;
using OpenCvSharp;
using OpenVinoSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using OpenVinoSharp.preprocess;
using System.Diagnostics;
using OpenVinoSharp.Extensions;
using OpenVinoSharp.Extensions.model;

namespace DeepLearningDotNet
{
    public class Predictor : IDisposable
    {
        /// <summary>
        /// OpenVINO Runtime Core
        /// </summary>
        private Core core;
        /// <summary>
        /// 加载前处理API的模型
        /// </summary>
        private Model model;
        /// <summary>
        /// 加载到设备中的模型
        /// </summary>
        private CompiledModel compiled;
        /// <summary>
        /// OpenVINO推理接口
        /// </summary>
        public InferRequest openvino_api_infer;
        /// <summary>
        /// OpenCV推理接口
        /// </summary>
        public Net opencv_infer;
        /// <summary>
        /// 接口类型
        /// </summary>
        private string engine;
        /// <summary>
        /// 模型图片尺寸
        /// </summary>
        private int[] input_size;

        public Predictor() { }

        /// <summary>
        /// 实例化推理器
        /// </summary>
        /// <param name="model_path"></param>
        /// <param name="engine"></param>
        /// <param name="device"></param>
        /// <exception cref="ArgumentNullException"></exception>
        public Predictor(string model_path, string engine = "OpenVINO", string device = "AUTO", int[] input_size = null)
        {
            //判断路径是否合法
            if (model_path == null || model_path == "" || !File.Exists(model_path))
            {
                throw new ArgumentNullException(nameof(model_path));
            }

            this.engine = engine;
            if (engine == "OpenVINO")
            {
                // -------- Step 1. Initialize OpenVINO Runtime Core --------
                core = new Core();
                //判断设备是否可用
                if (!core.get_available_devices().Contains(device))
                {
                    throw new ArgumentNullException(nameof(device));
                }
                // -------- Step 2. Read inference model --------
                Model tempModel = core.read_model(model_path);
                OvExtensions.printf_model_info(tempModel);
                PrePostProcessor processor = new PrePostProcessor(tempModel);
                this.input_size = input_size ?? new int[] { 1, 3, 640, 640 };
                Tensor input_tensor_pro = new Tensor(new OvType(ElementType.U8), new Shape(this.input_size[0], this.input_size[2], this.input_size[3], this.input_size[1]));
                InputInfo input_info = processor.input(0);
                InputTensorInfo input_tensor_info = input_info.tensor();
                input_tensor_info.set_from(input_tensor_pro).set_layout(new Layout("NHWC")).set_color_format(ColorFormat.BGR);
                PreProcessSteps process_steps = input_info.preprocess();
                process_steps.convert_color(ColorFormat.RGB).resize(ResizeAlgorithm.RESIZE_LINEAR)
                   .convert_element_type(new OvType(ElementType.F32)).scale(255.0f).convert_layout(new Layout("NCHW"));
                model = processor.build();
                // -------- Step 3. Loading a model to the device --------
                compiled = core.compile_model(model, device);
                // -------- Step 4. Create an infer request --------
                openvino_api_infer = compiled.create_infer_request();

            }
            if (engine == "OpenCv")
            {
                opencv_infer = CvDnn.ReadNetFromOnnx(model_path);
            }
        }

        public void Dispose()
        {
            openvino_api_infer.Dispose();
            opencv_infer.Dispose();
            compiled.Dispose();
            model.Dispose();
            core.Dispose();
            GC.Collect();
        }

        /// <summary>
        /// OpenVINO推理方法
        /// </summary>
        /// <param name="input_data"></param>
        /// <param name="input_names"></param>
        /// <param name="input_size"></param>
        /// <param name="output_names"></param>
        /// <param name="output_sizes"></param>
        /// <returns></returns>
        public List<float[]> OpenVinoInfer(Mat img, List<string> input_names, int[] input_size, List<string> output_names, List<int[]> output_sizes)
        {
            List<float[]> returns = new List<float[]>();

            try
            {
                // -------- Step 6. Set up input data --------
                if (set_input_tensor_data(img))
                {
                    // -------- Step 7. Do inference synchronously --------
                    openvino_api_infer.infer();

                    // -------- Step 8. Get infer result data --------
                    Tensor output_tensor = openvino_api_infer.get_output_tensor();
                    int output_length = (int)output_tensor.get_size();
                    float[] output_data = output_tensor.get_data<float>(output_length);
                    returns.Add(output_data);
                }

                return returns;
            }
            catch { return returns; }
        }

        /// <summary>
        /// 加载数据到推理器中
        /// 该函数不能放在继承类中会内存泄露
        /// </summary>
        /// <param name="img">输入前处理后的img</param>
        /// <returns></returns>
        public bool set_input_tensor_data(Mat img)
        {
            try
            {
                //输入的图片未经过前处理则不加载到推理器中
                if (Math.Max(img.Size().Width, img.Size().Height) != input_size[2] 
                    && Math.Max(img.Size().Width, img.Size().Height) != input_size[3])
                    return false;
                
                // 从推理器中获取输入数据格式
                Tensor input_tensor = openvino_api_infer.get_input_tensor();
                Shape input_shape = input_tensor.get_shape();
                byte[] input_data = new byte[input_shape[1] * input_shape[2] * input_shape[3]];
                // 通过非托管指针img数据写入到input_data数组
                Marshal.Copy(img.Ptr(0), input_data, 0, input_data.Length);
                IntPtr destination = input_tensor.data();
                // 把input_tensor的指针指向input_data数组
                Marshal.Copy(input_data, 0, destination, input_data.Length);
                return true;
            }
            catch {  return false; }
        }

        /// <summary>
        /// OpenCv推理方法
        /// </summary>
        /// <param name="input_data"></param>
        /// <param name="input_names"></param>
        /// <param name="input_size"></param>
        /// <param name="output_names"></param>
        /// <param name="output_sizes"></param>
        /// <returns></returns>
        public List<float[]> OpenCvInfer(Mat img, List<string> input_names, int[] input_size, List<string> output_names, List<int[]> output_sizes)
        {
            List<float[]> returns = new List<float[]>();
            float[] input_data = OpenVinoSharp.Extensions.process.Permute.run(img);
            var input_tensor = openvino_api_infer.get_input_tensor();
            input_tensor.set_data(input_data);
            openvino_api_infer.infer();
            foreach (var name in output_names)
            {
                var output_tensor = openvino_api_infer.get_tensor(name);
                returns.Add(output_tensor.get_data<float>((int)output_tensor.get_size()));
            }
            return returns;
        }
    }
}

OpenVINO提供了预处理C#API可以将前处理加载到模型中使用GPU进行前处理加快运行速度
只需要实例化PrePostProcessor将输入和输出数据的格式指定后将build的新模型加载到推理器中即可

Step4 前处理+推理+后处理

将OpenVINO推理方式封装为接口,只需要实例化DeepLearning类即可推理

using OpenCvSharp;
using OpenCvSharp.Dnn;
using OpenVinoSharp;
using OpenVinoSharp.Extensions.result;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using static OpenCvSharp.FileStorage;

namespace DeepLearningDotNet
{
    public class OpenVinoDet : DeepLearning
    {
        public OpenVinoDet(string model_path, string engine, string device, int categ_nums, float det_thresh, float det_nms_thresh, int[] input_size)
            : base(model_path, engine, device, categ_nums, det_thresh, det_nms_thresh, new int[] { 1, 3, input_size[0], input_size[1] },
            new List<string> { "images" }, new List<int[]> { new int[] { 1, 4 + categ_nums, 8400 } }, new List<string> { "output0" })
        {
        }
        /// <summary>
        /// OpenVino推理接口
        /// </summary>
        /// <param name="img"></param>
        /// <returns></returns>
        public BaseResult predict(Mat img)
        {
            if (img.Cols == (int)0)
                return new BaseResult();

            List<float[]> result_data;

            //前处理
            Mat afterPreImage = preprocess(img);

            //推理
            result_data = m_predictor.OpenVinoInfer(afterPreImage, m_input_names, m_input_size, m_output_names, m_output_sizes);

            //后处理
            BaseResult re = postprocess(result_data);

            return re;
        }

        /// <summary>
        /// API前处理方法
        /// </summary>
        /// <param name="img"></param>
        /// <returns></returns>
        public override Mat preprocess(Mat img)
        {
            m_image_size = new List<int> { (int)img.Size().Width, (int)img.Size().Height };

            //OpenCv图像的BGR通道转换对齐到模型图片的RGB通道【利用API前处理会自动对齐】
            //Cv2.CvtColor(img, img, ColorConversionCodes.BGR2RGB);

            不变形缩放【保留图片的宽高比,缺少的部分用灰色图像填充】
            img = OpenVinoSharp.Extensions.process.Resize.letterbox_img(img, (int)m_input_size[2], out m_factor);

            //将像素值从0-255映射成0-1归一化【利用API前处理会自动归一化】
            //img = OpenVinoSharp.Extensions.process.Normalize.run(img, true);

            return img;
        }

        /// <summary>
        /// 后处理方法
        /// </summary>
        /// <param name="results"></param>
        /// <returns></returns>
        public override BaseResult postprocess(List<float[]> results)
        {
            List<Rect> positionBoxes = new List<Rect>();
            List<int> classIds = new List<int>();
            List<float> confidences = new List<float>();

            //图像前处理后推理将结果还原到原图上
            for (int i = 0; i < results[0].Length / 6; i++)
            {
                int s = 6 * i;
                if ((float)results[0][s + 4] > m_det_thresh)
                {
                    float cx = results[0][s + 0];
                    float cy = results[0][s + 1];
                    float dx = results[0][s + 2];
                    float dy = results[0][s + 3];
                    //坐标缩放
                    int x = (int)((cx) * m_factor);
                    int y = (int)((cy) * m_factor);
                    //尺寸缩放
                    int width = (int)((dx - cx) * m_factor);
                    int height = (int)((dy - cy) * m_factor);
                    Rect box = new Rect();
                    box.X = x;
                    box.Y = y;
                    box.Width = width;
                    box.Height = height;

                    positionBoxes.Add(box);
                    classIds.Add((int)results[0][s + 5]);
                    confidences.Add((float)results[0][s + 4]);
                }
            }

            DetResult re = new DetResult();
            int[] indices;

            //利用OpenCv非极大值抑制来筛选检测结果
            CvDnn.NMSBoxes(positionBoxes, confidences, m_det_thresh, m_det_nms_thresh, out indices);
            foreach (int i in indices)
            {
                re.add(classIds[i], confidences[i], positionBoxes[i]);
            }

            return re;
        }

        /// <summary>
        /// 将结果绘制到图上
        /// </summary>
        /// <param name="bresult"></param>
        /// <param name="image"></param>
        /// <returns></returns>
        public static Mat draw_det_result(BaseResult bresult, Mat image)
        {
            Mat mat = new Mat();
            image.CopyTo(mat);

            DetResult detResult = bresult as DetResult;
            for (int i = 0; i < detResult.count; i++)
            {
                Cv2.Rectangle(mat, detResult.datas[i].box, new Scalar(0.0, 0.0, 255.0), 2);
                Cv2.Rectangle(mat, new Point(detResult.datas[i].box.TopLeft.X, detResult.datas[i].box.TopLeft.Y + 30), new Point(detResult.datas[i].box.BottomRight.X, detResult.datas[i].box.TopLeft.Y), new Scalar(0.0, 255.0, 255.0), -1);
                Cv2.PutText(mat, detResult.datas[i].lable + "-" + detResult.datas[i].score.ToString("0.00"), new Point(detResult.datas[i].box.X, detResult.datas[i].box.Y + 25), HersheyFonts.HersheySimplex, 0.8, new Scalar(0.0, 0.0, 0.0), 2);
            }
            return mat;
        }
    }
}

把继承关系取消后可直接使用

已经提前将预处理C#API加载到模型中不需要再进行通道对齐和归一化处理
通过分数和NMS来后处理筛选最终检测结果

Step5 异步推理

从下图可以看出使用Async异步推理的方式可以极大的提高系统的效率
在这里插入图片描述

/// <summary>
/// 实时检测
/// </summary>
public void video_api_predict()
{
    //目标检测
    string model_type = "YOLOv10Det";
    string engine_type = "OpenVINO";
    string device = "CPU";
    float score = 0.2f;
    float nms = 0.5f;
    int categ_num = 80;
    int[] input_size = { 640, 640 };

    //目前还未能使用OpenVINO调用iGPU进行处理
    DeepLearning curr_predictor = DeepLearning.GetYOLO(model_type, model_path, engine_type, device, categ_num, score, nms, input_size);
    DeepLearning next_predictor = DeepLearning.GetYOLO(model_type, model_path, engine_type, device, categ_num, score, nms, input_size);

    Mat frame_curr = CaptureVideo();
    /*先加载首帧图片进行推理*/
    if (frame_curr.Cols != 0)
    {
        try
        {
            //使用API前处理图片
            Mat img = ((OpenVinoDet)curr_predictor).preprocess(frame_curr);
            //数据加载到推理器中
            ((OpenVinoDet)curr_predictor).m_predictor.set_input_tensor_data(img);
            //异步启动推理器进行推理
            ((OpenVinoDet)curr_predictor).m_predictor.openvino_api_infer.start_async();
        }
        catch (Exception e)
        {
            MessageBox.Show(e.Message);
        }
    }

    while (true)
    {
        if (!isCaptureVideo)
        {
            Thread.Sleep(50);
            continue;
        }
        /*这个过程相当于提前把下一帧的图片丢进去推理与首帧的图片后处理并行*/
        Mat frame_next = CaptureVideo();

        sw.Restart();
        if (frame_next.Cols != 0)
        {
            try
            {
                //使用API前处理图片
                Mat img = ((OpenVinoDet)next_predictor).preprocess(frame_next);
                //数据加载到推理器中
                ((OpenVinoDet)next_predictor).m_predictor.set_input_tensor_data(img);
                //异步启动推理器进行推理
                ((OpenVinoDet)next_predictor).m_predictor.openvino_api_infer.start_async();
            }
            catch (Exception e){ }
        }

        //等待首帧处理完毕
        ((OpenVinoDet)curr_predictor).m_predictor.openvino_api_infer.wait();

        List<float[]> result_data = new List<float[]>();
        foreach (var name in ((OpenVinoDet)curr_predictor).m_output_names)
        {
            var output_tensor = ((OpenVinoDet)curr_predictor).m_predictor.openvino_api_infer.get_tensor(name);
            result_data.Add(output_tensor.get_data<float>((int)output_tensor.get_size()));
        }

        //异步进行绘制会出现内存无法访问的情况
        //this.BeginInvoke(() =>

        /*首帧图片已经处理完成进行绘制*/
        this.Invoke(() =>
        {
            //后处理首帧图片
            BaseResult result = ((OpenVinoDet)curr_predictor).postprocess(result_data);

            result.update_lable(class_names);
            Mat re_img = new Mat();
            re_img = OpenVinoDet.draw_det_result(result, frame_curr);
            if (re_img.Cols != 0)
            {
                sw.Stop();
                if(sw.ElapsedMilliseconds != 0)
                    PredictTimes.Add(1000 / sw.ElapsedMilliseconds);

                pictureBox2.Image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(re_img);
                DetResult detResult = result as DetResult;
                textBox8.Text = detResult.count.ToString();
                for (int i = 0; i < detResult.count; i++)
                {
                    textBox1.Text = detResult.datas[i].lable;
                    textBox2.Text = detResult.datas[i].score.ToString("0.00");

                    int X = detResult.datas[i].box.TopLeft.X;
                    int Y = detResult.datas[i].box.TopLeft.Y;
                    int W = detResult.datas[i].box.Width;
                    int H = detResult.datas[i].box.Height;
                    textBox3.Text = X.ToString();
                    textBox4.Text = Y.ToString();
                    textBox5.Text = W.ToString();
                    textBox6.Text = H.ToString();
                }
                if (PredictTimes.Count > 10)
                {
                    textBox9.Text = (PredictTimes.Average()).ToString("00");
                    PredictTimes.Clear();
                }
            }
        });

        //首帧处理完毕后交换到下一帧(包括推理器和图片)
        DeepLearning tempInferRequest;
        tempInferRequest = (OpenVinoDet)curr_predictor;
        curr_predictor = next_predictor;
        next_predictor = tempInferRequest;

        frame_next.CopyTo(frame_curr);
    }
}

/// <summary>
/// 模拟实时取图
/// </summary>
/// <returns></returns>
public Mat CaptureVideo()
{
    // 生成一个范围在 0 到 39 之间的随机整数用于获取图片模拟实时取图
    Random random = new Random();
    int randomNumber = random.Next(0, 40);
    string image_path = string.Format("D:\\MyCode\\yolo_train\\script\\source\\images\\{0}.png", randomNumber);
    Mat img = new Mat();
    double startTime = Convert.ToDouble(DateTime.Now.ToString("mmss.ff")) * 1000;
    img = Cv2.ImRead(image_path);
    double endTime = Convert.ToDouble(DateTime.Now.ToString("mmss.ff")) * 1000;
    this.Invoke(new Action(() => 
    {
        CaptureTimes.Add(endTime - startTime);
        if(CaptureTimes.Count > 10)
        {
            textBox10.Text = (CaptureTimes.Average()).ToString("00");
            CaptureTimes.Clear();
        }
    }));
    return img;
}

OpenVINO提供了推理请求(Infer Request)机制,来实现在指定的推理设备上以同步或异步方式运行AI模型
创建infer_request对象
将数据传入模型的指定输入节点set_tensor(input_node, input_tensor)
通过非阻塞(non-blocking)的方式启动推理计算start_async()
阻塞等待推理计算结束wait()
从模型的指定输出节点获取推理结果get_tensor(output_node)

OpenVINO推理YOLOv10模型

参考博客

1.用 OpenVINO™ C# API 在intel平台部署 YOLOv10 目标检测模型
2.【OpenVINO™】使用 OpenVINO™ C# 异步推理接口部署YOLOv8 ——在Intel IGPU 上速度依旧飞起!!
3.在蝰蛇峡谷上实现YOLOv5模型的OpenVINO异步推理程序

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

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

相关文章

架空防静电地板的贴面面层有哪些

很多机房装修都会用到架空防静电地板&#xff0c;架空防静电地板由贴面、基材、支架横梁系统组成&#xff0c;那么架空防静电地板的贴面都有哪些呢&#xff1f;一起来看看~ 防静电地板常用贴面面层有3种: 1、PVC防静电贴面面层;2、HPL防静电贴面面层;3、瓷砖防静电贴面面层 1、…

《治疗前磁共振成像显示的结直肠癌患者的结外扩展及其预后价值》| 文献速递-基于深度学习的癌症风险预测与疾病预后应用

Title 题目 Extranodal Extension at Pretreatment MRI and the Prognostic Value for Patients with Rectal Cancer 《治疗前磁共振成像显示的结直肠癌患者的结外扩展及其预后价值》 Background 背景 Detection of extranodal extension (ENE) at pathology is a poor pr…

什么是ERP,ERP系统主要包括哪些功能?

ERP的定义 ERP&#xff0c;全称 Enterprise Resource Planning 即企业资源计划。是一种集成化的管理软件系统&#xff0c;它通过信息技术手段&#xff0c;将企业的各个业务流程和资源管理进行整合&#xff0c;以提高企业的运营效率和管理水平。 ERP的核心价值在于“开源节流”…

linux 中docker git 容器磁盘占满如何解决

1.问题描述 git之前还使用ok&#xff0c;突然出现访问500 错误&#xff0c;懵圈了 2.问题排查 1. 服务器查看&#xff0c;服务正常&#xff0c;没有异常出现。 2. 查找资料&#xff0c;需要查看是否磁盘已经满了果然使用df-h 后显示磁盘已经满了&#xff0c;且容器和本地都…

Java异常捕获与处理:深入理解与实践

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

ATGM332D-F8S:高集成度单北斗多频车载航位推算模块规格书

车载航位 推算 模块 描述 &#xff1a; 是一种在车载导航系统中广泛应用的自主导航算法模块&#xff0c;主要用于弥补GPS或GNSS&#xff08;全球导航卫星系统&#xff09;在信号遮蔽环境下的定位不足。 车载航位推算模块通过收集来自车辆上各种传感器&#xff08;如陀螺仪传感器…

【JAVA入门】Day24 - 排序算法

【JAVA入门】Day24 - 排序算法 文章目录 【JAVA入门】Day24 - 排序算法一、冒泡排序二、选择排序三、插入排序四、快速排序4.1 递归4.2 快速排序 排序&#xff0c;是把混乱的数据排成从小到大或从大到小。 排序一共有十种左右&#xff0c;它们是&#xff1a;冒泡排序、…

Salesforce 发布开源大模型 xGen-MM

xGen-MM 论文 在当今 AI 技术飞速发展的时代&#xff0c;一个新的多模态 AI 模型悄然崛起&#xff0c;引起了业界的广泛关注。这个由 Salesforce 推出的开源模型—— xGen-MM&#xff0c;正以其惊人的全能特性和独特优势&#xff0c;在 AI 领域掀起一阵旋风。那么&#xff0c;x…

书生大模型(第3期)基础岛第5关--XTuner 微调个人小助手认知

XTuner微调前置基础 1 基本概念 在进行微调之前&#xff0c;我们需要了解一些基本概念。 1.1 Finetune简介 微调&#xff08;fine-tuning&#xff09;是一种基于预训练模型&#xff0c;通过少量的调整&#xff08;fine-tune&#xff09;来适应新的任务或数据的方法。 微调…

VUE3 无法修改 el-dialog 样式

用下面这种方式修改 el-dialog 组件样式一点作用都没有&#xff0c;正常用这种方式修改 el 的el-button、tab等都是百试不爽的。最后找到解决办法和原因。在el-dialog外面套一层div /deep/ .el-dialog { background: url(https://lanhu-oss.lanhuapp.com/7cbd761cd26f7b255086…

Cesium for Unreal——第四节 Transition Between Locations on the Globe 在两个位置间平稳飞行

文章目录 1. 创建或打开上次的项目2. 蓝图3. 构建一个新的关卡 Globel Level,添加墨尔本地形数据4. 选择蓝图文章参考与Cesium官网 Transition Between Locations on the Globe 1. 创建或打开上次的项目 学习之前,需要先安装——创建项目——运行 UE ,点击跳转 2. 蓝图 蓝图…

AI工具集合

AI工具集官网 | 1000 AI工具集合&#xff0c;国内外AI工具集导航大全

QT下显示自己派生的QWidget界面(提升为)

在实际开发过程中&#xff0c;我们可能有这样的需求&#xff0c;自己绘制一个仪表盘界面&#xff0c;然后将其贴到主界面上方。 这个时候就会用到“提升为”这个功能&#xff0c;该功能目的是将QWidget提升为自己派生的QWdiget子类&#xff0c;具体操作为&#xff0c;在主界面…

元数据管理gravitino学习

元数据管理的组成有几个部分&#xff1a;Metaservice(Gravitino)、Luoshu&#xff08;amoro)、Hive Metastore&#xff0c;其中gravitino是数据管理模块实现元数据统一管理的核心。前面有提到hive metastore可以存储hive的库表元数据信息&#xff0c;可以用于存储关于hive表、列…

19 自定义类型:结构体、联合体、枚举

目录 一、结构体 &#xff08;一&#xff09;结构体类型的定义 &#xff08;二&#xff09;结构体变量的创建和初始化 1、结构体变量的创建 &#xff08;1&#xff09;定义完结构体后再创建变量 &#xff08;2&#xff09;在定义结构体的同时创建变量 &#xff08;3&…

代码随想录算法day19 | 回溯算法part01 | 77. 组合,216.组合总和III,17.电话号码的字母组合

第77题. 组合 对着 在 回溯算法理论基础 给出的 代码模板&#xff0c;来做本题组合问题&#xff0c;大家就会发现 写回溯算法套路。 力扣题目链接(opens new window) 给定两个整数 n 和 k&#xff0c;返回 1 ... n 中所有可能的 k 个数的组合。 示例: 输入: n 4, k 2 输出: […

使用Xshell6远程登录Linux 服务器--远程上传下载文件Xftp6的使用

&#x1f600;前言 本篇博文是关于Linux 实操篇-使用Xshell6远程登录Linux 服务器–远程上传下载文件Xftp6的使用&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章…

“跨越国界,共赢未来:跨境联盟营销的策略与实践

全球化背景下跨境联盟营销的市场有很多机遇&#xff0c;随着全球化的深入发展&#xff0c;跨境电商市场不断扩大&#xff0c;为企业提供了广阔的市场空间。跨境联盟营销可以借助全球化的趋势&#xff0c;实现品牌国际化和市场拓展。随着大数据、人工智能、区块链等技术的不断发…

16款热门WMS 智能仓储管理系统盘点,助力企业数字化转型!

你是否想过&#xff0c;一个企业的仓库就如同其心脏般重要&#xff1f;而 WMS 智能仓储管理系统正是确保这颗 “心脏” 高效跳动的关键。它不仅能精准管理库存&#xff0c;实现货物的快速出入库&#xff0c;还能优化库位分配&#xff0c;提高仓库空间利用率。通过实时的数据监控…

【Linux】冯诺依曼体系|操作系统概念

目录 一、冯诺依曼体系结构 注意事项 存储器的意义&#xff1a;缓冲 数据流动事例 二、操作系统 操作系统的概念 操作系统的定位与目的 操作系统的管理 系统调用和库函数概念 一、冯诺依曼体系结构 冯诺依曼架构&#xff08;von Neumann architecture&#xff09;是一…