全流程机器视觉工程开发(四)PaddleDetection C++工程化应用部署到本地DLL以供软件调用

news2024/11/28 8:34:05

前言

我们之前跑了一个yolo的模型,然后我们通过PaddleDetection的库对这个模型进行了一定程度的调用,但是那个调用还是基于命令的调用,这样的库首先第一个不能部署到客户的电脑上,第二个用起来也非常不方便,那么我们可不可以直接将Paddle的库直接做成一个DLL部署到我们的软件上呢?答案是可以的,接下来我就会全流程地完成这一操作。

流程

部署流程主要参考了几个文档:
Visual Studio 2019 Community CMake 编译指南
PaddleDetection部署c++测试图片视频 (win10+vs2017)

流程的话主要有以下几个步骤

  1. 编译opencv
  2. 下载cuda库(如果之前安装了cudnn和cuda toolkit,那就不用管这一块,我这里演示cpu版本的安装)
  3. 下载PaddlePaddle C++ 预测库 paddle_inference
  4. 编译

内容

编译opencv

这里直接略过吧,这个没什么好说的,而且在我们上文中全流程机器视觉工程开发(三)任务前瞻 - 从opencv的安装编译说起,到图像增强和分割

我们已经完成了opencv的安装和配置,这里就不多说了

下载cuda库

这里由于使用的是CPU版本的推理库,所以这里也直接略过。

下载推理库paddle_inference

在这里插入图片描述

下载安装预测库

下载后解压即可,找个地方摆好备用。

  1. 编译

编译的时候找到PaddleDetection库下的 \PaddleDetection\deploy\cpp 目录
在这里插入图片描述
使用CMake GUI打开这个文件夹,会有很多地方爆红,按照文档中给定的要求填就可以了

在这里插入图片描述在这里插入图片描述
由于我这里没有用CUDA,所以只用填OPencv_dir和paddle_dir即可,如果你不想每次都填,可以直接改写cpp文件夹下的CMakeList.txt

将11 -17行中改成你想要的,比如我下面的改法:


option(WITH_MKL        "Compile demo with MKL/OpenBlas support,defaultuseMKL."          ON)
#我这里不开GPU,所以这个改成off
option(WITH_GPU        "Compile demo with GPU/CPU, default use CPU."                    OFF)
option(WITH_TENSORRT   "Compile demo with TensorRT."                                    OFF)

option(WITH_KEYPOINT        "Whether to Compile KeyPoint detector"                    OFF)
option(WITH_MOT       "Whether to Compile MOT detector" OFF)

#SET(PADDLE_DIR "" CACHE PATH "Location of libraries")
#SET(PADDLE_LIB_NAME "" CACHE STRING "libpaddle_inference")
#SET(OPENCV_DIR "" CACHE PATH "Location of libraries")
#SET(CUDA_LIB "" CACHE PATH "Location of libraries")
#SET(CUDNN_LIB "" CACHE PATH "Location of libraries")
#SET(TENSORRT_INC_DIR "" CACHE PATH "Compile demo with TensorRT")
#SET(TENSORRT_LIB_DIR "" CACHE PATH "Compile demo with TensorRT")
SET(PADDLE_DIR "D:\\WorkShop\\Python\\paddle_inference")
SET(PADDLE_LIB_NAME "paddle_inference")
SET(OPENCV_DIR "C:\\Program Files (x86)\\opencv")
include(cmake/yaml-cpp.cmake)

include_directories("${CMAKE_SOURCE_DIR}/")
include_directories("${CMAKE_CURRENT_BINARY_DIR}/ext/yaml-cpp/src/ext-yaml-cpp/include")
link_directories("${CMAKE_CURRENT_BINARY_DIR}/ext/yaml-cpp/lib")

重新在gui里面configure一下这个工程,就会变成这样:

在这里插入图片描述
点击generate,就在build 文件夹下生成了工程
在这里插入图片描述

在这里插入图片描述

这时候直接build一下总的解决方案就可以了

常见错误

未定义标识符CV_xxxx_xxxx

这个是因为在opencv新版本中将这些标识符都改名了,现在将这些未定义的标识符从CV_xxx_xxx改成cv::xxx_xxx即可,比如:
在这里插入图片描述
改为:
在这里插入图片描述
把报错的地方都改一下就可以了

