当前位置: 首页 > article >正文

深入解析ncnn::Net类——高效部署神经网络的核心组件

在这里插入图片描述

最近在学习ncnn推理框架,下面整理了ncnn::Net 的使用方法。

在移动端和嵌入式设备上进行高效的神经网络推理,要求框架具备轻量化、高性能以及灵活的扩展能力。作为腾讯开源的高性能神经网络推理框架,ncnn在这些方面表现出色。而在ncnn的核心组件中,ncnn::Net类扮演了至关重要的角色。本文将详细介绍ncnn::Net类的结构、功能及其使用方法,帮助开发者更好地理解和利用这一强大的工具。

目录

  1. Net类简介
  2. 构造与析构
  3. Option选项配置
  4. 自定义层注册
  5. 加载网络参数与模型
  6. 输入与输出管理
  7. 创建Extractor
  8. Vulkan支持
  9. 内存管理与优化
  10. 实例代码
  11. 使用示例
  12. 总结

1. Net类简介

ncnn::Net类是ncnn框架中用于管理和执行神经网络的主要接口。它负责加载网络结构和权重、配置推理选项、管理层与Blob等关键资源,并提供了与外部数据交互的接口。通过Net类,开发者可以方便地加载预训练模型、进行前向推理并获取结果,适用于多种应用场景,如图像分类、目标检测和语义分割等。

namespace ncnn {class NCNN_EXPORT Net
{
public:// 构造函数Net();// 析构函数virtual ~Net();public:// 配置选项Option opt;// Vulkan相关设置#if NCNN_VULKANvoid set_vulkan_device(int device_index);void set_vulkan_device(const VulkanDevice* vkdev);const VulkanDevice* vulkan_device() const;#endif // NCNN_VULKAN// 自定义层注册int register_custom_layer(const char* type, layer_creator_func creator, layer_destroyer_func destroyer = 0, void* userdata = 0);virtual int custom_layer_to_index(const char* type);int register_custom_layer(int index, layer_creator_func creator, layer_destroyer_func destroyer = 0, void* userdata = 0);// 加载参数与模型int load_param_bin(const DataReader& dr);int load_model(const DataReader& dr);#if NCNN_STDIO#if NCNN_STRINGint load_param(FILE* fp);int load_param(const char* protopath);int load_param_mem(const char* mem);#endif // NCNN_STRINGint load_param_bin(FILE* fp);int load_param_bin(const char* protopath);int load_model(FILE* fp);int load_model(const char* modelpath);#endif // NCNN_STDIO// 清除网络void clear();// 创建提取器Extractor create_extractor() const;// 获取输入输出信息const std::vector<int>& input_indexes() const;const std::vector<int>& output_indexes() const;#if NCNN_STRINGconst std::vector<const char*>& input_names() const;const std::vector<const char*>& output_names() const;#endifconst std::vector<Blob>& blobs() const;const std::vector<Layer*>& layers() const;std::vector<Blob>& mutable_blobs();std::vector<Layer*>& mutable_layers();protected:// 自定义层创建virtual Layer* create_custom_layer(int index);virtual Layer* create_overwrite_builtin_layer(int typeindex);private:Net(const Net&);Net& operator=(const Net&);private:NetPrivate* const d;
};} // namespace ncnn

2. 构造与析构

Net类提供了默认构造函数和析构函数,用于初始化和销毁网络对象。在构造函数中,网络的内部资源会被初始化,而析构函数则负责释放所有分配的资源,确保没有内存泄漏。

ncnn::Net net; // 创建Net对象
// 使用net进行操作// Net对象在作用域结束时自动析构

3. Option选项配置

Net类中的Option成员用于配置网络的各种参数,如线程数、内存分配器、是否启用Vulkan计算等。这些选项可以在加载网络结构和权重之前进行设置,以优化推理性能和资源使用。

Option类概述

Option类定义在option.h文件中,包含了多个可配置选项,如下所示:

namespace ncnn {class Allocator;
#if NCNN_VULKAN
class VkAllocator;
class PipelineCache;
#endif // NCNN_VULKANclass NCNN_EXPORT Option
{
public:Option();public:bool lightmode;int num_threads;Allocator* blob_allocator;Allocator* workspace_allocator;#if NCNN_VULKANVkAllocator* blob_vkallocator;VkAllocator* workspace_vkallocator;VkAllocator* staging_vkallocator;PipelineCache* pipeline_cache;
#endif // NCNN_VULKANint openmp_blocktime;bool use_winograd_convolution;bool use_sgemm_convolution;bool use_int8_inference;bool use_vulkan_compute;bool use_bf16_storage;bool use_fp16_packed;bool use_fp16_storage;bool use_fp16_arithmetic;bool use_int8_packed;bool use_int8_storage;bool use_int8_arithmetic;bool use_packing_layout;bool use_shader_pack8;bool use_subgroup_basic;bool use_subgroup_vote;bool use_subgroup_ballot;bool use_subgroup_shuffle;bool use_image_storage;bool use_tensor_storage;bool use_reserved_0;int flush_denormals;bool use_local_pool_allocator;bool use_shader_local_memory;bool use_cooperative_matrix;bool use_winograd23_convolution;bool use_winograd43_convolution;bool use_winograd63_convolution;bool use_a53_a55_optimized_kernel;bool use_reserved_7;bool use_reserved_8;bool use_reserved_9;bool use_reserved_10;bool use_reserved_11;
};} // namespace ncnn

配置选项示例

ncnn::Net net;
ncnn::Option opt;// 启用轻量模式
opt.lightmode = true;// 设置线程数为4
opt.num_threads = 4;// 启用Vulkan计算
opt.use_vulkan_compute = true;// 使用自定义内存分配器
CustomAllocator alloc;
opt.blob_allocator = &alloc;
opt.workspace_allocator = &alloc;// 将配置应用到网络
net.opt = opt;

4. 自定义层注册

ncnn框架允许开发者注册自定义层,以扩展框架的功能或实现特定的操作。register_custom_layer函数用于注册自定义层,支持通过层类型名称或索引进行注册。

通过层类型名称注册

#if NCNN_STRINGint register_custom_layer(const char* type, layer_creator_func creator, layer_destroyer_func destroyer = 0, void* userdata = 0);virtual int custom_layer_to_index(const char* type);
#endif // NCNN_STRING

通过层索引注册

int register_custom_layer(int index, layer_creator_func creator, layer_destroyer_func destroyer = 0, void* userdata = 0);

使用示例

假设我们有一个自定义层MyCustomLayer,其创建和销毁函数分别为create_my_custom_layerdestroy_my_custom_layer

// 定义创建函数
Layer* create_my_custom_layer();
void destroy_my_custom_layer(Layer* layer);ncnn::Net net;// 通过层类型名称注册
net.register_custom_layer("MyCustomLayer", create_my_custom_layer, destroy_my_custom_layer);// 或者通过层索引注册(假设MyCustomLayer的索引为100)
net.register_custom_layer(100, create_my_custom_layer, destroy_my_custom_layer);

5. 加载网络参数与模型

Net类提供了多种加载网络参数和模型文件的方法,支持从文件系统和内存中加载网络结构(参数)和权重(模型)。以下将详细介绍这些方法。

加载参数

参数文件定义了网络的结构,包括各层的类型、序列及其连接关系。参数文件可以是文本格式(param)或二进制格式(param.bin)。

// 加载二进制参数文件
int ret = net.load_param_bin("model.param.bin");
if (ret != 0) {// 处理错误
}// 加载内存中的参数数据
unsigned char* param_mem = ...; // 参数数据内存指针
int consumed = net.load_param(param_mem);

加载模型

模型文件包含了网络的权重数据。模型文件通常为二进制格式(model.bin),也可以从内存中加载。

// 加载二进制模型文件
int ret = net.load_model("model.bin");
if (ret != 0) {// 处理错误
}// 加载内存中的模型数据
unsigned char* model_mem = ...; // 模型数据内存指针
int consumed = net.load_model(model_mem);

从文件加载参数与模型

#if NCNN_STDIO#if NCNN_STRING// 从文件路径加载参数int ret = net.load_param("model.param");#endif // NCNN_STRING// 从文件路径加载二进制参数int ret = net.load_param_bin("model.param.bin");// 从文件路径加载模型ret = net.load_model("model.bin");
#endif // NCNN_STDIO

从Android Asset加载(仅适用于Android平台)

