TensorRT Samples: MNIST API

2023-11-03

关于TensorRT的介绍可以参考:  http://blog.csdn.net/fengbingchun/article/details/78469551 

以下是参考TensorRT 2.1.2中的sampleMNISTAPI.cpp文件改写的实现对手写数字0-9识别的测试代码,各个文件内容如下:

common.hpp:

#ifndef FBC_TENSORRT_TEST_COMMON_HPP_
#define FBC_TENSORRT_TEST_COMMON_HPP_

#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <NvInfer.h>

template< typename T >
static inline int check_Cuda(T result, const char * const func, const char * const file, const int line)
{
	if (result) {
		fprintf(stderr, "Error CUDA: at %s: %d, error code=%d, func: %s\n", file, line, static_cast<unsigned int>(result), func);
		cudaDeviceReset(); // Make sure we call CUDA Device Reset before exiting
		return -1;
	}
}

template< typename T >
static inline int check(T result, const char * const func, const char * const file, const int line)
{
	if (result) {
		fprintf(stderr, "Error: at %s: %d, error code=%d, func: %s\n", file, line, static_cast<unsigned int>(result), func);
		return -1;
	}
}

#define checkCudaErrors(val) check_Cuda((val), __FUNCTION__, __FILE__, __LINE__)
#define checkErrors(val) check((val), __FUNCTION__, __FILE__, __LINE__)