无法打开源文件 “yaml-cpp/yaml.h”

这个是main工程里的CMakeList.txt的问题,将25行左右按照如下修改一下就可以了:

# 尽量不要用CMAKE_CURRENT_BINARY_DIR,改用CMAKE_BINARY_DIR
#include_directories("${CMAKE_CURRENT_BINARY_DIR}/ext/yaml-cpp/src/ext-yaml-cpp/include")
message(STATUS ".123123${CMAKE_BINARY_DIR}/ext/yaml-cpp/src/ext-yaml-cpp/include")
include_directories(${CMAKE_BINARY_DIR}/ext/yaml-cpp/src/ext-yaml-cpp/include)
#link_directories("${CMAKE_CURRENT_BINARY_DIR}/ext/yaml-cpp/lib")
link_directories(${CMAKE_BINARY_DIR}/ext/yaml-cpp/lib)

无法找到libyaml-cppmt.lib

请使用Release编译,貌似这个库不支持debug编译,因为这里引用似乎没有引用libyaml-cppmtd.lib

修改

我们现在就编译过了这个库,但是现在编译完成的结果其实还有个问题,那就是这玩意还是个exe,当然了我并不需要exe,我希望我的东西是个dll,这样就可以集成到别的项目去了。

这里提一嘴,我这里编译出来的库没有用CMake管理,因为接口比较简单,所以就是直接用windows的那种lib+dll的形式导出的,没有让 cmake直接管理。因为用CMake直接管理引用的话,会比较麻烦,容易造成一些不必要的问题。用静态链接的方式的话,反正直接链接上去掉接口就完了,只要头文件里面不要包含paddle的东西,管你内容是什么呢?

我这里给main函数添加了一个头文件:


//extern "C" __declspec(dllexport) int  main_test();
#include <iostream>
#include <vector>
extern "C" __declspec(dllexport) int add(int a, int b);

int add(int a, int b) {
	return a + b;
}
struct ObjDetector {
	std::string model_dir;
	const std::string device = "CPU";
	bool use_mkldnn = false; int cpu_threads = 1;
	std::string run_mode = std::string("paddle");
	int batch_size = 1; int gpu_id = 0;
	int trt_min_shape = 1;
	int trt_max_shape = 1280;
	int trt_opt_shape = 640;
	bool trt_calib_mode = false;
};


using namespace std;


class __declspec(dllexport) Lev_ModelInfer
{

public:
	void PrintBenchmarkLog(std::vector<double> det_time, int img_num);
	static string DirName(const std::string& filepath);
	static bool PathExists(const std::string& path);
	static void MkDir(const std::string& path);
	static void MkDirs(const std::string& path);
	void GetAllFiles(const char* dir_name, std::vector<std::string>& all_inputs);
	//void PredictVideo(const std::string& video_path, PaddleDetection::ObjectDetector* det);
	void PredictImage_(const std::vector<std::string> all_img_paths,
		const int batch_size,
		const double threshold,
		const bool run_benchmark,
		ObjDetector det,
		const std::string& output_dir = "output");
private:

};

同时修改了main函数如下:

//   Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


#include "Leventure_ModelInfer.h"
#include "include/object_detector.h"
#include <glog/logging.h>

#include <dirent.h>
#include <iostream>
#include <string>
#include <vector>
#include <numeric>
#include <sys/types.h>
#include <sys/stat.h>
#include <math.h>
#include <filesystem>
#ifdef _WIN32
#include <direct.h>
#include <io.h>
#elif LINUX
#include <stdarg.h>
#include <sys/stat.h>
#endif

#include <gflags/gflags.h>






void PrintBenchmarkLog(std::vector<double> det_time, int img_num) {
}
void Lev_ModelInfer::PrintBenchmarkLog(std::vector<double> det_time, int img_num) {
	PrintBenchmarkLog(det_time, img_num);
}
string Lev_ModelInfer::DirName(const std::string& filepath) {
	auto pos = filepath.rfind(OS_PATH_SEP);
	if (pos == std::string::npos) {
		return "";
	}
	return filepath.substr(0, pos);
}