#if NCNN_PLATFORM_API && __ANDROID_API__ >= 9#if NCNN_STRING// 从Android Asset加载参数文件net.load_param(asset);net.load_param(asset_mgr, "path/to/model.param");// 从Android Asset加载二进制参数文件net.load_param_bin(asset);net.load_param_bin(asset_mgr, "path/to/model.param.bin");#endif // NCNN_STRING// 从Android Asset加载模型文件net.load_model(asset);net.load_model(asset_mgr, "path/to/model.bin");
#endif // NCNN_PLATFORM_API

6. 输入与输出管理

ncnn::Net类通过Blob索引和名称管理输入和输出。开发者可以通过索引或名称设置输入数据,并提取推理结果。

获取输入输出索引与名称

const std::vector<int>& input_indexes = net.input_indexes();
const std::vector<int>& output_indexes = net.output_indexes();#if NCNN_STRINGconst std::vector<const char*>& input_names = net.input_names();const std::vector<const char*>& output_names = net.output_names();
#endif

通过索引设置输入和提取输出

ncnn::Extractor ex = net.create_extractor();// 设置输入
ex.input(0, input_mat); // 通过Blob索引设置// 提取输出
ncnn::Mat output_mat;
ex.extract(0, output_mat); // 通过Blob索引提取

通过名称设置输入和提取输出(需要开启NCNN_STRING)

#if NCNN_STRINGncnn::Extractor ex = net.create_extractor();// 设置输入ex.input("input_blob_name", input_mat); // 通过Blob名称设置// 提取输出ncnn::Mat output_mat;ex.extract("output_blob_name", output_mat); // 通过Blob名称提取
#endif

7. 创建Extractor

Extractor类是用于执行前向推理的实例,通过Net类创建。Extractor支持多线程、内存分配器设置以及Vulkan加速等配置。

创建Extractor

ncnn::Extractor ex = net.create_extractor();

配置Extractor选项

ex.set_light_mode(true); // 启用轻量模式,回收中间Blob
ex.set_num_threads(4); // 设置Extractor的线程数// 设置自定义内存分配器
CustomAllocator alloc;
ex.set_blob_allocator(&alloc);
ex.set_workspace_allocator(&alloc);

8. Vulkan支持

ncnn支持基于Vulkan的GPU加速,通过VkAllocator和相关配置,可以在支持Vulkan的设备上利用GPU进行高效推理。

设置Vulkan设备

#if NCNN_VULKAN// 通过设备索引设置Vulkan设备net.set_vulkan_device(0); // 使用第一个Vulkan设备// 或者通过VulkanDevice句柄设置const VulkanDevice* vkdev = ...;net.set_vulkan_device(vkdev);
#endif // NCNN_VULKAN

设置Extractor的Vulkan选项

#if NCNN_VULKANncnn::Extractor ex = net.create_extractor();ex.set_vulkan_compute(true); // 启用Vulkan计算// 设置Vulkan内存分配器VkAllocator vkalloc;ex.set_blob_vkallocator(&vkalloc);ex.set_workspace_vkallocator(&vkalloc);ex.set_staging_vkallocator(&vkalloc);
#endif // NCNN_VULKAN

9. 内存管理与优化

ncnn通过引用计数、自定义内存分配器和内存池复用等机制,实现高效的内存管理。同时,OptionExtractor类提供了多种配置选项,帮助开发者根据需求优化内存使用和推理性能。

自定义内存分配器

开发者可以实现自定义的Allocator,以满足特定的内存管理需求或优化策略。

