VS2015 + OpenCV + OnnxRuntime-Cpp + YOLOv8 部署

  • 近期有个工作需求是进行 YOLOv8 模型的 C++ 部署,部署环境如下
    1. 系统:Windows
    2. IDE:VS2015
    3. 语言:C++
    4. OpenCV 4.5.0
    5. OnnxRuntime 1.15.1

0. 预训练模型保存为 .onnx 格式

  • 假设已经有使用 ultralytics 库训练并保存为 .pt 格式的 YOLOv8 模型,将其转换为 .onnx 格式是简单的
    import os
    import sys
    base_path = os.path.abspath(os.path.join(os.path.dirname(__file__),))
    sys.path.append(base_path)from ultralytics import YOLO# Load a YOLOv8 model
    model = YOLO(f"{base_path}/model/output/best.pt")# Export the model
    model.export(format="onnx", opset=9, simplify=True, dynamic=False, imgsz=640)
    

    注意导出时设置了 opset=9,这个版本可以和 OnnxRuntime 1.15.1 匹配

  • 除了 .onnx 模型文件以外,还需要准备数据集的 .yaml 文件和用于测试的图片

1. 依赖下载

1.1 OpenCV

  • 在 window 上用 Cmake 从源码编译 OpenCV 很麻烦,直接下载 release 库
  • 下载地址:OpenCV-4.5.0
    在这里插入图片描述
    下载后得到 opencv-4.5.0-vc14_vc15.exe,双击解压。把 C:\Users…\opencv\build\x64\vc14\bin 添加到环境变量。其中 vc14 对应 vs2015
  • 提取的文件中,以下是我们之后需要的
    1. 头文件:C:\Users…\opencv\build\include\opencv2
    2. 动态库:C:\Users…\opencv\build\x64\vc14\bin\opencv_world450.dll
    3. 静态库:C:\Users…\opencv\build\x64\vc14\lib\opencv_world450.lib & opencv_world450d.lib

      opencv_world450.lib 用于 vs release 模式;opencv_world450d.lib 用于 vs debug 模式

1.2 OnnxRuntime

  • 下载地址:ONNX Runtime v1.15.1,下载 onnxruntime-win-x64-1.15.1.zip,下载后直接解压。
  • 提取的文件中,以下是我们之后需要的
    1. 头文件:C:\Users…\onnxruntime-win-x64-1.15.1\include
    2. 动态库:C:\Users…\onnxruntime-win-x64-1.15.1\lib 下的所有 .dll 文件
    3. 静态库:C:\Users…\onnxruntime-win-x64-1.15.1\lib 下的所有 .lib 文件

1.3 Cpp 源码

  • 下载地址:YOLOv8 OnnxRuntime C++。这是 ultralytics 提供的官方案例,注意其依赖
    在这里插入图片描述
    由于 vs2015 无法设置 C++17 标准,后续会修改源码,去掉其中使用的 filesystem 库,由于仅部署 CPU 版本,无需 Cuda 和 cuDNN。另外这个 readme 还提到了 Cmake 编译,我们不需要做这步,直接用它的源码就行了

2. VS2015 工程配置

2.1 创建项目

  • 首先新建 Win32 控制台项目,选择空项目。我这里项目路径为 YOLOv8-Test,项目名为 Test。打开工程根目录,新建 bin、lib、include 三个目录
    在这里插入图片描述
    • 在 bin 中粘贴 1.2 和 1.3 节提到的所有动态库文件
      在这里插入图片描述
    • 在 lib 中粘贴 1.2 和 1.3 节提到的所有静态库文件
      在这里插入图片描述
    • 在 include 中粘贴 1.2 和 1.3 节提到的所有头文件目录
      在这里插入图片描述
      其中 onnxruntime-win-x64 就是 C:\Users…\onnxruntime-win-x64-1.15.1\include 文件夹
  • 把源码粘贴到根目录,并添加到项目中
    在这里插入图片描述

2.2 配置项目属性

  • 在解决方案名 Test 处右键,点最下面属性,打开项目属性页。首先配置 Release 属性,平台选择 x64
    在这里插入图片描述
    • VC++目录 -> 包含目录,编辑增加以下路径
      在这里插入图片描述

    • VC++目录 -> 库目录,编辑增加以下路径
      在这里插入图片描述

    • C/C++ -> 常规 -> 附加包含目录,编辑增加以下路径。并关闭 SDL 检查
      在这里插入图片描述

    • 链接器 -> 附加库目录,编辑增加以下路径
      在这里插入图片描述

    • 链接器 -> 输入 -> 附加依赖项,编辑增加以下文件名(主要这里是Release版本所以用 opencv_world450.lib)
      在这里插入图片描述

    • 类似地配置 Debug 属性,区别仅在于最后一步链接附加依赖项写入 opencv_world450d.lib

  • 由于 1.3 节没有对源码进行 Cmake 安装,还需要将 onnxruntime.dll 复制粘贴到编译运行后 .exe 文件的生成目录下,如 C:…\YOLOv8-Test\Test\x64\Release,以免出现链接错误
    在这里插入图片描述
    如果不想在每次编译都粘贴,可以直接把它粘贴到 C:\Windows\System32 和 C:\Windows\SysWOW64

