windows C++ NCNN部署yolov8视觉模型流程记录
·
-
通过安装ncnn库和pnnx库
pip install -U pnnx ncnn -i https://pypi.tuna.tsinghua.edu.cn/simple
pnnx 20250530
ncnn 1.0.20250503 -
将pt模型转换为best.torchscript
yolo export model=best.pt format=torchscript -
转换best.torchscript为静态图模型
pnnx best.torchscript
最终会生成两个ncnn文件:best.ncnn.param、best.ncnn.bin。
-
设置vs的配置



-
main.cpp
#include <cstdio>
#include "yolov8.h"
#include <opencv2/opencv.hpp>
int main(int argc, char** argv)
{
// 手动定义参数文件、权重文件和测试图像路径
std::string param_file = "C:\\Users\\Administrator\\Desktop\\ncnn_test\\best.ncnn.param"; // 替换为实际的参数文件路径
std::string bin_file = "C:\\Users\\Administrator\\Desktop\\ncnn_test\\best.ncnn.bin"; // 替换为实际的权重文件路径
std::string test_image = "C:\\Users\\Administrator\\Desktop\\ncnn_test\\1.jpg"; // 替换为实际的测试图像路径
// 创建 Yolo8 检测器对象
//auto detector = cvx::Yolo8(param_file.c_str(), bin_file.c_str());
cvx::Yolo8 detector(param_file.c_str(), bin_file.c_str());
// 读取图像并转换为 RGB 格式
cv::Mat image = cv::imread(test_image);
if (image.empty()) {
std::cerr << "Failed to load image: " << test_image << std::endl;
return -1;
}
cv::cvtColor(image, image, cv::COLOR_BGR2RGB);
// 存储检测结果
std::vector<cvx::Instance> insts;
// 进行推理
detector.inference(image, insts);
std::cout << "Inference completed. Detected " << insts.size() << " objects." << std::endl;
return 0;
}
- yolov8.h
#ifndef __YOLO_H__
#define __YOLO_H__
#include <string>
#include <memory>
#include <opencv2/opencv.hpp>
#include "layer.h"
#include "net.h"
namespace cvx
{
struct KeyPoint
{
int x = 0;
int y = 0;
float score = 0.f;
bool visible = false;
KeyPoint(int x, int y, float score, bool visible) : \
x(x), y(y), score(score), visible(visible) {}
};
struct Instance
{
cv::Mat mask{}; //
std::vector<KeyPoint> keypoints{};
cv::Rect box{ 0, 0, 0, 0 };
int label{ -1 };
float prob{ 0.f };
Instance(cv::Rect box, int label, float prob,
cv::Mat mask = cv::Mat(), std::vector<KeyPoint> keypoints = {}) : \
box(box), label(label), prob(prob), mask(mask), keypoints(keypoints) {}
};
class Yolo8
{
public:
Yolo8() = default;
Yolo8(const char* param_file, const char* bin_file);
~Yolo8();
static float clamp(float val, float min = 0.f, float max = 1280.f)
{
return val > min ? (val < max ? val : max) : min;
}
int inference(const cv::Mat& image, std::vector<Instance>& instances) const;
static void visualize(const cv::Mat& image,
const std::vector<Instance>& instances,
int top_pad = 0,
int left_pad = 0,
float scale = 1.f);
private:
void decodeInstances(ncnn::Mat& data, std::vector<Instance>& instances) const;
std::unique_ptr<ncnn::Net> net_{ nullptr };
std::vector<std::string> classes_;
float score_threshold_{ 0.8 };
float iou_threshold_{ 0.2 };
unsigned short kpt_shape_[2];
};
}
#endif // __YOLO_H__
- yolov8.cpp
#include <cassert>
#include <cfloat>
#include <benchmark.h>
#include "yolov8.h"
namespace cvx
{
Yolo8::Yolo8(const char* param_file, const char* bin_file)
{
net_ = std::make_unique<ncnn::Net>();
// net_->opt.use_vulkan_compute = true;
assert(net_->load_param(param_file) == 0);
assert(net_->load_model(bin_file) == 0);
classes_ = { "cell" };
score_threshold_ = 0.25; // 降低阈值以检测更多细胞
iou_threshold_ = 0.8; // NMS阈值
}
Yolo8::~Yolo8()
{
}
void Yolo8::visualize(const cv::Mat& image,
const std::vector<Instance>& instances,
int top_pad,
int left_pad,
float scale)
{
size_t w = image.cols;
size_t h = image.rows;
cv::Mat visual;
cv::resize(image, visual, cv::Size(w * scale, h * scale));
cv::copyMakeBorder(visual, visual, top_pad, top_pad, left_pad, left_pad, cv::BORDER_CONSTANT, cv::Scalar(0, 125, 0));
std::cout << "visual.size: " << visual.size() << std::endl;
for (auto& inst : instances) {
cv::rectangle(visual, inst.box, cv::Scalar(0, 255, 0), 2, 8, 0);
}
cv::cvtColor(visual, visual, cv::COLOR_RGB2BGR);
cv::imwrite("visual.png", visual);
}
void Yolo8::decodeInstances(ncnn::Mat& data, std::vector<Instance>& instances) const
{
std::vector<int> class_ids;
std::vector<float> confidences;
std::vector<cv::Rect> boxes;
float* data_ptr = static_cast<float*>(data.data);
float resizeScales = 1.0;
int class_id;
for (size_t i = 0; i < data.w; i++) {
float* data_col = data_ptr + i;
double maxClassScore = DBL_MIN;
maxClassScore = *(data_col + 4 * data.w);
class_id = 0;
if (maxClassScore > score_threshold_)
{
confidences.push_back(maxClassScore);
class_ids.push_back(class_id);
float x = *(data_col);
float y = *(data_col + data.w);
float w = *(data_col + 2 * data.w);
float h = *(data_col + 3 * data.w);
int left = int((x - 0.5 * w) * resizeScales);
int top = int((y - 0.5 * h) * resizeScales);
int width = int(w * resizeScales);
int height = int(h * resizeScales);
boxes.push_back(cv::Rect(left, top, width, height));
}
}
std::vector<int> nmsResult;
cv::dnn::NMSBoxes(boxes, confidences, score_threshold_, iou_threshold_, nmsResult);
for (int i = 0; i < nmsResult.size(); ++i)
{
int idx = nmsResult[i];
instances.emplace_back(boxes[idx],
class_ids[idx],
confidences[idx],
cv::Mat());
}
}
int Yolo8::inference(const cv::Mat& image, std::vector<Instance>& instances) const
{
int target_size = 640;
int img_w = image.cols;
int img_h = image.rows;
// letterbox pad to multiple of MAX_STRIDE
int w = img_w;
int h = img_h;
float scale = 1.f;
if (w > h)
{
scale = (float)target_size / w;
w = target_size;
h = h * scale;
}
else
{
scale = (float)target_size / h;
h = target_size;
w = w * scale;
}
ncnn::Mat in = ncnn::Mat::from_pixels_resize(image.data, ncnn::Mat::PIXEL_BGR2RGB, img_w, img_h, w, h);
int wpad = target_size - w;
int hpad = target_size - h;
int top = hpad / 2;
int bottom = hpad - top;
int left = wpad / 2;
int right = wpad - left;
ncnn::Mat in_pad;
ncnn::copy_make_border(in,
in_pad,
top,
bottom,
left,
right,
ncnn::BORDER_CONSTANT,
114.f);
const float norm_vals[3] = { 1 / 255.f, 1 / 255.f, 1 / 255.f };
in_pad.substract_mean_normalize(0, norm_vals);
auto t0 = ncnn::get_current_time();
ncnn::Extractor ex = net_->create_extractor();
ex.input("in0", in_pad);
ncnn::Mat out;
ex.extract("out0", out);
this->decodeInstances(out, instances);
auto t1 = ncnn::get_current_time();
this->visualize(image, instances, top, left, scale);
return 0;
}
}
最后运行main.cpp即可。
更多推荐
所有评论(0)