bool Lev_ModelInfer::PathExists(const std::string& path) {
#ifdef _WIN32
	struct _stat buffer;
	return (_stat(path.c_str(), &buffer) == 0);
#else
	struct stat buffer;
	return (stat(path.c_str(), &buffer) == 0);
#endif  // !_WIN32
}


void Lev_ModelInfer::MkDir(const std::string& path) {
	if (PathExists(path)) return;
	int ret = 0;
#ifdef _WIN32
	ret = _mkdir(path.c_str());
#else
	ret = mkdir(path.c_str(), 0755);
#endif  // !_WIN32
	if (ret != 0) {
		std::string path_error(path);
		path_error += " mkdir failed!";
		throw std::runtime_error(path_error);
	}
}

void Lev_ModelInfer::MkDirs(const std::string& path) {
	if (path.empty()) return;
	if (PathExists(path)) return;

	MkDirs(DirName(path));
	MkDir(path);
}


void Lev_ModelInfer::GetAllFiles(const char* dir_name,
	std::vector<std::string>& all_inputs) {
	if (NULL == dir_name) {
		std::cout << " dir_name is null ! " << std::endl;
		return;
	}
	struct stat s;
	stat(dir_name, &s);
	if (!S_ISDIR(s.st_mode)) {
		std::cout << "dir_name is not a valid directory !" << std::endl;
		all_inputs.push_back(dir_name);
		return;
	}
	else {
		struct dirent* filename; // return value for readdir()
		DIR* dir;                // return value for opendir()
		dir = opendir(dir_name);
		if (NULL == dir) {
			std::cout << "Can not open dir " << dir_name << std::endl;
			return;
		}
		std::cout << "Successfully opened the dir !" << std::endl;
		while ((filename = readdir(dir)) != NULL) {
			if (strcmp(filename->d_name, ".") == 0 ||
				strcmp(filename->d_name, "..") == 0)
				continue;
			all_inputs.push_back(dir_name + std::string("/") +
				std::string(filename->d_name));
		}
	}
}


//void Lev_ModelInfer::PredictVideo(const std::string& video_path,
//	PaddleDetection::ObjectDetector* det) {
//	// Open video
//	cv::VideoCapture capture;
//	if (camera_id != -1) {
//		capture.open(camera_id);
//	}
//	else {
//		capture.open(video_path.c_str());
//	}
//	if (!capture.isOpened()) {
//		printf("can not open video : %s\n", video_path.c_str());
//		return;
//	}
//
//	// Get Video info : resolution, fps
//	int video_width = static_cast<int>(capture.get(CV_CAP_PROP_FRAME_WIDTH));
//	int video_height = static_cast<int>(capture.get(CV_CAP_PROP_FRAME_HEIGHT));
//	int video_fps = static_cast<int>(capture.get(CV_CAP_PROP_FPS));
//
//	// Create VideoWriter for output
//	cv::VideoWriter video_out;
//	std::string video_out_path = "output.avi";
//	video_out.open(video_out_path.c_str(),
//		CV_FOURCC('D', 'I', 'V', 'X'),
//		video_fps,
//		cv::Size(video_width, video_height),
//		true);
//	std::cout << video_out.isOpened();
//	if (!video_out.isOpened()) {
//		printf("create video writer failed!\n");
//		return;
//	}
//
//	std::vector<PaddleDetection::ObjectResult> result;
//	std::vector<int> bbox_num;
//	std::vector<double> det_times;
//	auto labels = det->GetLabelList();
//	auto colormap = PaddleDetection::GenerateColorMap(labels.size());
//	// Capture all frames and do inference
//	cv::Mat frame;
//	int frame_id = 0;
//	bool is_rbox = false;
//	int icu = 0;
//	while (capture.read(frame)) {
//		icu += 1;
//		std::cout << icu << "frame" << std::endl;
//		if (frame.empty()) {
//			break;
//		}
//		std::vector<cv::Mat> imgs;
//		imgs.push_back(frame);
//		det->Predict(imgs, 0.5, 0, 1, &result, &bbox_num, &det_times);
//		for (const auto& item : result) {
//			if (item.rect.size() > 6) {
//				is_rbox = true;
//				printf("class=%d confidence=%.4f rect=[%d %d %d %d %d %d %d %d]\n",
//					item.class_id,
//					item.confidence,
//					item.rect[0],
//					item.rect[1],
//					item.rect[2],
//					item.rect[3],
//					item.rect[4],
//					item.rect[5],
//					item.rect[6],
//					item.rect[7]);
//			}
//			else {
//				printf("class=%d confidence=%.4f rect=[%d %d %d %d]\n",
//					item.class_id,
//					item.confidence,
//					item.rect[0],
//					item.rect[1],
//					item.rect[2],
//					item.rect[3]);
//			}
//		}
//
//		cv::Mat out_im = PaddleDetection::VisualizeResult(
//			frame, result, labels, colormap, is_rbox);
//
//		video_out.write(out_im);
//		frame_id += 1;
//	}
//	capture.release();
//	video_out.release();
//}