3. 修改源码

  • 由于 vs2015 无法使用 C++17 特征,修改 main.cpp 去掉其中对 filesystem 库的依赖,如下

    #include <iostream>
    #include <iomanip>
    #include "inference.h"
    #include <fstream>
    #include <random>
    #include <vector>
    #include <string>
    #include <dirent.h>
    #include <sys/stat.h>
    #include <opencv2/opencv.hpp>void Detector(YOLO_V8*& p) {std::string current_path = ".";std::string imgs_path = current_path + "/images";DIR* dir;struct dirent* ent;if ((dir = opendir(imgs_path.c_str())) != NULL) {while ((ent = readdir(dir)) != NULL) {std::string file_name = ent->d_name;if (file_name.find(".jpg") != std::string::npos || file_name.find(".png") != std::string::npos || file_name.find(".jpeg") != std::string::npos) {std::string img_path = imgs_path + "/" + file_name;cv::Mat img = cv::imread(img_path);std::vector<DL_RESULT> res;p->RunSession(img, res);for (auto& re : res) {cv::RNG rng(cv::getTickCount());cv::Scalar color(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));cv::rectangle(img, re.box, color, 3);float confidence = floor(100 * re.confidence) / 100;std::cout << std::fixed << std::setprecision(2);std::string label = p->classes[re.classId] + " " +std::to_string(confidence).substr(0, std::to_string(confidence).size() - 4);cv::rectangle(img,cv::Point(re.box.x, re.box.y - 25),cv::Point(re.box.x + label.length() * 15, re.box.y),color,cv::FILLED);cv::putText(img,label,cv::Point(re.box.x, re.box.y - 5),cv::FONT_HERSHEY_SIMPLEX,0.75,cv::Scalar(0, 0, 0),2);std::replace(label.begin(), label.end(), '\r', ' ');std::cout << "Target Type:   " << label << std::endl;std::cout << "Loc (x,y,w,h): " << "(" << re.box.x << "," << re.box.y << "," << re.box.width << "," << re.box.height << ")" << std::endl;}cv::Mat resized_img; double scale_factor = 5.0; // 放大倍数 cv::resize(img, resized_img, cv::Size(), scale_factor, scale_factor, cv::INTER_LINEAR); cv::imshow("Result of Detection", resized_img); std::cout << "Press any key to exit" << std::endl;//cv::imshow("Result of Detection", img);cv::waitKey(0);cv::destroyAllWindows();}}closedir(dir);}
    }void Classifier(YOLO_V8*& p) {std::string current_path = ".";std::string imgs_path = current_path;std::random_device rd;std::mt19937 gen(rd());std::uniform_int_distribution<int> dis(0, 255);DIR* dir;struct dirent* ent;if ((dir = opendir(imgs_path.c_str())) != NULL) {while ((ent = readdir(dir)) != NULL) {std::string file_name = ent->d_name;if (file_name.find(".jpg") != std::string::npos || file_name.find(".png") != std::string::npos) {std::string img_path = imgs_path + "/" + file_name;cv::Mat img = cv::imread(img_path);std::vector<DL_RESULT> res;char* ret = p->RunSession(img, res);float positionY = 50;for (int i = 0; i < res.size(); i++) {int r = dis(gen);int g = dis(gen);int b = dis(gen);cv::putText(img, std::to_string(i) + ":", cv::Point(10, positionY), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(b, g, r), 2);cv::putText(img, std::to_string(res.at(i).confidence), cv::Point(70, positionY), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(b, g, r), 2);positionY += 50;}cv::imshow("TEST_CLS", img);cv::waitKey(0);cv::destroyAllWindows();}}closedir(dir);}
    }int ReadPlaneYaml(YOLO_V8*& p) {// Open the YAML filestd::ifstream file("./cfg/plane.yaml");if (!file.is_open()) {std::cerr << "Failed to open file" << std::endl;return 1;}// Read the file line by linestd::string line;std::vector<std::string> lines;while (std::getline(file, line)) {lines.push_back(line);}// Find the start and end of the names sectionstd::size_t start = 0;std::size_t end = 0;for (std::size_t i = 0; i < lines.size(); i++) {if (lines[i].find("names:") != std::string::npos) {start = i + 1;} else if (start > 0 && lines[i].find(':') == std::string::npos) {end = i;break;}}// Extract the namesstd::vector<std::string> names;for (std::size_t i = start; i < end; i++) {std::stringstream ss(lines[i]);std::string name;std::getline(ss, name, ':'); // Extract the number before the delimiterstd::getline(ss, name); // Extract the string after the delimiternames.push_back(name);}p->classes = names;return 0;
    }void DetectTest() {YOLO_V8* yoloDetector = new YOLO_V8;ReadPlaneYaml(yoloDetector);DL_INIT_PARAM params;params.rectConfidenceThreshold = 0.1;params.iouThreshold = 0.5;params.modelPath = "./model/best.onnx";params.imgSize = { 640, 640 };
    #ifdef USE_CUDAparams.cudaEnable = true;// GPU FP32 inferenceparams.modelType = YOLO_DETECT_V8;// GPU FP16 inference//Note: change fp16 onnx model//params.modelType = YOLO_DETECT_V8_HALF;#else// CPU inferenceparams.modelType = YOLO_DETECT_V8;params.cudaEnable = false;#endifyoloDetector->CreateSession(params);Detector(yoloDetector);
    }// void ClsTest() {
    //     YOLO_V8* yoloDetector = new YOLO_V8;
    //     std::string model_path = "cls.onnx";
    //     ReadPlaneYaml(yoloDetector);
    //     DL_INIT_PARAM params{ model_path, YOLO_CLS, {224, 224} };
    //     yoloDetector->CreateSession(params);
    //     Classifier(yoloDetector);
    // }int main() {DetectTest();//ClsTest();
    }
  • 以上代码使用了 dirent.h 中封装的 windows 文件读写方法,如果 vs 报错找不到这个头文件,则自己创建该文件,放置于 vs 安装路径,我这是 D:\Programmer\Microsoft Visual Studio 14.0\VC\include

    /** Dirent interface for Microsoft Visual Studio** Copyright (C) 1998-2019 Toni Ronkko* This file is part of dirent.  Dirent may be freely distributed* under the MIT license.  For all details and documentation, see* https://github.com/tronkko/dirent*/
    #ifndef DIRENT_H
    #define DIRENT_H/* Hide warnings about unreferenced local functions */
    #if defined(__clang__)
    #   pragma clang diagnostic ignored "-Wunused-function"
    #elif defined(_MSC_VER)
    #   pragma warning(disable:4505)
    #elif defined(__GNUC__)
    #   pragma GCC diagnostic ignored "-Wunused-function"
    #endif/** Include windows.h without Windows Sockets 1.1 to prevent conflicts with* Windows Sockets 2.0.*/
    #ifndef WIN32_LEAN_AND_MEAN
    #   define WIN32_LEAN_AND_MEAN
    #endif
    #include <windows.h>#include <stdio.h>
    #include <stdarg.h>
    #include <wchar.h>
    #include <string.h>
    #include <stdlib.h>
    #include <malloc.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <errno.h>/* Indicates that d_type field is available in dirent structure */
    #define _DIRENT_HAVE_D_TYPE/* Indicates that d_namlen field is available in dirent structure */
    #define _DIRENT_HAVE_D_NAMLEN/* Entries missing from MSVC 6.0 */
    #if !defined(FILE_ATTRIBUTE_DEVICE)
    #   define FILE_ATTRIBUTE_DEVICE 0x40
    #endif/* File type and permission flags for stat(), general mask */
    #if !defined(S_IFMT)
    #   define S_IFMT _S_IFMT
    #endif/* Directory bit */
    #if !defined(S_IFDIR)
    #   define S_IFDIR _S_IFDIR
    #endif/* Character device bit */
    #if !defined(S_IFCHR)
    #   define S_IFCHR _S_IFCHR
    #endif/* Pipe bit */
    #if !defined(S_IFFIFO)
    #   define S_IFFIFO _S_IFFIFO
    #endif/* Regular file bit */
    #if !defined(S_IFREG)
    #   define S_IFREG _S_IFREG
    #endif/* Read permission */
    #if !defined(S_IREAD)
    #   define S_IREAD _S_IREAD
    #endif/* Write permission */
    #if !defined(S_IWRITE)
    #   define S_IWRITE _S_IWRITE
    #endif/* Execute permission */
    #if !defined(S_IEXEC)
    #   define S_IEXEC _S_IEXEC
    #endif/* Pipe */
    #if !defined(S_IFIFO)
    #   define S_IFIFO _S_IFIFO
    #endif/* Block device */
    #if !defined(S_IFBLK)
    #   define S_IFBLK 0
    #endif/* Link */
    #if !defined(S_IFLNK)
    #   define S_IFLNK 0
    #endif/* Socket */
    #if !defined(S_IFSOCK)
    #   define S_IFSOCK 0
    #endif/* Read user permission */
    #if !defined(S_IRUSR)
    #   define S_IRUSR S_IREAD
    #endif/* Write user permission */
    #if !defined(S_IWUSR)
    #   define S_IWUSR S_IWRITE
    #endif/* Execute user permission */
    #if !defined(S_IXUSR)
    #   define S_IXUSR 0
    #endif/* Read group permission */
    #if !defined(S_IRGRP)
    #   define S_IRGRP 0
    #endif/* Write group permission */
    #if !defined(S_IWGRP)
    #   define S_IWGRP 0
    #endif/* Execute group permission */
    #if !defined(S_IXGRP)
    #   define S_IXGRP 0
    #endif/* Read others permission */
    #if !defined(S_IROTH)
    #   define S_IROTH 0
    #endif/* Write others permission */
    #if !defined(S_IWOTH)
    #   define S_IWOTH 0
    #endif/* Execute others permission */
    #if !defined(S_IXOTH)
    #   define S_IXOTH 0
    #endif/* Maximum length of file name */
    #if !defined(PATH_MAX)
    #   define PATH_MAX MAX_PATH
    #endif
    #if !defined(FILENAME_MAX)
    #   define FILENAME_MAX MAX_PATH
    #endif
    #if !defined(NAME_MAX)
    #   define NAME_MAX FILENAME_MAX
    #endif/* File type flags for d_type */
    #define DT_UNKNOWN 0
    #define DT_REG S_IFREG
    #define DT_DIR S_IFDIR
    #define DT_FIFO S_IFIFO
    #define DT_SOCK S_IFSOCK
    #define DT_CHR S_IFCHR
    #define DT_BLK S_IFBLK
    #define DT_LNK S_IFLNK/* Macros for converting between st_mode and d_type */
    #define IFTODT(mode) ((mode) & S_IFMT)
    #define DTTOIF(type) (type)/** File type macros.  Note that block devices, sockets and links cannot be* distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are* only defined for compatibility.  These macros should always return false* on Windows.*/
    #if !defined(S_ISFIFO)
    #   define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
    #endif
    #if !defined(S_ISDIR)
    #   define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
    #endif
    #if !defined(S_ISREG)
    #   define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
    #endif
    #if !defined(S_ISLNK)
    #   define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
    #endif
    #if !defined(S_ISSOCK)
    #   define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
    #endif
    #if !defined(S_ISCHR)
    #   define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
    #endif
    #if !defined(S_ISBLK)
    #   define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
    #endif/* Return the exact length of the file name without zero terminator */
    #define _D_EXACT_NAMLEN(p) ((p)->d_namlen)/* Return the maximum size of a file name */
    #define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1)#ifdef __cplusplus
    extern "C" {
    #endif/* Wide-character version */
    struct _wdirent {/* Always zero */long d_ino;/* File position within stream */long d_off;/* Structure size */unsigned short d_reclen;/* Length of name without \0 */size_t d_namlen;/* File type */int d_type;/* File name */wchar_t d_name[PATH_MAX+1];
    };
    typedef struct _wdirent _wdirent;struct _WDIR {/* Current directory entry */struct _wdirent ent;/* Private file data */WIN32_FIND_DATAW data;/* True if data is valid */int cached;/* Win32 search handle */HANDLE handle;/* Initial directory name */wchar_t *patt;
    };
    typedef struct _WDIR _WDIR;/* Multi-byte character version */
    struct dirent {/* Always zero */long d_ino;/* File position within stream */long d_off;/* Structure size */unsigned short d_reclen;/* Length of name without \0 */size_t d_namlen;/* File type */int d_type;/* File name */char d_name[PATH_MAX+1];
    };
    typedef struct dirent dirent;struct DIR {struct dirent ent;struct _WDIR *wdirp;
    };
    typedef struct DIR DIR;/* Dirent functions */
    static DIR *opendir (const char *dirname);
    static _WDIR *_wopendir (const wchar_t *dirname);static struct dirent *readdir (DIR *dirp);
    static struct _wdirent *_wreaddir (_WDIR *dirp);static int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
    static int _wreaddir_r(_WDIR *dirp, struct _wdirent *entry, struct _wdirent **result);static int closedir (DIR *dirp);
    static int _wclosedir (_WDIR *dirp);static void rewinddir (DIR* dirp);
    static void _wrewinddir (_WDIR* dirp);static int scandir (const char *dirname, struct dirent ***namelist,int (*filter)(const struct dirent*),int (*compare)(const struct dirent**, const struct dirent**));static int alphasort (const struct dirent **a, const struct dirent **b);static int versionsort (const struct dirent **a, const struct dirent **b);/* For compatibility with Symbian */
    #define wdirent _wdirent
    #define WDIR _WDIR
    #define wopendir _wopendir
    #define wreaddir _wreaddir
    #define wclosedir _wclosedir
    #define wrewinddir _wrewinddir/* Internal utility functions */
    static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp);
    static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp);static int dirent_mbstowcs_s(size_t *pReturnValue,wchar_t *wcstr,size_t sizeInWords,const char *mbstr,size_t count);static int dirent_wcstombs_s(size_t *pReturnValue,char *mbstr,size_t sizeInBytes,const wchar_t *wcstr,size_t count);static void dirent_set_errno (int error);/** Open directory stream DIRNAME for read and return a pointer to the* internal working area that is used to retrieve individual directory* entries.*/
    static _WDIR*
    _wopendir(const wchar_t *dirname)
    {_WDIR *dirp;DWORD n;wchar_t *p;/* Must have directory name */if (dirname == NULL  ||  dirname[0] == '\0') {dirent_set_errno (ENOENT);return NULL;}/* Allocate new _WDIR structure */dirp = (_WDIR*) malloc (sizeof (struct _WDIR));if (!dirp) {return NULL;}/* Reset _WDIR structure */dirp->handle = INVALID_HANDLE_VALUE;dirp->patt = NULL;dirp->cached = 0;/** Compute the length of full path plus zero terminator** Note that on WinRT there's no way to convert relative paths* into absolute paths, so just assume it is an absolute path.*/
    #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)/* Desktop */n = GetFullPathNameW (dirname, 0, NULL, NULL);
    #else/* WinRT */n = wcslen (dirname);
    #endif/* Allocate room for absolute directory name and search pattern */dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16);if (dirp->patt == NULL) {goto exit_closedir;}/** Convert relative directory name to an absolute one.  This* allows rewinddir() to function correctly even when current* working directory is changed between opendir() and rewinddir().** Note that on WinRT there's no way to convert relative paths* into absolute paths, so just assume it is an absolute path.*/
    #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)/* Desktop */n = GetFullPathNameW (dirname, n, dirp->patt, NULL);if (n <= 0) {goto exit_closedir;}
    #else/* WinRT */wcsncpy_s (dirp->patt, n+1, dirname, n);
    #endif/* Append search pattern \* to the directory name */p = dirp->patt + n;switch (p[-1]) {case '\\':case '/':case ':':/* Directory ends in path separator, e.g. c:\temp\ *//*NOP*/;break;default:/* Directory name doesn't end in path separator */*p++ = '\\';}*p++ = '*';*p = '\0';/* Open directory stream and retrieve the first entry */if (!dirent_first (dirp)) {goto exit_closedir;}/* Success */return dirp;/* Failure */
    exit_closedir:_wclosedir (dirp);return NULL;
    }/** Read next directory entry.** Returns pointer to static directory entry which may be overwritten by* subsequent calls to _wreaddir().*/
    static struct _wdirent*
    _wreaddir(_WDIR *dirp)
    {struct _wdirent *entry;/** Read directory entry to buffer.  We can safely ignore the return value* as entry will be set to NULL in case of error.*/(void) _wreaddir_r (dirp, &dirp->ent, &entry);/* Return pointer to statically allocated directory entry */return entry;
    }/** Read next directory entry.** Returns zero on success.  If end of directory stream is reached, then sets* result to NULL and returns zero.*/
    static int
    _wreaddir_r(_WDIR *dirp,struct _wdirent *entry,struct _wdirent **result)
    {WIN32_FIND_DATAW *datap;/* Read next directory entry */datap = dirent_next (dirp);if (datap) {size_t n;DWORD attr;/** Copy file name as wide-character string.  If the file name is too* long to fit in to the destination buffer, then truncate file name* to PATH_MAX characters and zero-terminate the buffer.*/n = 0;while (n < PATH_MAX  &&  datap->cFileName[n] != 0) {entry->d_name[n] = datap->cFileName[n];n++;}entry->d_name[n] = 0;/* Length of file name excluding zero terminator */entry->d_namlen = n;/* File type */attr = datap->dwFileAttributes;if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {entry->d_type = DT_CHR;} else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {entry->d_type = DT_DIR;} else {entry->d_type = DT_REG;}/* Reset dummy fields */entry->d_ino = 0;entry->d_off = 0;entry->d_reclen = sizeof (struct _wdirent);/* Set result address */*result = entry;} else {/* Return NULL to indicate end of directory */*result = NULL;}return /*OK*/0;
    }/** Close directory stream opened by opendir() function.  This invalidates the* DIR structure as well as any directory entry read previously by* _wreaddir().*/
    static int
    _wclosedir(_WDIR *dirp)
    {int ok;if (dirp) {/* Release search handle */if (dirp->handle != INVALID_HANDLE_VALUE) {FindClose (dirp->handle);}/* Release search pattern */free (dirp->patt);/* Release directory structure */free (dirp);ok = /*success*/0;} else {/* Invalid directory stream */dirent_set_errno (EBADF);ok = /*failure*/-1;}return ok;
    }/** Rewind directory stream such that _wreaddir() returns the very first* file name again.*/
    static void
    _wrewinddir(_WDIR* dirp)
    {if (dirp) {/* Release existing search handle */if (dirp->handle != INVALID_HANDLE_VALUE) {FindClose (dirp->handle);}/* Open new search handle */dirent_first (dirp);}
    }/* Get first directory entry (internal) */
    static WIN32_FIND_DATAW*
    dirent_first(_WDIR *dirp)
    {WIN32_FIND_DATAW *datap;DWORD error;/* Open directory and retrieve the first entry */dirp->handle = FindFirstFileExW(dirp->patt, FindExInfoStandard, &dirp->data,FindExSearchNameMatch, NULL, 0);if (dirp->handle != INVALID_HANDLE_VALUE) {/* a directory entry is now waiting in memory */datap = &dirp->data;dirp->cached = 1;} else {/* Failed to open directory: no directory entry in memory */dirp->cached = 0;datap = NULL;/* Set error code */error = GetLastError ();switch (error) {case ERROR_ACCESS_DENIED:/* No read access to directory */dirent_set_errno (EACCES);break;case ERROR_DIRECTORY:/* Directory name is invalid */dirent_set_errno (ENOTDIR);break;case ERROR_PATH_NOT_FOUND:default:/* Cannot find the file */dirent_set_errno (ENOENT);}}return datap;
    }/** Get next directory entry (internal).** Returns*/
    static WIN32_FIND_DATAW*
    dirent_next(_WDIR *dirp)
    {WIN32_FIND_DATAW *p;/* Get next directory entry */if (dirp->cached != 0) {/* A valid directory entry already in memory */p = &dirp->data;dirp->cached = 0;} else if (dirp->handle != INVALID_HANDLE_VALUE) {/* Get the next directory entry from stream */if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) {/* Got a file */p = &dirp->data;} else {/* The very last entry has been processed or an error occurred */FindClose (dirp->handle);dirp->handle = INVALID_HANDLE_VALUE;p = NULL;}} else {/* End of directory stream reached */p = NULL;}return p;
    }/** Open directory stream using plain old C-string.*/
    static DIR*
    opendir(const char *dirname)
    {struct DIR *dirp;/* Must have directory name */if (dirname == NULL  ||  dirname[0] == '\0') {dirent_set_errno (ENOENT);return NULL;}/* Allocate memory for DIR structure */dirp = (DIR*) malloc (sizeof (struct DIR));if (!dirp) {return NULL;}{int error;wchar_t wname[PATH_MAX + 1];size_t n;/* Convert directory name to wide-character string */error = dirent_mbstowcs_s(&n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1);if (error) {/** Cannot convert file name to wide-character string.  This* occurs if the string contains invalid multi-byte sequences or* the output buffer is too small to contain the resulting* string.*/goto exit_free;}/* Open directory stream using wide-character name */dirp->wdirp = _wopendir (wname);if (!dirp->wdirp) {goto exit_free;}}/* Success */return dirp;/* Failure */
    exit_free:free (dirp);return NULL;
    }/** Read next directory entry.*/
    static struct dirent*
    readdir(DIR *dirp)
    {struct dirent *entry;/** Read directory entry to buffer.  We can safely ignore the return value* as entry will be set to NULL in case of error.*/(void) readdir_r (dirp, &dirp->ent, &entry);/* Return pointer to statically allocated directory entry */return entry;
    }/** Read next directory entry into called-allocated buffer.** Returns zero on success.  If the end of directory stream is reached, then* sets result to NULL and returns zero.*/
    static int
    readdir_r(DIR *dirp,struct dirent *entry,struct dirent **result)
    {WIN32_FIND_DATAW *datap;/* Read next directory entry */datap = dirent_next (dirp->wdirp);if (datap) {size_t n;int error;/* Attempt to convert file name to multi-byte string */error = dirent_wcstombs_s(&n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1);/** If the file name cannot be represented by a multi-byte string,* then attempt to use old 8+3 file name.  This allows traditional* Unix-code to access some file names despite of unicode* characters, although file names may seem unfamiliar to the user.** Be ware that the code below cannot come up with a short file* name unless the file system provides one.  At least* VirtualBox shared folders fail to do this.*/if (error  &&  datap->cAlternateFileName[0] != '\0') {error = dirent_wcstombs_s(&n, entry->d_name, PATH_MAX + 1,datap->cAlternateFileName, PATH_MAX + 1);}if (!error) {DWORD attr;/* Length of file name excluding zero terminator */entry->d_namlen = n - 1;/* File attributes */attr = datap->dwFileAttributes;if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {entry->d_type = DT_CHR;} else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {entry->d_type = DT_DIR;} else {entry->d_type = DT_REG;}/* Reset dummy fields */entry->d_ino = 0;entry->d_off = 0;entry->d_reclen = sizeof (struct dirent);} else {/** Cannot convert file name to multi-byte string so construct* an erroneous directory entry and return that.  Note that* we cannot return NULL as that would stop the processing* of directory entries completely.*/entry->d_name[0] = '?';entry->d_name[1] = '\0';entry->d_namlen = 1;entry->d_type = DT_UNKNOWN;entry->d_ino = 0;entry->d_off = -1;entry->d_reclen = 0;}/* Return pointer to directory entry */*result = entry;} else {/* No more directory entries */*result = NULL;}return /*OK*/0;
    }/** Close directory stream.*/
    static int
    closedir(DIR *dirp)
    {int ok;if (dirp) {/* Close wide-character directory stream */ok = _wclosedir (dirp->wdirp);dirp->wdirp = NULL;/* Release multi-byte character version */free (dirp);} else {/* Invalid directory stream */dirent_set_errno (EBADF);ok = /*failure*/-1;}return ok;
    }/** Rewind directory stream to beginning.*/
    static void
    rewinddir(DIR* dirp)
    {/* Rewind wide-character string directory stream */_wrewinddir (dirp->wdirp);
    }/** Scan directory for entries.*/
    static int
    scandir(const char *dirname,struct dirent ***namelist,int (*filter)(const struct dirent*),int (*compare)(const struct dirent**, const struct dirent**))
    {struct dirent **files = NULL;size_t size = 0;size_t allocated = 0;const size_t init_size = 1;DIR *dir = NULL;struct dirent *entry;struct dirent *tmp = NULL;size_t i;int result = 0;/* Open directory stream */dir = opendir (dirname);if (dir) {/* Read directory entries to memory */while (1) {/* Enlarge pointer table to make room for another pointer */if (size >= allocated) {void *p;size_t num_entries;/* Compute number of entries in the enlarged pointer table */if (size < init_size) {/* Allocate initial pointer table */num_entries = init_size;} else {/* Double the size */num_entries = size * 2;}/* Allocate first pointer table or enlarge existing table */p = realloc (files, sizeof (void*) * num_entries);if (p != NULL) {/* Got the memory */files = (dirent**) p;allocated = num_entries;} else {/* Out of memory */result = -1;break;}}/* Allocate room for temporary directory entry */if (tmp == NULL) {tmp = (struct dirent*) malloc (sizeof (struct dirent));if (tmp == NULL) {/* Cannot allocate temporary directory entry */result = -1;break;}}/* Read directory entry to temporary area */if (readdir_r (dir, tmp, &entry) == /*OK*/0) {/* Did we get an entry? */if (entry != NULL) {int pass;/* Determine whether to include the entry in result */if (filter) {/* Let the filter function decide */pass = filter (tmp);} else {/* No filter function, include everything */pass = 1;}if (pass) {/* Store the temporary entry to pointer table */files[size++] = tmp;tmp = NULL;/* Keep up with the number of files */result++;}} else {/** End of directory stream reached => sort entries and* exit.*/qsort (files, size, sizeof (void*),(int (*) (const void*, const void*)) compare);break;}} else {/* Error reading directory entry */result = /*Error*/ -1;break;}}} else {/* Cannot open directory */result = /*Error*/ -1;}/* Release temporary directory entry */free (tmp);/* Release allocated memory on error */if (result < 0) {for (i = 0; i < size; i++) {free (files[i]);}free (files);files = NULL;}/* Close directory stream */if (dir) {closedir (dir);}/* Pass pointer table to caller */if (namelist) {*namelist = files;}return result;
    }/* Alphabetical sorting */
    static int
    alphasort(const struct dirent **a, const struct dirent **b)
    {return strcoll ((*a)->d_name, (*b)->d_name);
    }/* Sort versions */
    static int
    versionsort(const struct dirent **a, const struct dirent **b)
    {/* FIXME: implement strverscmp and use that */return alphasort (a, b);
    }/* Convert multi-byte string to wide character string */
    static int
    dirent_mbstowcs_s(size_t *pReturnValue,wchar_t *wcstr,size_t sizeInWords,const char *mbstr,size_t count)
    {int error;#if defined(_MSC_VER)  &&  _MSC_VER >= 1400/* Microsoft Visual Studio 2005 or later */error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count);#else/* Older Visual Studio or non-Microsoft compiler */size_t n;/* Convert to wide-character string (or count characters) */n = mbstowcs (wcstr, mbstr, sizeInWords);if (!wcstr  ||  n < count) {/* Zero-terminate output buffer */if (wcstr  &&  sizeInWords) {if (n >= sizeInWords) {n = sizeInWords - 1;}wcstr[n] = 0;}/* Length of resulting multi-byte string WITH zero terminator */if (pReturnValue) {*pReturnValue = n + 1;}/* Success */error = 0;} else {/* Could not convert string */error = 1;}#endifreturn error;
    }/* Convert wide-character string to multi-byte string */
    static int
    dirent_wcstombs_s(size_t *pReturnValue,char *mbstr,size_t sizeInBytes, /* max size of mbstr */const wchar_t *wcstr,size_t count)
    {int error;#if defined(_MSC_VER)  &&  _MSC_VER >= 1400/* Microsoft Visual Studio 2005 or later */error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count);#else/* Older Visual Studio or non-Microsoft compiler */size_t n;/* Convert to multi-byte string (or count the number of bytes needed) */n = wcstombs (mbstr, wcstr, sizeInBytes);if (!mbstr  ||  n < count) {/* Zero-terminate output buffer */if (mbstr  &&  sizeInBytes) {if (n >= sizeInBytes) {n = sizeInBytes - 1;}mbstr[n] = '\0';}/* Length of resulting multi-bytes string WITH zero-terminator */if (pReturnValue) {*pReturnValue = n + 1;}/* Success */error = 0;} else {/* Cannot convert string */error = 1;}#endifreturn error;
    }/* Set errno variable */
    static void
    dirent_set_errno(int error)
    {
    #if defined(_MSC_VER)  &&  _MSC_VER >= 1400/* Microsoft Visual Studio 2005 and later */_set_errno (error);#else/* Non-Microsoft compiler or older Microsoft compiler */errno = error;#endif
    }#ifdef __cplusplus
    }
    #endif
    #endif /*DIRENT_H*/
  • 注意以上写死了模型路径为 ./model/best.onnx,数据配置文件路径为 ./cfg/plane.yaml,测试图像放在 ./images 路径下。将这些资源放到编译后生成的 C:\Users…\YOLOv8-Test\Test\x64\Release(或Debug) 目录下
    在这里插入图片描述
    现在双击 Test.exe 就能看到 YOLOv8 的识别结果了

  • 最后的问题是,如果在 vscode 里点击运行,还是会报错找不到资源,这是因为工作目录还在根目录,修改属性页->调试 -> 工作目录为 $(OutDir) 即可
    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uniApp通过xgplayer(西瓜播放器)接入视频实时监控