class CustomAllocator : public ncnn::Allocator 
{
public:virtual void* fastMalloc(size_t size) override {// 自定义内存分配逻辑return malloc(size);}virtual void fastFree(void* ptr) override {// 自定义内存释放逻辑free(ptr);}
};CustomAllocator alloc;
ncnn::Option opt;
opt.blob_allocator = &alloc;
opt.workspace_allocator = &alloc;
net.opt = opt;

内存池复用与对齐

通过内存池复用和自动内存对齐,ncnn提高了内存利用率和数据访问效率,减少了内存分配和释放的开销。

class PoolAllocator : public ncnn::Allocator 
{std::vector<std::pair<size_t, void*>> freed_memory;
public:virtual void* fastMalloc(size_t size) override {for (auto it = freed_memory.begin(); it != freed_memory.end(); ++it) {if (it->first >= size) {void* ptr = it->second;freed_memory.erase(it);return ptr;}}return malloc(size);}virtual void fastFree(void* ptr) override {size_t size = /* 获取ptr对应的大小 */;freed_memory.emplace_back(size, ptr);}
};PoolAllocator pool_alloc;
ncnn::Option opt;
opt.blob_allocator = &pool_alloc;
opt.workspace_allocator = &pool_alloc;
net.opt = opt;

10. 实例代码

以下是一个完整的示例,展示如何使用ncnn::Net类加载模型、设置Option、创建Extractor并进行推理。

#include "net.h"
#include "mat.h"
#include <stdio.h>int main() {// 创建Net对象ncnn::Net net;// 配置选项ncnn::Option opt;opt.lightmode = true;opt.num_threads = 4;opt.use_winograd_convolution = true;opt.use_sgemm_convolution = true;opt.use_int8_inference = true;opt.use_packing_layout = true;net.opt = opt;// 加载参数和模型if (net.load_param("model.param") != 0) {printf("Failed to load param file\n");return -1;}if (net.load_model("model.bin") != 0) {printf("Failed to load model file\n");return -1;}// 创建Extractorncnn::Extractor ex = net.create_extractor();ex.set_num_threads(4);ex.set_light_mode(true);// 准备输入数据ncnn::Mat in = ncnn::Mat::from_pixels_resize(image_data, ncnn::Mat::PIXEL_RGB, width, height, 224, 224);in.substract_mean_normalize(mean_vals, norm_vals);// 设置输入ex.input("input_blob", in);// 提取输出ncnn::Mat out;ex.extract("output_blob", out);// 处理输出float* ptr = out;for (int i = 0; i < out.w; i++) {printf("Class %d: %f\n", i, ptr[i]);}return 0;
}

11. 使用示例

下面是一个更具体的示例,展示如何加载一个预训练的图像分类模型,并对一张图片进行推理。

#include "net.h"
#include "mat.h"
#include <stdio.h>// 假设有一个函数读取图片并返回RGB数据
unsigned char* load_image(const char* image_path, int& width, int& height);int main() {// 创建Net对象ncnn::Net net;// 配置选项ncnn::Option opt;opt.lightmode = true;opt.num_threads = 4;opt.use_int8_inference = true;net.opt = opt;// 加载网络结构和模型if (net.load_param("mobilenet_v1.param") != 0) {printf("加载参数文件失败\n");return -1;}if (net.load_model("mobilenet_v1.bin") != 0) {printf("加载模型文件失败\n");return -1;}// 创建Extractorncnn::Extractor ex = net.create_extractor();// 加载并预处理图片int width, height;unsigned char* image_data = load_image("test.jpg", width, height);if (!image_data) {printf("无法加载图片\n");return -1;}ncnn::Mat in = ncnn::Mat::from_pixels_resize(image_data, ncnn::Mat::PIXEL_RGB, width, height, 224, 224);in.substract_mean_normalize(ncnn::Mat(3, 1, (float[]) {104.f, 117.f, 123.f}), // 均值ncnn::Mat(3, 1, (float[]) {1.f, 1.f, 1.f})      // 归一化);// 设置输入ex.input("data", in);// 提取输出ncnn::Mat out;ex.extract("prob", out);// 找到概率最高的分类float max_score = out[0];int max_index = 0;for (int i = 1; i < out.w; i++) {if (out[i] > max_score) {max_score = out[i];max_index = i;}}printf("预测结果: 类别索引 = %d, 置信度 = %f\n", max_index, max_score);// 释放图片数据free(image_data);return 0;
}

12. 总结

ncnn::Net类作为ncnn框架的核心组件,具备以下显著特点:

  1. 灵活的选项配置:通过Option类,开发者可以细粒度地配置推理参数,如线程数、内存分配策略、优化选项等。
  2. 高效的内存管理:支持自定义内存分配器、内存池复用和自动对齐,确保内存的高效利用和访问效率。
  3. 扩展性强:允许开发者注册自定义层,扩展框架功能,以适应特定的应用需求。
  4. 支持多种数据加载方式:支持从文件系统和内存中加载网络结构和权重,适用于不同的应用场景。
  5. Vulkan加速支持:通过Vulkan实现GPU加速,提升推理性能,特别适用于移动设备和嵌入式系统。
  6. 简便的输入输出管理:通过Blob索引和名称管理输入和输出,提供多种设置和提取接口,便于集成和使用。
  7. 易于使用的接口:提供简洁的API接口,结合丰富的示例代码,降低了神经网络部署的门槛。