void PredictImage(const std::vector<std::string> all_img_paths,
	const int batch_size,
	const double threshold,
	const bool run_benchmark,
	PaddleDetection::ObjectDetector* det,
	const std::string& output_dir) {
	std::vector<double> det_t = { 0, 0, 0 };
	int steps = ceil(float(all_img_paths.size()) / batch_size);
	printf("total images = %d, batch_size = %d, total steps = %d\n",
		all_img_paths.size(), batch_size, steps);
	for (int idx = 0; idx < steps; idx++) {
		std::vector<cv::Mat> batch_imgs;
		int left_image_cnt = all_img_paths.size() - idx * batch_size;
		if (left_image_cnt > batch_size) {
			left_image_cnt = batch_size;
		}
		for (int bs = 0; bs < left_image_cnt; bs++) {
			std::string image_file_path = all_img_paths.at(idx * batch_size + bs);
			cv::Mat im = cv::imread(image_file_path, 1);
			batch_imgs.insert(batch_imgs.end(), im);
		}

		// Store all detected result
		std::vector<PaddleDetection::ObjectResult> result;
		std::vector<int> bbox_num;
		std::vector<double> det_times;
		bool is_rbox = false;
		if (run_benchmark) {
			det->Predict(batch_imgs, threshold, 10, 10, &result, &bbox_num, &det_times);
		}
		else {
			det->Predict(batch_imgs, threshold, 0, 1, &result, &bbox_num, &det_times);
			// get labels and colormap
			auto labels = det->GetLabelList();
			auto colormap = PaddleDetection::GenerateColorMap(labels.size());

			int item_start_idx = 0;
			for (int i = 0; i < left_image_cnt; i++) {
				std::cout << all_img_paths.at(idx * batch_size + i) << "result" << std::endl;
				if (bbox_num[i] <= 1) {
					continue;
				}
				for (int j = 0; j < bbox_num[i]; j++) {
					PaddleDetection::ObjectResult item = result[item_start_idx + j];
					if (item.rect.size() > 6) {
						is_rbox = true;
						printf("class=%d confidence=%.4f rect=[%d %d %d %d %d %d %d %d]\n",
							item.class_id,
							item.confidence,
							item.rect[0],
							item.rect[1],
							item.rect[2],
							item.rect[3],
							item.rect[4],
							item.rect[5],
							item.rect[6],
							item.rect[7]);
					}
					else {
						printf("class=%d confidence=%.4f rect=[%d %d %d %d]\n",
							item.class_id,
							item.confidence,
							item.rect[0],
							item.rect[1],
							item.rect[2],
							item.rect[3]);
					}
				}
				item_start_idx = item_start_idx + bbox_num[i];
			}
			// Visualization result
			int bbox_idx = 0;
			for (int bs = 0; bs < batch_imgs.size(); bs++) {
				if (bbox_num[bs] <= 1) {
					continue;
				}
				cv::Mat im = batch_imgs[bs];
				std::vector<PaddleDetection::ObjectResult> im_result;
				for (int k = 0; k < bbox_num[bs]; k++) {
					im_result.push_back(result[bbox_idx + k]);
				}
				bbox_idx += bbox_num[bs];
				cv::Mat vis_img = PaddleDetection::VisualizeResult(
					im, im_result, labels, colormap, is_rbox);
				std::vector<int> compression_params;
				compression_params.push_back(CV_IMWRITE_JPEG_QUALITY);
				compression_params.push_back(95);
				std::string output_path = output_dir + "\\";
				std::string image_file_path = all_img_paths.at(idx * batch_size + bs);
				output_path += std::filesystem::path(image_file_path).filename().string();
				cv::imwrite(output_path, vis_img, compression_params);
				printf("Visualized output saved as %s\n", output_path.c_str());
				//std::string output_path(output_dir);
				//if (output_dir.rfind(OS_PATH_SEP) != output_dir.size() - 1) {
				//	output_path += OS_PATH_SEP;
				//}
				//std::string image_file_path = all_img_paths.at(idx * batch_size + bs);
				//output_path += image_file_path.substr(image_file_path.find_last_of('/') + 1);
				//cv::imwrite(output_path, vis_img, compression_params);
				//printf("Visualized output saved as %s\n", output_path.c_str());
			}
		}
		det_t[0] += det_times[0];
		det_t[1] += det_times[1];
		det_t[2] += det_times[2];
	}
	//PrintBenchmarkLog(det_t, all_img_paths.size());
}
void Lev_ModelInfer::PredictImage_(const std::vector<std::string> all_img_paths,
	const int batch_size,
	const double threshold,
	const bool run_benchmark,
	ObjDetector det,
	const std::string& output_dir) {
	PaddleDetection::ObjectDetector* model = new PaddleDetection::ObjectDetector(
		det.model_dir, det.device, det.use_mkldnn, det.cpu_threads, det.run_mode, det.batch_size, det.trt_min_shape
		, det.trt_max_shape, det.trt_opt_shape, det.trt_calib_mode
	);

	PredictImage(all_img_paths, batch_size, threshold, run_benchmark, model, output_dir);
}
//std::string model_dir;
//std::string image_file;
//std::string video_file;
//std::string image_dir;
//int batch_size = 1;
//bool use_gpu = true;
//int camera_id = -1;
//double threshold = 0.1;
//std::string output_dir = "output";
//std::string run_mode = "fluid";
//int gpu_id = 0;
//bool run_benchmark = false;
//bool use_mkldnn = false;
//double cpu_threads = 0.9;
//bool use_dynamic_shape = false;
//int trt_min_shape = 1;
//int trt_max_shape = 1280;
//int trt_opt_shape = 640;
//bool trt_calib_mode = false;
//
//int main_test() {
//	model_dir = "D:/projects/PaddleDetection/deploy/cpp/out/Release/models";
//	image_file = "D:/projects/PaddleDetection/deploy/cpp/out/Release/images/1.jpg";
//	//video_file = "bb.mp4";
//	//image_dir = "";
//  // Parsing command-line
//  //google::ParseCommandLineFlags(&argc, &argv, true);
//
//	if (model_dir.empty()
//		|| (image_file.empty() && image_dir.empty() && video_file.empty())) {
//		std::cout << "Usage: ./main --model_dir=/PATH/TO/INFERENCE_MODEL/ "
//			<< "--image_file=/PATH/TO/INPUT/IMAGE/" << std::endl;
//	}
//	if (!(run_mode == "fluid" || run_mode == "trt_fp32"
//		|| run_mode == "trt_fp16" || run_mode == "trt_int8")) {
//		std::cout << "run_mode should be 'fluid', 'trt_fp32', 'trt_fp16' or 'trt_int8'.";
//		return -1;
//	}
//	// Load model and create a object detector
//	PaddleDetection::ObjectDetector det(model_dir, use_gpu, use_mkldnn,
//		threshold, run_mode, batch_size, gpu_id, use_dynamic_shape,
//		trt_min_shape, trt_max_shape, trt_opt_shape, trt_calib_mode);
//	// Do inference on input video or image
//	MyClass predictvideo;
//	if (!video_file.empty() || camera_id != -1) {
//		predictvideo.PredictVideo(video_file, &det);
//	}
//	else if (!image_file.empty() || !image_dir.empty()) {
//		if (!predictvideo.PathExists(output_dir)) {
//			predictvideo.MkDirs(output_dir);
//		}
//		std::vector<std::string> all_imgs;
//		if (!image_file.empty()) {
//			all_imgs.push_back(image_file);
//			if (batch_size > 1) {
//				std::cout << "batch_size should be 1, when image_file is not None" << std::endl;
//				batch_size = 1;
//			}
//		}
//		else {
//			predictvideo.GetAllFiles((char*)image_dir.c_str(), all_imgs);
//		}
//		predictvideo.PredictImage(all_imgs, batch_size, threshold, run_benchmark, &det, output_dir);
//	}
//	return 0;
//}


