1. 通过安装ncnn库和pnnx库
    pip install -U pnnx ncnn -i https://pypi.tuna.tsinghua.edu.cn/simple
    pnnx 20250530
    ncnn 1.0.20250503

  2. 将pt模型转换为best.torchscript
    yolo export model=best.pt format=torchscript

  3. 转换best.torchscript为静态图模型
    pnnx best.torchscript

最终会生成两个ncnn文件:best.ncnn.param、best.ncnn.bin。

  1. 设置vs的配置
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  2. 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;
}
  1. 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__
  1. 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即可。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