通过本文的详细介绍,相信您已经对ncnn::Net类有了全面的了解。无论是在资源受限的移动设备上进行高效推理,还是在嵌入式系统中实现复杂的神经网络应用,ncnn::Net类都能为您提供强大的支持和高效的性能表现。


希望这篇博客能帮助您更好地理解和使用ncnn::Net类,在您的深度学习项目中发挥更大的作用。如果您有任何问题或建议,欢迎在评论区留言讨论!

相关文章:

深入解析ncnn::Net类——高效部署神经网络的核心组件

最近在学习ncnn推理框架&#xff0c;下面整理了ncnn::Net 的使用方法。 在移动端和嵌入式设备上进行高效的神经网络推理&#xff0c;要求框架具备轻量化、高性能以及灵活的扩展能力。作为腾讯开源的高性能神经网络推理框架&#xff0c;ncnn在这些方面表现出色。而在ncnn的核心…...

前端【8】HTML+CSS+javascript实战项目----实现一个简单的待办事项列表 (To-Do List)

目录 一、功能需求 二、 HTML 三、CSS 四、js 1、绑定事件与初始设置 2.、绑定事项 &#xff08;1&#xff09;添加操作&#xff1a; &#xff08;2&#xff09;完成操作 &#xff08;3&#xff09;删除操作 &#xff08;4&#xff09;修改操作 3、完整js代码 总结…...

程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图<1>

大家好啊&#xff0c;我是小象٩(๑ω๑)۶ 我的博客&#xff1a;Xiao Xiangζั͡ޓއއ 很高兴见到大家&#xff0c;希望能够和大家一起交流学习&#xff0c;共同进步。 这一节我们来学习指针的相关知识&#xff0c;学习内存和地址&#xff0c;指针变量和地址&#xff0c;包…...

向量和矩阵算法笔记

向量和矩阵算法笔记 Ps:因为本人实力有限,有一部分可能不太详细,若有补充评论区回复,QWQ 向量 向量的定义 首先,因为我刚刚学到高中的向量,对向量的看法呢就是一条有长度和方向的线,不过这在数学上的定义其实是不对,甚至跟我看的差别其实有点大,真正的定义就是数域…...

Nginx配置中的常见错误:SSL参数解析

摘要 在高版本的Nginx中&#xff0c;用户可能会遇到unknown directive “ssl”的错误提示。这是因为旧版本中使用的ssl on参数已被弃用。正确的配置SSL加密的方法是在listen指令中添加ssl参数。这一改动简化了配置流程&#xff0c;提高了安全性。用户应更新配置文件以适应新版本…...

積分方程與簡單的泛函分析6.有連續對稱核的弗雷德霍姆積分算子的特徵值

1)def弗雷德霍姆算子的核函數定義 定义: 设 是定义在矩形区域 上的函数。 若满足以下条件,则称 为弗雷德霍姆算子的核函数: 实值性: 是实值函数,即对于任意的 ,。 这是因为在许多实际的物理和数学问题中,所涉及的量往往是实数值,例如在积分方程描述的物理模型中,…...

AI编程工具使用技巧:在Visual Studio Code中高效利用阿里云通义灵码

AI编程工具使用技巧&#xff1a;在Visual Studio Code中高效利用阿里云通义灵码 前言一、通义灵码介绍1.1 通义灵码简介1.2 主要功能1.3 版本选择1.4 支持环境 二、Visual Studio Code介绍1.1 VS Code简介1.2 主要特点 三、安装VsCode3.1下载VsCode3.2.安装VsCode3.3 打开VsCod…...

Yii框架中的扩展:如何使用外部库

在Yii框架中&#xff0c;扩展功能的一种常见且有效的方式是使用外部库。这些外部库可以帮助开发者实现特定的功能&#xff0c;如调用第三方API、处理图片、生成PDF文件或发送邮件等。以下是使用外部库扩展Yii框架的详细步骤&#xff1a; 一、安装外部库 使用Composer&#xff…...

kettle与Springboot的集成方法,完整支持大数据组件

目录 概要整体架构流程技术名词解释技术细节小结 概要 在现代数据处理和ETL&#xff08;提取、转换、加载&#xff09;流程中&#xff0c;Kettle&#xff08;Pentaho Data Integration, PDI&#xff09;作为一种强大的开源ETL工具&#xff0c;被广泛应用于各种数据处理场景。…...