int main(int argc, char** argv) {
}

详情见本人github仓库:

添加链接描述

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

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

相关文章

vue使用es的reduce方法编译报错Error: Can‘t resolve ‘core-js/modules/es.array.reduce.js‘

哈喽 大家好啊 最近在vue使用es的reduce方法编译报错Error: Cant resolve core-js/modules/es.array.reduce.js 报错如图所示&#xff1a; 解决方案&#xff1a; npm install --save core-js 然后重新编译下将正常了 参考原文: 使用import异步加载语法报错_module not foun…

2024年:用OKR管理你的生活

在科技高速发展的时代&#xff0c;越来越多的企业和团队开始采用OKR&#xff08;Objectives and Key Results&#xff09;管理方法来设定目标并跟踪进度。你是否想过&#xff0c;将OKR理念引入个人生活&#xff0c;以更有效地实现人生目标&#xff1f;本文将探讨如何在2024年运…

网络规划与部署实训

一 实训目的及意义 本周实训主要是了解网络规划与部署&#xff0c;熟悉三大厂商华为、思科、锐捷交换机路由器以及相关协议的原理和配置&#xff0c;提高学生的动手能力和分析规划部署能力。 实训主要针对计算机网络系统集成的设计与实现的实际训练&#xff0c;着重锻炼学生熟练…