&#x1f680; 个人简介&#xff1a;某大型国企资深软件开发工程师&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主&#xff0c;华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码喽的自我修养&#x1f9…

fast-crud select下拉框 实现多选功能及下拉框数据动态获取(通过接口获取)

教程 fast-crud select示例配置需求:需求比较复杂 1. 下拉框选项需要通过后端接口获取 2. 实现多选功能 由于这个前端框架使用逻辑比较复杂我也是第一次使用,所以只记录核心问题 环境:vue3,typescript,fast-crud ,elementPlus 效果 代码 // crud.tsx文件(/.ts也行 js应…

【Go】:图片上添加水印的全面指南——从基础到高级特性

前言 在数字内容日益重要的今天&#xff0c;保护版权和标识来源变得关键。为图片添加水印有助于声明所有权、提升品牌认知度&#xff0c;并防止未经授权的使用。本文将介绍如何用Go语言实现图片水印&#xff0c;包括静态图片和带旋转、倾斜效果的文字水印&#xff0c;帮助您有…

Win10微调大语言模型ChatGLM2-6B

在《Win10本地部署大语言模型ChatGLM2-6B-CSDN博客》基础上进行&#xff0c;官方文档在这里&#xff0c;参考了这篇文章 首先确保ChatGLM2-6B下的有ptuning AdvertiseGen下载地址1&#xff0c;地址2&#xff0c;文件中数据留几行 模型文件下载地址 &#xff08;注意&#xff1…