#define CHECK(x) { \
	if (x) {} \
	else { fprintf(stderr, "Check Failed: %s, file: %s, line: %d\n", #x, __FILE__, __LINE__); return -1; } \
}

// Logger for GIE info/warning/errors
class Logger : public nvinfer1::ILogger			
{
	void log(Severity severity, const char* msg) override
	{
		// suppress info-level messages
		if (severity != Severity::kINFO)
			std::cout << msg << std::endl;
	}
};

#endif // FBC_TENSORRT_TEST_COMMON_HPP_
mnist_api.cpp:

#include <string>
#include <fstream>
#include <iostream>
#include <map>
#include <tuple>

#include <NvInfer.h>
#include <NvCaffeParser.h>
#include <cuda_runtime_api.h>
#include <opencv2/opencv.hpp>

#include "common.hpp"

// reference: TensorRT-2.1.2/samples/sampleMNIST/sampleMNISTAPI.cpp

// intput width, input height, output size, input blob name, output blob name, weight file, mean file
typedef std::tuple<int, int, int, std::string, std::string, std::string, std::string> DATA_INFO;

// Our weight files are in a very simple space delimited format.
// [type] [size] <data x size in hex> 
static std::map<std::string, nvinfer1::Weights> loadWeights(const std::string& file)
{
    std::map<std::string, nvinfer1::Weights> weightMap;
	std::ifstream input(file);
	if (!input.is_open()) {
		fprintf(stderr, "Unable to load weight file: %s\n", file.c_str());
		return  weightMap;
	}

    int32_t count;
    input >> count;
	if (count <= 0) {
		fprintf(stderr, "Invalid weight map file: %d\n", count);
		return weightMap;
	}

    while(count--) {
    	nvinfer1:: Weights wt{nvinfer1::DataType::kFLOAT, nullptr, 0};
    	uint32_t type, size;
        std::string name;
        input >> name >> std::dec >> type >> size;
        wt.type = static_cast<nvinfer1::DataType>(type);
        if (wt.type == nvinfer1::DataType::kFLOAT) {
            uint32_t *val = reinterpret_cast<uint32_t*>(malloc(sizeof(val) * size));
            for (uint32_t x = 0, y = size; x < y; ++x) {
                input >> std::hex >> val[x];
            }
            wt.values = val;
        } else if (wt.type == nvinfer1::DataType::kHALF) {
            uint16_t *val = reinterpret_cast<uint16_t*>(malloc(sizeof(val) * size));
            for (uint32_t x = 0, y = size; x < y; ++x) {
                input >> std::hex >> val[x];
            }
            wt.values = val;
        }
        wt.count = size;
        weightMap[name] = wt;
    }

    return weightMap;
}

// Creat the Engine using only the API and not any parser.
static nvinfer1::ICudaEngine* createMNISTEngine(unsigned int maxBatchSize, nvinfer1::IBuilder* builder, nvinfer1::DataType dt, const DATA_INFO& info)
{
	nvinfer1::INetworkDefinition* network = builder->createNetwork();

	//  Create input of shape { 1, 1, 28, 28 } with name referenced by INPUT_BLOB_NAME
	auto data = network->addInput(std::get<3>(info).c_str(), dt, nvinfer1::DimsCHW{ 1, std::get<1>(info), std::get<0>(info)});
	assert(data != nullptr);

	// Create a scale layer with default power/shift and specified scale parameter.
	float scale_param = 0.0125f;
	nvinfer1::Weights power{nvinfer1::DataType::kFLOAT, nullptr, 0};
	nvinfer1::Weights shift{nvinfer1::DataType::kFLOAT, nullptr, 0};
	nvinfer1::Weights scale{nvinfer1::DataType::kFLOAT, &scale_param, 1};
	auto scale_1 = network->addScale(*data,	nvinfer1::ScaleMode::kUNIFORM, shift, scale, power);
	assert(scale_1 != nullptr);

	// Add a convolution layer with 20 outputs and a 5x5 filter.
    std::map<std::string, nvinfer1::Weights> weightMap = loadWeights(std::get<5>(info));
	auto conv1 = network->addConvolution(*scale_1->getOutput(0), 20, nvinfer1::DimsHW{5, 5}, weightMap["conv1filter"], weightMap["conv1bias"]);
	assert(conv1 != nullptr);
	conv1->setStride(nvinfer1::DimsHW{1, 1});

	// Add a max pooling layer with stride of 2x2 and kernel size of 2x2.
	auto pool1 = network->addPooling(*conv1->getOutput(0), nvinfer1::PoolingType::kMAX, nvinfer1::DimsHW{2, 2});
	assert(pool1 != nullptr);
	pool1->setStride(nvinfer1::DimsHW{2, 2});

	// Add a second convolution layer with 50 outputs and a 5x5 filter.
	auto conv2 = network->addConvolution(*pool1->getOutput(0), 50, nvinfer1::DimsHW{5, 5}, weightMap["conv2filter"], weightMap["conv2bias"]);
	assert(conv2 != nullptr);
	conv2->setStride(nvinfer1::DimsHW{1, 1});

	// Add a second max pooling layer with stride of 2x2 and kernel size of 2x3>
	auto pool2 = network->addPooling(*conv2->getOutput(0), nvinfer1::PoolingType::kMAX, nvinfer1::DimsHW{2, 2});
	assert(pool2 != nullptr);
	pool2->setStride(nvinfer1::DimsHW{2, 2});

	// Add a fully connected layer with 500 outputs.
	auto ip1 = network->addFullyConnected(*pool2->getOutput(0), 500, weightMap["ip1filter"], weightMap["ip1bias"]);
	assert(ip1 != nullptr);

	// Add an activation layer using the ReLU algorithm.
	auto relu1 = network->addActivation(*ip1->getOutput(0), nvinfer1::ActivationType::kRELU);
	assert(relu1 != nullptr);

	// Add a second fully connected layer with 20 outputs.
	auto ip2 = network->addFullyConnected(*relu1->getOutput(0), std::get<2>(info), weightMap["ip2filter"], weightMap["ip2bias"]);
	assert(ip2 != nullptr);

	// Add a softmax layer to determine the probability.
	auto prob = network->addSoftMax(*ip2->getOutput(0));
	assert(prob != nullptr);
	prob->getOutput(0)->setName(std::get<4>(info).c_str());
	network->markOutput(*prob->getOutput(0));

	// Build the engine
	builder->setMaxBatchSize(maxBatchSize);
	builder->setMaxWorkspaceSize(1 << 20);

	auto engine = builder->buildCudaEngine(*network);
	// we don't need the network any more
	network->destroy();

	// Once we have built the cuda engine, we can release all of our held memory.
	for (auto &mem : weightMap) {
        free((void*)(mem.second.values));
    }

	return engine;
}

static int APIToModel(unsigned int maxBatchSize, // batch size - NB must be at least as large as the batch we want to run with)
		     nvinfer1::IHostMemory** modelStream, Logger logger, const DATA_INFO& info)
{
	// create the builder
	nvinfer1::IBuilder* builder = nvinfer1::createInferBuilder(logger);

	// create the model to populate the network, then set the outputs and create an engine
	nvinfer1::ICudaEngine* engine = createMNISTEngine(maxBatchSize, builder, nvinfer1::DataType::kFLOAT, info);
	CHECK(engine != nullptr);

	// serialize the engine, then close everything down
	(*modelStream) = engine->serialize();
	engine->destroy();
	builder->destroy();

	return 0;
}

static int doInference(nvinfer1::IExecutionContext& context, float* input, float* output, int batchSize, const DATA_INFO& info)
{
	const nvinfer1::ICudaEngine& engine = context.getEngine();
	// input and output buffer pointers that we pass to the engine - the engine requires exactly IEngine::getNbBindings(),
	// of these, but in this case we know that there is exactly one input and one output.
	CHECK(engine.getNbBindings() == 2);
	void* buffers[2];

	// In order to bind the buffers, we need to know the names of the input and output tensors.
	// note that indices are guaranteed to be less than IEngine::getNbBindings()
	int inputIndex = engine.getBindingIndex(std::get<3>(info).c_str()), 
	    outputIndex = engine.getBindingIndex(std::get<4>(info).c_str());

	// create GPU buffers and a stream
	checkCudaErrors(cudaMalloc(&buffers[inputIndex], batchSize * std::get<1>(info) * std::get<0>(info) * sizeof(float)));
	checkCudaErrors(cudaMalloc(&buffers[outputIndex], batchSize * std::get<2>(info) * sizeof(float)));

	cudaStream_t stream;
	checkCudaErrors(cudaStreamCreate(&stream));

	// DMA the input to the GPU,  execute the batch asynchronously, and DMA it back:
	checkCudaErrors(cudaMemcpyAsync(buffers[inputIndex], input, batchSize * std::get<1>(info) * std::get<0>(info) * sizeof(float), cudaMemcpyHostToDevice, stream));
	context.enqueue(batchSize, buffers, stream, nullptr);
	checkCudaErrors(cudaMemcpyAsync(output, buffers[outputIndex], batchSize * std::get<2>(info) * sizeof(float), cudaMemcpyDeviceToHost, stream));
	cudaStreamSynchronize(stream);

	// release the stream and the buffers
	cudaStreamDestroy(stream);
	checkCudaErrors(cudaFree(buffers[inputIndex]));
	checkCudaErrors(cudaFree(buffers[outputIndex]));

	return 0;
}

int test_mnist_api()
{
	Logger logger; // multiple instances of IRuntime and/or IBuilder must all use the same logger
	// stuff we know about the network and the caffe input/output blobs
	const DATA_INFO info(28, 28, 10, "data", "prob", "models/mnistapi.wts", "models/mnist_mean.binaryproto");

	// create a model using the API directly and serialize it to a stream
    nvinfer1::IHostMemory* modelStream{ nullptr };
    APIToModel(1, &modelStream, logger, info);

	// parse the mean file produced by caffe and subtract it from the image
	nvcaffeparser1::ICaffeParser* parser = nvcaffeparser1::createCaffeParser();
	nvcaffeparser1::IBinaryProtoBlob* meanBlob = parser->parseBinaryProto(std::get<6>(info).c_str());
	parser->destroy();
	const float* meanData = reinterpret_cast<const float*>(meanBlob->getData());

	nvinfer1::IRuntime* runtime = nvinfer1::createInferRuntime(logger);
	nvinfer1::ICudaEngine* engine = runtime->deserializeCudaEngine(modelStream->data(), modelStream->size(), nullptr);
	nvinfer1::IExecutionContext* context = engine->createExecutionContext();

	uint8_t fileData[std::get<1>(info) * std::get<0>(info)];
	const std::string image_path{ "images/digit/" };
	for (int i = 0; i < 10; ++i) {
		const std::string image_name = image_path + std::to_string(i) + ".png";
		cv::Mat mat = cv::imread(image_name, 0);
		if (!mat.data) {
			fprintf(stderr, "read image fail: %s\n", image_name.c_str());
			return -1;
		}

		cv::resize(mat, mat, cv::Size(std::get<0>(info), std::get<1>(info)));
		mat.convertTo(mat, CV_32FC1);

		float data[std::get<1>(info)*std::get<0>(info)];
		const float* p = (float*)mat.data;
		for (int j = 0; j < std::get<1>(info)*std::get<0>(info); ++j) {
			data[j] = p[j] - meanData[j];
		}

		// run inference
		float prob[std::get<2>(info)];
		doInference(*context, data, prob, 1, info);

		float val{-1.f};
		int idx{-1};

		for (int t = 0; t < std::get<2>(info); ++t) {
			if (val < prob[t]) {
				val = prob[t];
				idx = t;
			}
		}

		fprintf(stdout, "expected value: %d, actual value: %d, probability: %f\n", i, idx, val);
	}

	meanBlob->destroy();
    if (modelStream) modelStream->destroy();
	// destroy the engine
	context->destroy();
	engine->destroy();
	runtime->destroy();

	return 0;
}
测试图像如下:


执行结果如下:(与  http://blog.csdn.net/fengbingchun/article/details/78552908  中结果一致)


测试代码编译步骤如下(ReadMe.txt):

在Linux下通过CMake编译TensorRT_Test中的测试代码步骤:
1. 将终端定位到CUDA_Test/prj/linux_tensorrt_cmake,依次执行如下命令:
	$ mkdir build
	$ cd build
	$ cmake ..
	$ make (生成TensorRT_Test执行文件)
	$ ln -s ../../../test_data/models  ./ (将models目录软链接到build目录下)
	$ ln -s ../../../test_data/images  ./ (将images目录软链接到build目录下)
	$ ./TensorRT_Test
2. 对于有需要用OpenCV参与的读取图像的操作,需要先将对应文件中的图像路径修改为Linux支持的路径格式

GitHub: https://github.com/fengbingchun/CUDA_Test

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

TensorRT Samples: MNIST API 的相关文章

  • caffe代码阅读7:LayerRegistry的实现细节-2016.3.18

    一 LayerRegistry的作用简介 LayerResistry的功能很简单 就是将类和对应的字符串类型放入到一个map当中去 以便灵活调用 主要就是注册类的功能 二 LayerRegistry类的详细介绍 1 构造函数和析构函数 构造
  • ubuntu16.04 安装CUDA 8.0 和 cuDNN 5.1 /cudnn6.0,可适用于gpu版本的(tensorflow,caffe,mxnet)

    转载https zhuanlan zhihu com p 27890924文章 略有修改 感谢原作者 环境 ubuntu 16 04 GTX 960 安装步骤 安装Nvidia驱动 系统设置 gt 软件与更新 gt 附加驱动 如下图选择nv
  • Caffe源码中caffe.proto文件分析

    Caffe源码 caffe version 09868ac date 2015 08 15 中有一些重要文件 这里介绍下caffe proto文件 在src caffe proto目录下有一个caffe proto文件 proto目录下除了
  • Caffe sigmoid交叉熵损失

    我正在使用 sigmoid 交叉熵损失函数来解决多标签分类问题 如下所示本教程 然而 在他们的教程结果和我的结果中 输出预测都在范围内 Inf Inf 而 sigmoid 的范围是 0 1 sigmoid 仅在反向传播中处理吗 也就是说 前
  • Caffe:如果两层反向传播渐变到同一个底部斑点会发生什么?

    我想知道如果我有一个层生成一个底部斑点 该斑点进一步被两个后续层消耗 这两个层都会生成一些梯度来填充反向传播阶段的 Bottom diff 将两个梯度相加形成最终梯度吗 或者说 只有他们一个人能够活下去 根据我的理解 Caffe 层需要在用
  • 用于 Caffe 的 Python 还是 Matlab?

    我将致力于在 Caffe 中实现 DQN 和 Google DeepMind 的最新扩展 为此 我将编写一个模拟器 代替 Atari 模拟器 来为代理创建培训体验 我的问题是 Matlab 或 Python 的 Caffe 接口中哪一个最成
  • Caffe Sigmoid交叉熵损失层损失函数

    我正在查看Caffe的代码Sigmoid 交叉熵损失层 https github com BVLC caffe blob master src caffe layers sigmoid cross entropy loss layer cp
  • 在 Mac 上安装 Caffe 错误:“致命错误:找不到‘cblas.h’文件”

    我一直在关注本指南 http playittodeath ru how to install caffe on mac os x yosemite 10 10 4 安装在我的 El Capitan macbook pro 上 使用 CMak
  • CNN 上的快速损失收敛意味着什么?

    我正在两个不同的深度学习库 Caffe e Tensorflow 中训练两个 CNN AlexNet e GoogLeNet 该网络由每个图书馆的开发团队实施 here https github com BVLC caffe tree ma
  • Caffe 快照:.solverstate 与 .caffemodel

    训练网络时 每 N 次迭代拍摄的快照有两种形式 一个是 solverstate 文件 我想它就像它听起来的那样 存储损失函数和梯度的状态等 另一个是 caffemodel 文件 我知道它存储训练后的参数 如果您想要预训练的模型 caffem
  • caffe全卷积cnn - 如何使用裁剪参数

    我正在尝试为我的问题训练一个完全卷积网络 我正在使用实施https github com shelhamer fcn berkeleyvision org https github com shelhamer fcn berkeleyvis
  • 在caffe中定义新层时如何获取学习率或迭代次数

    我想当迭代次数达到一定次数时改变损失层中的损失计算方法 为了实现它 我认为我需要获取当前的学习率或迭代次数 然后我使用if短语选择是否改变损失计算方法 您可以添加一个成员变量咖啡类保存当前的学习率或迭代次数并在您想要的层中访问它 例如 要获
  • 无法导入caffe

    我执行了make pycaffe在 caffe 目录中并且运行良好 我也这样做了 gsamaras gsamaras A15 caffe python export PYTHONPATH PYTHONPATH home gsamaras c
  • 如何加载 caffe 模型并转换为 numpy 数组?

    我有一个 caffemodel 文件 其中包含 ethereon 的 caffe tensorflow 转换实用程序不支持的层 我想生成我的咖啡模型的 numpy 表示 我的问题是 如何将 caffemodel 文件 我还有 prototx
  • Caffe,在层中设置自定义权重

    I have a network In one place I want to use concat As on this picture 不幸的是 该网络无法训练 为了理解为什么我想连续改变权重 这意味着 FC4096 中的所有值一开始都
  • Caffe 的 LSTM 模块

    有谁知道 Caffe 是否有一个不错的 LSTM 模块 我从 russel91 的 github 帐户中找到了一个 但显然包含示例和解释的网页消失了 以前是http apollo deepmatter io http apollo deep
  • 如何在 Caffe 中从头开始训练 ResNet101 模型?

    我正在使用深度实验室 v2 https bitbucket org aquariusjay deeplab public ver2Caffe 版本 以便进行语义分割 我可以使用 imagenet 模型微调 ResNet101 但无法使用自定
  • caffe reshape / 上采样全连接层

    假设我们有一个像这样的层 layer name fully connected type InnerProduct bottom bottom top top inner product param num output 1 输出是batc
  • 通过 Caffe 中的层提供数据的多种路径

    我想在 Caffe 中构建一个网络 其中传入的数据最初被分割 分别通过同一组层 最后使用 eltwise 层重新组合 此后 所有部件将作为一个斑点移动 除了学习的参数之外 数据并行移动的网络部分的层配置将是相同的 有没有一种方法可以在 Ca
  • 使用Python构建caffe(找不到-lboost_python3)

    我正在尝试用 python 构建 caffe 但它一直这样说 CXX LD o python caffe caffe so python caffe caffe cpp usr bin ld cannot find lboost pytho

随机推荐

  • Linux设置自定义命令

    哪个用户使用 就在哪个用户的目录下进行下面操作即可 例如我的用户名是cgk 1 首先进入到cgk的目录下 2 打开bashrc文件 3 将想要添加的自定义命令写入后保存即可 例 alias jup jupyter lab port 8889
  • exception is java.sql.SQLDataException: Unsupported conversion from TIMESTAMP to java.lang.Long

    org springframework dao DataIntegrityViolationException Error attempting to get column operate time from result set Caus
  • [Spring Boot]05 国内Java开发者必备的两个提速神器:Maven的国内镜像、Spring的国内脚手架

    目录 一 Maven的国内镜像 二 Spring的国内脚手架 给 Java 开发者推荐两个提速神器 一 Maven的国内镜像 Maven的国内镜像 官网地址 http maven aliyun com 配置方法很简单 只需要找到 Maven
  • Python+selenium学习

    maximize window 最大化浏览器和刷新当前页面refresh from selenium import webdriver driver webdriver Firefox driver get https www baidu
  • WordPress使用【前端投稿】功能时为用户怎么添加插入文章标签

    在使用Wordpress做前端投稿功能的时候 可能需要用户填写文章标签 在插入文章的时候很多人不知道怎么把这些标签插入进去 下面这篇文章来为大家带来WordPress使用前端投稿功能时插入文章标签方法 在Wordpress里 wp inse
  • [力扣] 剑指 Offer 07. 重建二叉树-----Java

    题目 剑指 Offer 07 重建二叉树 例子 preorder 3 9 20 15 7 inorder 9 3 15 20 7 分析 1 我们知道前序遍历 那么前序遍历的第一个数一定是根结点 也就是 3 一定是根结点 2 我们可以找到中序
  • 【JS逆向】破解xx志愿headers中u-sign加密参数

    注意 文章内容仅用于学习和技术交流 如有侵权请联系我删除 学者应洁身自好 切勿做出违法的事情 旨在提供逆向思路 aHR0cHM6Ly93d3cueW91enkuY24vdHp5L3NlYXJjaC9jb2xsZWdlcy9jb2xsZWdl
  • linux下wifi的sta和ap操作

    前言 在linux开发中wifi是很常见的一个工作 wifi有STA模式和AP模式 今天分享下如何使用工具在Linux中控制wifi 作者 良知犹存 转载授权以及围观 欢迎关注微信公众号 羽林君 或者添加作者个人微信 become me 介
  • [网络安全]xss-labs level-4 解题详析

    读者可参考 订阅专栏 Xss Labs靶场攻防实战 姿势 逻辑后端代码 使用 str replace 分别将 gt 和 lt 替换为空字符串 该题使用onblur onfocus事件绕过 onblur onfocus事件 onblur 事件
  • 本地化部署大语言模型 ChatGLM

    本地化部署大语言模型 ChatGLM 本地化部署大语言模型 ChatGLM 前期筹备 GitHub 基础包 语言模型文件 基础配置 显存查看方法 Anaconda 模块 ChatGLM 6B 网页部署 Anaconda 环境创建 根目录操作
  • HTML编程之js跨域抓取问题

    对于网站建设来说 通常会需要遇到js跨域抓取的问题 那么对于初学者来说 都不知道该怎么来解决 对此 郑州网站建设公司燚轩科技就来跟大家好好的分析解答一下吧 源代码如下
  • Unix and perl primer for Biologists - Part1 :Unix - Learning the Essentials - Reading Notes(U13-U24)

    U13 Time to tidy up use the rmdir command this will only remove empty directories U14 The art of typing less to do more
  • 分页乱码问题(解决)

    上一个blog中 粗略的使用了下分页标签使用 pg使用问题还是有很多 如 分页中乱码 多java代码块下面用一个
  • CUDA Samples: Streams' usage

    以下CUDA sample是分别用C 和CUDA实现的流的使用code 并对其中使用到的CUDA函数进行了解说 code参考了 GPU高性能编程CUDA实战 一书的第十章 各个文件内容如下 funset cpp include funset
  • 关于示波器地线的问题!

    用信号发生器给板子输入信号 则示波器一般只能用于测试电路上某个节点和地之间的波形 如果测两个节点之间的波形 则探头上的地线可能会将地线后面其余的电路短路掉 所以 要想测两个节点间的波形 要合理的变换一下电路形式 或者做一些用于测试的附加电路
  • 以太坊的私钥、公钥、地址、账户之间的关系总结

    在以太坊中 账户 地址 私钥 Private Key 和公钥 Public Key 是非常重要的概念 账户扮演着以太坊的中心角色 地址是我们与以太坊系统进行交互的标识 它是以太坊账户与外界进行交互的名字 而私钥与公钥是保护我们账户安全的重要
  • 微搭低代码学习之数据展示

    低代码平台是一个快速发展的领域 未来有着广阔的发展前景 以下是一些低代码平台未来的发展方向 1 人工智能和机器学习 随着人工智能和机器学习技术的不断发展 低代码平台将能够更好地利用这些技术来提高应用程序的智能化和自动化水平 例如 低代码平台
  • 如何动态生成antd vue的menu组件

    目录 1 需求背景 2 解决方案 3 代码示例 1 需求背景 需要根据动态生成垂直菜单组件 数据来源的一级菜单可能包含二级子菜单 也可能不包含二级子菜单 难度在于是否包含二级子菜单决定着生成
  • 开源自动化测试平台介绍一览

    以下为小编整理的 各类自动化测试平台 供大家工作中自己参考 一 autotestplat 作者 fin 功能 接口自动化测试 性能测试 appui自动化测试 webui自动化测试 源码 https github com testdevhom
  • TensorRT Samples: MNIST API

    关于TensorRT的介绍可以参考 http blog csdn net fengbingchun article details 78469551 以下是参考TensorRT 2 1 2中的sampleMNISTAPI cpp文件改写的实