让cgteamwork自动为Houdini载入相机,角色道具的abc文件

一 需求 最近接到个需求&#xff1a;在创建EFX文件时&#xff0c;自动加载动画出的缓存abc文件相机&#xff0c; 不用手动一个个的载入&#xff0c;还容易出错 ABC文件自动导入到Houndini里 二 过程/效果 在CGTeamwork里打开对应的镜头&#xff0c;下面的文件列表显示相机和角…

大型软件编程实例分享,诊所门诊处方笺管理系统多台电脑同时使用的软件教程

大型软件编程实例分享&#xff0c;诊所门诊处方笺管理系统多台电脑同时使用的软件教程 一、前言 以下教程以 佳易王诊所门诊电子处方管理系统V17.2 为例说明 软件资源可以点击最下方官网卡片了解详情 软件左侧为导航栏 1、系统参数设置&#xff1a;可以设置打印等参数 2、…

zabbix配置监控脚本

zabbix配置监控脚本 1.修改agent配置文件 [rootchang ~]# vim /etc/zabbix/zabbix_agentd.conf 333行 原# UnsafeUserParameters0 修改成 UnsafeUserParameters12.创建脚本与脚本存放目录 [rootchang ~]# mkdir /etc/zabbix/zabbix_scripts [rootchang zabbix_scripts]# vi…

BUUCTF-Real-ThinkPHP]5.0.23-Rce

漏洞介绍 这个版本容易存在我们都喜欢的rce漏洞&#xff01; 网站为了提高访问效率往往会将用户访问过的页面存入缓存来减少开销。而Thinkphp 在使用缓存的时候是将数据序列化&#xff0c;然后存进一个 php 文件中&#xff0c;这使得命令执行等行为成为可能&#xff01; ThinkP…

2.0 Hadoop 运行环境

由于 Hadoop 是为集群设计的软件&#xff0c;所以我们在学习它的使用时难免会遇到在多台计算机上配置 Hadoop 的情况&#xff0c;这对于学习者来说会制造诸多障碍&#xff0c;主要有两个&#xff1a; 昂贵的计算机集群。多计算机构成的集群环境需要昂贵的硬件.难以部署和维护。…

物联网与智慧景区的未来:机遇与挑战并存

随着科技的不断发展&#xff0c;物联网技术在智慧景区中的应用越来越广泛&#xff0c;为旅游业带来了巨大的变革。然而&#xff0c;在物联网与智慧景区的未来发展中&#xff0c;机遇与挑战并存。本文将探讨物联网与智慧景区面临的机遇和挑战&#xff0c;并提出应对措施&#xf…

【npm】修改npm全局安装包的位置路径