多模态论文笔记——CLIP

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细介绍这几年AIGC火爆的隐藏功臣&#xff0c;多模态模型&#xff1a;CLIP。 文章目录 CLIP&#xff08;Contrastive Language-Image Pre-training&#xff09…

【ArcGIS微课1000例】0137:色彩映射表转为RGB全彩模式

本文讲述ArcGIS中,将tif格式的影像数据从色彩映射表转为RGB全彩模式。 参考阅读:【GlobalMapper精品教程】093:将tif影像色彩映射表(调色板)转为RGB全彩模式 文章目录 一、色彩映射表预览二、色彩映射表转为RGB全彩模式一、色彩映射表预览 加载配套数据包中的0137.rar中的…

计算机网络(四)——网络层

目录 一、功能 二、IP数据报分片 三、DHCP动态主机配置协议 四、网络地址转换&#xff08;NAT&#xff09;技术 五、无分类编址CIDR 六、ARP地址解析协议 七、ICMP网际控制报文协议 八、IPv4和IPv6的区别 九、IPv4向IPv6的两种过渡技术——双栈协议和隧道技术 十、路由…

【大数据】机器学习 -----关于data.csv数据集分析案例

打开表 import pandas as pd df2 pd.read_csv("data.csv",encoding"gbk") df2.head()查看数据属性&#xff08;列标题&#xff0c;表形状&#xff0c;类型&#xff0c;行标题&#xff0c;值&#xff09; print("列标题:",df2.columns)Data…