GitHub Actions 使用需谨慎:深度剖析其痛点与替代方案

在持续集成与持续部署&#xff08;CI/CD&#xff09;领域&#xff0c;GitHub Actions 曾是众多开发者的热门选择&#xff0c;但如今&#xff0c;其弊端逐渐显现&#xff0c;让不少人在使用前不得不深思熟虑。 团队由大约 15 名工程师组成&#xff0c;采用基于主干的开发方式&am…...

<iframe>标签和定时调用函数setInterval

iframe 标签和定时调用函数 setInterval 问题描述&#xff1a;解决方法&#xff1a; 问题描述&#xff1a; 今天遇到一个前端问题&#xff0c;在浏览器页面上传Excel文件后&#xff0c;然后点击导入按钮&#xff0c;经后端Java类读取文件内容校验后&#xff0c;将校验结果返回…...

MYSQL学习笔记(六):聚合函数、sql语句执行原理简要分析

前言&#xff1a; 学习和使用数据库可以说是程序员必须具备能力&#xff0c;这里将更新关于MYSQL的使用讲解&#xff0c;大概应该会更新30篇&#xff0c;涵盖入门、进阶、高级(一些原理分析);这一篇是内容较少&#xff0c;主要讲解&#xff1a;聚合函数和简要介绍sql语句执行过…...

Qt——界面优化

一.QSS 1.背景 在网页前端开发领域中&#xff0c; CSS 是⼀个至关重要的部分。 描述了⼀个网页的 "样式"。 从而起到对网页美化的作用。 所谓样式&#xff0c;包括不限于大小&#xff0c;位置&#xff0c;颜色&#xff0c;背景&#xff0c;间距&#xff0c;字体等等…...

MySQL 数据库常见字段类型大全及详细解析

在工作期间会遇到数据库建表的业务&#xff0c;经常会使用复制粘帖等操作&#xff0c;而不清楚数据库的字段类型。本文记录了 MySQL 数据库常见字段类型&#xff0c;根据不同的数据需求&#xff0c;可以选择不同的字段类型来存储数据。 文章目录 一、整数类型1、TINYINT&#x…...

MATLAB 工具库的使用说明和案例示例

以下是一些常见的 MATLAB 工具库的使用说明和案例示例&#xff1a; 信号处理工具箱&#xff08;Signal Processing Toolbox&#xff09;&#xff1a; 使用说明&#xff1a;提供了用于生成、测量、变换、过滤和可视化信号的函数和应用程序。包括重新采样、平滑、同步信号、设计…...

对于Docker的初步了解

简介与概述 1.不需要安装环境&#xff0c;工具包包含了环境&#xff08;jdk等&#xff09; 2.打包好&#xff0c;“一次封装&#xff0c;到处运行” 3.跨平台&#xff0c;docker容器在任何操作系统上都是一致的&#xff0c;这就是实现跨平台跨服务器。只需要一次配置好环境&…...

java基础学习——jdbc基础知识详细介绍

引言 数据的存储 我们在开发 java 程序时&#xff0c;数据都是存储在内存中的&#xff0c;属于临时存储&#xff0c;当程序停止或重启时&#xff0c;内存中的数据就会丢失&#xff0c;我们为了解决数据的长期存储问题&#xff0c;有以下解决方案&#xff1a; 通过 IO流书记&…...

第20篇:Python 开发进阶:使用Django进行Web开发详解

第20篇&#xff1a;使用Django进行Web开发 内容简介 在上一篇文章中&#xff0c;我们深入探讨了Flask框架的高级功能&#xff0c;并通过构建一个博客系统展示了其实际应用。本篇文章将转向Django&#xff0c;另一个功能强大且广泛使用的Python Web框架。我们将介绍Django的核…...

CukeTest使用 | 1 CukeTest是什么?如何下载安装?

CukeTest使用 | 1 CukeTest是什么&#xff1f;如何下载安装&#xff1f; 1 CukeTest是什么&#xff1f;2 关于开发者3 CukeTest有哪些特性&#xff1f;4 都支持哪些自动化技术类型&#xff1f;5 版本区别6 下载安装 特殊说明&#xff1a;学习内容主要来自官网的教程、以及网上公…...