问题 全局安装的默认安装路径为&#xff1a;C:\Users\admin\AppData\Roaming\npm&#xff0c;缓存路径为&#xff1a;C:\Users\admin\AppData\Roaming\npm_cache&#xff08;其中admin为自己的用户名&#xff09;。 由于默认的安装路径在C盘&#xff0c;太浪费C盘内存啦&#…

LeetCode--121

121. 买卖股票的最佳时机 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从…

1.0 Hadoop 教程

Hadoop 是一个开源的分布式计算和存储框架&#xff0c;由 Apache 基金会开发和维护。 Hadoop 为庞大的计算机集群提供可靠的、可伸缩的应用层计算和存储支持&#xff0c;它允许使用简单的编程模型跨计算机群集分布式处理大型数据集&#xff0c;并且支持在单台计算机到几千台计…

智慧港口:山海鲸可视化引领未来

随着疫情的结束&#xff0c;全球贸易迎来新的春天&#xff0c;港口作为物流枢纽的地位日益凸显。然而&#xff0c;传统港口的运营和管理方式已无法满足现代物流的需求。为了提高港口运营效率&#xff0c;降低成本&#xff0c;智慧港口的概念应运而生。作为山海鲸可视化的开发者…

适用于您站点的12个免费模态窗口库和插件

1. jQuery模态 寻找超级干净且易于使用的东西吗&#xff1f;然后jQuery Modal应该是您的首选资源。 这个可访问的插件可与键盘快捷键&#xff08;ESC关闭&#xff09;一起使用&#xff0c;甚至支持触摸操作。总库重约1KB&#xff0c;非常小。 更不用说实际的设计足够干净&…

路由引入路由过滤

目录 路由引入 什么是路由引入&#xff1f; 为什么需要路由引入&#xff1f; 路由引入的规划分为两种 路由过滤 路由过滤的工具 前缀列表格式 filter-policy router-policy 路由引入 什么是路由引入&#xff1f; 将一种协议导入到另一种协议或在同种协议的不同进程…

2023年全球软件架构师峰会(ArchSummit上海站):核心内容与学习收获(附大会核心PPT下载)

微服务架构是当今软件架构的主流趋势之一。随着云计算和分布式系统的普及&#xff0c;越来越多的企业开始采用微服务架构来构建他们的应用。微服务架构可以将一个大型的应用拆分成多个小型的服务&#xff0c;每个服务都独立部署、独立运行&#xff0c;并通过轻量级的通信协议进…

Openresty+Lua+Redis实现高性能缓存

一、背景 当我们的程序需要提供较高的并发访问时&#xff0c;往往需要在程序中引入缓存技术&#xff0c;通常都是使用Redis作为缓存&#xff0c;但是要再更进一步提升性能的话&#xff0c;就需要尽可能的减少请求的链路长度&#xff0c;比如可以将访问Redis缓存从Tomcat服务器…

计算机设计大赛 深度学习 植物识别算法系统

文章目录 0 前言2 相关技术2.1 VGG-Net模型2.2 VGG-Net在植物识别的优势(1) 卷积核&#xff0c;池化核大小固定(2) 特征提取更全面(3) 网络训练误差收敛速度较快 3 VGG-Net的搭建3.1 Tornado简介(1) 优势(2) 关键代码 4 Inception V3 神经网络4.1 网络结构 5 开始训练5.1 数据集…

Electron实战(二):将Node.js和UI能力(app/BrowserWindow/dialog)等注入html

文章目录 设置webPreferences参数安装electron/remotemain进程中初始化html中使用dialog踩坑参考文档 上一篇&#xff1a;Electron实战(一)&#xff1a;环境搭建/Hello World/打包exe 设置webPreferences参数 为了能够在html/js中访问Node.js提供fs等模块&#xff0c;需要在n…

生物发酵展同期论坛|2024节能环保绿色低碳发展论坛

“十四五”规划中提出&#xff0c;提高工业、能源领城智能化与信息 化融合&#xff0c;明确“低碳经济”新的战略目标&#xff0c;热能产业是能源产 业和民生保障的重要组成部分&#xff0c;也是二氧化碳排放量大的行业 之一&#xff0c;产业高效、清洁、低碳、灵活、智能化水平…