在 Linux 下Ubuntu创建同权限用户

我是因为不小心把最开始创建的用户的文件夹颜色搞没了&#xff0c;再后来全白用习惯了&#xff0c;就不想卸载了&#xff0c;像创建一个和最开始创建的用户有一样的权限可以执行sudo -i进入root一样的用户 如图这是最原始的样子 第一步 创建新用户&#xff0c;我这里是因为之前…

toRef 和 toRefs 详解及应用

在 Vue 3 中&#xff0c;toRef 和 toRefs 是两个用于创建响应式引用的工具&#xff0c;主要用于组合式 API&#xff08;Composition API&#xff09;的场景中 1. toRef 定义 toRef 将某个对象的某个属性包装成一个响应式引用。这样可以直接对该引用进行操作&#xff0c;而不需…

【大模型入门指南 07】量化技术浅析

【大模型入门指南】系列文章&#xff1a; 【大模型入门指南 01】深度学习入门【大模型入门指南 02】LLM大模型基础知识【大模型入门指南 03】提示词工程【大模型入门指南 04】Transformer结构【大模型入门指南 05】LLM技术选型【大模型入门指南 06】LLM数据预处理【大模型入门…

Nginx配置VTS模块-对接Promethues监控

一、背景 Nginx有一个stub_status模块&#xff0c;可以获取到Nginx的一些相关指标。stub_status 模块用于提供基本的 Nginx 性能统计数据。这个模块不是默认编译进 Nginx 的&#xff0c;所以如果需要使用它&#xff0c;确保 Nginx 是带有 --with-http_stub_status_module 选项编…