An OpenGL Toolbox

3.An OpenGL Toolbox 声明&#xff1a;该代码来自&#xff1a;Computer Graphics Through OpenGL From Theory to Experiments&#xff0c;仅用作学习参考 3.1 Vertex Arrays and Their Drawing Commands 顶点数组及其绘制命令&#xff1a;将几何数据存储在一个位置&#xff0c…...

R语言学习笔记之高效数据操作

一、概要 数据操作是R语言的一大优势&#xff0c;用户可以利用基本包或者拓展包在R语言中进行复杂的数据操作&#xff0c;包括排序、更新、分组汇总等。R数据操作包&#xff1a;data.table和tidyfst两个扩展包。 data.table是当前R中处理数据最快的工具&#xff0c;可以实现快…...

构造函数及实例化的过程:实例成员

构造函数:是一种特殊的函数&#xff0c;主要用来初始化对象 使用场景:比如我们创建了佩奇的对象&#xff0c;继续创建乔治的对象还需要重新写一遍&#xff0c;此时可以通过构造函数来快速创建多个类似的对象。 构造函数语法:大写字母开头的函数 function Pig(name,age,gender)…...

Linux的权限和一些shell原理

目录 shell的原理 Linux权限 sudo命令提权 权限 文件的属性 ⽂件类型&#xff1a; 基本权限&#xff1a; chmod改权限 umask chown 该拥有者 chgrp 改所属组 最后&#xff1a; 目录权限 粘滞位 shell的原理 我们广义上的Linux系统 Linux内核Linux外壳 Linux严格…...

LearnOpenGL——光照

教程地址&#xff1a;简介 - LearnOpenGL CN 前言 这篇开始光照的学习。 颜色 原文链接&#xff1a; 颜色 - LearnOpenGL CN总结&#xff1a; 重新搭建了一个简单场景&#xff0c;为后面的学习做准备。 现实世界中有无数种颜色&#xff0c;每一个物体都有它们自己的颜色。我…...

落地 ORB角点检测与sift检测

ORB角点检测 可以说ORB是由FAST、灰度质心和BRIEF等技术组合优化形成的&#xff0c;不过更准确地说&#xff0c;ORB是在FAST特征检测算法基础上&#xff0c;结合了灰度质心确定方向以及改进后的BRIEF描述子等技术形成的&#xff0c;以下是具体分析&#xff1a; • FAST特征检…...

步入响应式编程篇(二)之Reactor API

步入响应式编程篇&#xff08;二&#xff09;之Reactor API 前言回顾响应式编程Reactor API的使用Stream引入依赖Reactor API的使用流源头的创建 reactor api的背压模式发布者与订阅者使用的线程查看弹珠图查看形成新流的日志 前言 对于响应式编程的基于概念&#xff0c;以及J…...

利用Redis实现数据缓存

目录 1 为啥要缓存捏&#xff1f; 2 基本流程&#xff08;以查询商铺信息为例&#xff09; 3 实现数据库与缓存双写一致 3.1 内存淘汰 3.2 超时剔除&#xff08;半自动&#xff09; 3.3 主动更新&#xff08;手动&#xff09; 3.3.1 双写方案 3.3.2 读写穿透方案 3.3.…...

如何使用 pytest-html 创建自定义 HTML 测试报告

关注开源优测不迷路 大数据测试过程、策略及挑战 测试框架原理&#xff0c;构建成功的基石 在自动化测试工作之前&#xff0c;你应该知道的10条建议 在自动化测试中&#xff0c;重要的不是工具 测试 Python 代码对于提高代码质量、检测漏洞或意外行为至关重要。 但测试结果又该…...

服务器中的流量主要是指什么?

服务器流量就是指服务器在单位时间内所传输的数据量&#xff0c;服务器流量在互联网中起着十分重要的作用&#xff0c;一般会被用来处理网站的访问请求&#xff0c;当用户在网站中浏览网页和视频时&#xff0c;服务器会接收到用户的请求&#xff0c;同时会返回网站的内容。 服务…...

飞牛NAS安装过程中的docker源问题

采用CloudFlare进行飞牛NAS的远程访问 【安全免费】无需公网IP、端口号&#xff0c;NAS外网访问新方法_网络存储_什么值得买 sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<EOF {"registry-mirrors": ["https://docker.1panel.dev&quo…...