python 寻找数据拐点

import numpy as np import cv2 from scipy.signal import find_peaks# 示例数据 y_data [365.63258786, 318.34824281, 258.28434505, 228.8913738, 190.87220447, 158.28434505, 129.53035144, 111.95846645, 111.95846645, 120.26517572, 140.71246006, 161.79872204, 180.…

【Leetcode 热题 100】84. 柱状图中最大的矩形

问题背景 给定 n n n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 1 1。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 示例 输入&#xff1a; h e i g h t s [ 2 , 1 , 5 , 6 , 2 , 3 ] heights [2,1…

网络原理(三)—— 传输层 之 UDP 和 TCP协议

传输层 在传输层两大关键的协议就是UDP和TCP协议了&#xff0c;除此之外&#xff0c;还有别的传输层协议&#xff0c;本文章将介绍UDP和TCP协议&#xff0c;重点介绍TCP协议。 首先回顾TCP和UDP 的特点&#xff1a; UDP&#xff1a;不可靠传输&#xff0c;面向数据包&#xf…

MySQL素材怎么导入Navicat???

不管用什么方法都要先关掉MySQL服务&#xff0c;并且提前备份数据&#xff01; 1.有sql文件时候。 打开navicat&#xff0c;运行sql文件 然后点击后面三个点&#xff0c;选中要运行的sql文件&#xff0c;开始。 鼠标右键刷新一下&#xff0c;就能看到sql文件中的表了 2.没有s…

Windows安装ES单机版设置密码

下载ES ES下载链接 我用的是7.17.26 启动前配置 解压之后打开D:\software\elasticsearch-7.17.26\bin\elasticsearch-env.bat 在elasticsearch-env.bat文件中修改jdk的路径 修改前 修改内容 if defined ES_JAVA_HOME (set JAVA"D:\software\elasticsearch-7.17.26\…

mac intel芯片下载安卓模拟器

一、调研 目前主流两个模拟器&#xff1a; 雷神模拟器 不支持macosmumu模拟器pro版 不支持macos intel芯片 搜索到mumu的Q&A中有 “Intel芯片Mac如何安装MuMu&#xff1f;” q&a&#x1f517;&#xff1a;https://mumu.163.com/mac/faq/install-on-intel-mac.html 提…

系统看门狗配置--以ubuntu为例

linux系统配置看门狗 以 ubuntu 系统配置看门狗为例 配置看门狗使用的脚本文件&#xff0c;需要使用管理员权限来执行&#xff1a; 配置是&#xff1a;系统每 30S 喂一次狗&#xff0c;超过 60S 不进行投喂&#xff0c;就会自动重启。 1. 系统脚本内容&#xff1a; #!/bin/b…

每天五分钟深度学习框架pytorch:快速搭建VGG网络的基础模块VGG块

本文重点 前面我们介绍了VGG神经网络,我们知道VGG是由许多的VGG块构成,那么本文我们将使用pytorch搭建VGG块 代码实现: import torch from torch import nn def vgg_block(num_convs,in_channels,out_channels): net=[nn.Conv2d(in_channels,out_channels,kernel_size=3,p…