yolov8数据标注、模型训练到模型部署全过程

news2024/11/25 18:28:53

文章目录

  • 一、数据标注(x-anylabeling)
    • 1. 安装方式
      • 1.1 直接通过Releases安装
      • 1.2 clone源码后采用终端运行
    • 2. 如何使用
  • 二、模型训练
  • 三、模型部署
    • 3.1 onnx转engine
    • 3.2 c++调用engine模型
      • 3.2.1 main_tensorRT.cpp
      • 3.2.2 segmentationModel.cpp

一、数据标注(x-anylabeling)

1. 安装方式

1.1 直接通过Releases安装

https://github.com/CVHub520/X-AnyLabeling/releases
根据自己的系统选择CPU还是GPU推理以及Linux或者win系统
在这里插入图片描述

1.2 clone源码后采用终端运行

https://github.com/CVHub520/X-AnyLabeling
在这里插入图片描述
在项目中打开终端安装所需的环境依赖:

pip install -r requirements.txt

安装完成后运行app.py

python anylabeling/app.py

注:当直接使用exe运行失败的话,最好就是采用第二种方式,可以通过终端知道报错的原因。

2. 如何使用

注:由于x-anylabeling是可以使用自己训练后的模型,然后自动生成标注数据的,但是第一次的话就需要自己标注数据。

  1. 首次打开可以进行语言的选择在这里插入图片描述

  2. 打开需要标注数据的文件夹
    在这里插入图片描述

  3. 点击矩形框或者使用快捷键(R)
    在这里插入图片描述

  4. 直接进行标记并自己定义类
    在这里插入图片描述

  5. 打开左上角“文件”选项,点击自动保存
    保存的文件类型是json,你可以自己选择导出的类型。
    yolov8训练的标签格式是txt,通常我标记的时候都是选择导出voc(xml格式)。
    注:你也可以直接导出yolo标签格式
    在这里插入图片描述

  6. 将标记好的数据进行训练的格式进行划分

    import os
    import random
    import shutil
    
    # 输入文件夹路径和划分比例
    folder_path = input("请输入文件夹路径:")
    train_ratio = float(input("请输入训练集比例:"))
    
    # 检查文件夹是否存在
    if not os.path.exists(folder_path):
        print("文件夹不存在!")
        exit()
    
    # 获取所有jpg和txt文件
    jpg_files = [file for file in os.listdir(folder_path) if file.endswith(".jpg")]
    txt_files = [file for file in os.listdir(folder_path) if file.endswith(".txt")]
    
    # 检查文件数量是否相等
    if len(jpg_files) != len(txt_files):
        print("图片和标签数量不匹配!")
        exit()
    
    # 打乱文件顺序
    random.shuffle(jpg_files)
    
    # 划分训练集和验证集
    train_size = int(len(jpg_files) * train_ratio)
    train_jpg = jpg_files[:train_size]
    train_txt = [file.replace(".jpg", ".txt") for file in train_jpg]
    val_jpg = jpg_files[train_size:]
    val_txt = [file.replace(".jpg", ".txt") for file in val_jpg]
    
    # 创建文件夹和子文件夹
    if not os.path.exists("images/train"):
        os.makedirs("images/train")
    if not os.path.exists("images/val"):
        os.makedirs("images/val")
    if not os.path.exists("labels/train"):
        os.makedirs("labels/train")
    if not os.path.exists("labels/val"):
        os.makedirs("labels/val")
    
    # 复制文件到目标文件夹
    for file in train_jpg:
        shutil.copy(os.path.join(folder_path, file), "images/train")
    for file in train_txt:
        shutil.copy(os.path.join(folder_path, file), "labels/train")
    for file in val_jpg:
        shutil.copy(os.path.join(folder_path, file), "images/val")
    for file in val_txt:
        shutil.copy(os.path.join(folder_path, file), "labels/val")
    
    
    print("处理完成!")
    
    
    

    生成images和labels的文件夹
    在这里插入图片描述
    我这里没有加入测试集,只使用了训练集和验证集
    在这里插入图片描述

  7. 训练好自己的模型后
    将生成的.onnx和.yaml放在一个路径下
    在这里插入图片描述
    yaml文件的配置,注意这个类不要用数字,会被认定为int型,然后导致无法生成框,也就是报错。这个类的名称和个数一定要与训练的时候进行配置的一样
    在这里插入图片描述
    就是这里面的class names,这里填的什么,那么上面配置的yaml文件也要一样。
    在这里插入图片描述

二、模型训练

yolov8的源代码:
https://github.com/ultralytics/ultralytics

  1. 首先安装yolov8运行所依赖的库

    pip install ultralytics
    
  2. 根据代码进行
    首先下载一个预训练模型:https://docs.ultralytics.com/tasks/detect/

在这里插入图片描述

from ultralytics import YOLO

# Load a models
model = YOLO("D:\MyProject\yolov8s.pt")  # load a pretrained model (recommended for training)

# Use the model
model.train(data="D:\MyProject\data\myData.yaml", epochs=3)  # train the model
metrics = model.val()  # evaluate model performance on the validation set
results = model("https://ultralytics.com/images/bus.jpg")  # predict on an image
path = model.export(format="onnx")  # export the model to ONNX format

将前面标记好的数据放在路径下,配置好myData.yaml,如下:
在这里插入图片描述

三、模型部署

3.1 onnx转engine

首先把前面训练好的模型pt通过model.export(format="onnx")转换成onnx。
工程文件如下:
在这里插入图片描述
环境配置(一):需要配置anaconda、opencv、cuda以及tensorRT。
tensorRT的安装与使用:链接
在这里插入图片描述

环境配置(二):
在这里插入图片描述
环境配置(三):
在这里插入图片描述

opencv_world455.lib
myelin64_1.lib
nvinfer.lib
nvonnxparser.lib
nvparsers.lib
nvinfer_plugin.lib
cuda.lib
cudadevrt.lib
cudart_static.lib

由于x64中Release里面的依赖项太大,所以进行了分卷上传
在这里插入图片描述
yolov8使用tensorRT部署的环境依赖项(一)
yolov8使用tensorRT部署的环境依赖项(二)
在这里插入图片描述
将上面两个压缩包下载后放到一个文件夹里面,直接解压001,就可以将两个压缩包里面的依赖项全部解压出来。把解压的dll所有文件复制到/x64/Release这个路径下。

代码执行:

#include <iostream>
#include "logging.h"
#include "NvOnnxParser.h"
#include "NvInfer.h"
#include <fstream>

using namespace nvinfer1;
using namespace nvonnxparser;

static Logger gLogger;



int main(int argc, char** argv) {
	
	const char* onnx_filename = "D://TR_YOLOV8_DLL//zy_onnx2engine//models//best.onnx";
	const char* engine_filename = "D://TR_YOLOV8_DLL//zy_onnx2engine//models//best.engine";
	
	// 1 onnx解析器
	IBuilder* builder = createInferBuilder(gLogger);
	const auto explicitBatch = 1U << static_cast<uint32_t>(NetworkDefinitionCreationFlag::kEXPLICIT_BATCH);
	INetworkDefinition* network = builder->createNetworkV2(explicitBatch);

	nvonnxparser::IParser* parser = nvonnxparser::createParser(*network, gLogger);

	
	parser->parseFromFile(onnx_filename, static_cast<int>(Logger::Severity::kWARNING));
	for (int i = 0; i < parser->getNbErrors(); ++i)
	{
		std::cout << parser->getError(i)->desc() << std::endl;
	}
	std::cout << "successfully load the onnx model" << std::endl;

	// 2build the engine
	unsigned int maxBatchSize = 1;
	builder->setMaxBatchSize(maxBatchSize);
	IBuilderConfig* config = builder->createBuilderConfig();
	config->setMaxWorkspaceSize(1 << 20);
	//config->setMaxWorkspaceSize(128 * (1 << 20));  // 16MB
	config->setFlag(BuilderFlag::kFP16);
	ICudaEngine* engine = builder->buildEngineWithConfig(*network, *config);

	// 3serialize Model
	IHostMemory* gieModelStream = engine->serialize();
	std::ofstream p(engine_filename, std::ios::binary);
	if (!p)
	{
		std::cerr << "could not open plan output file" << std::endl;
		return -1;
	}
	p.write(reinterpret_cast<const char*>(gieModelStream->data()), gieModelStream->size());
	gieModelStream->destroy();


	std::cout << "successfully generate the trt engine model" << std::endl;
	return 0;
}

完整项目:https://download.csdn.net/download/qq_44747572/88791740

3.2 c++调用engine模型

在这里插入图片描述
这里的两个工程环境部署都跟上面部署的方式一样。进行重复的动作即可
在这里插入图片描述

3.2.1 main_tensorRT.cpp

// Xray_test.cpp : 定义控制台应用程序的入口点。
#define _AFXDLL
#include <iomanip>
#include <string>    
#include <fstream>  
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <io.h>
#include "segmentationModel.h"


// stuff we know about the network and the input/output blobs
#define input_h 640
#define input_w  640
#define	channel  3
#define classe  2       // 80个类
#define segWidth  160
#define segHeight  160
#define segChannels  32
#define Num_box  34000   //8400    1280 33600

MODELDLL predictClasse;
#pragma comment(lib, "..//x64//Release//segmentationModel.lib")

using namespace cv;
using namespace std;

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

	//检测测试
	string engine_filename = "D://TR_YOLOV8_DLL//zy_Xray_inspection//models//best.engine";
	string img_filename = "D://TR_YOLOV8_DLL//zy_Xray_inspection//imgs//";
	predictClasse.LoadYoloV8DetectEngine(engine_filename);
	string pattern_jpg = img_filename + "*.jpg";  // test_images
	vector<cv::String> image_files;
	glob(pattern_jpg, image_files);
	vector<ObjectTR> output;
	float confTh = 0.25;
	for (int i = 0; i < image_files.size(); i++)
	{
		Mat src = imread(image_files[i], 1);
		Mat dst;
		clock_t start = clock();
		predictClasse.YoloV8DetectPredict(src, dst, channel, classe, input_h, input_w, Num_box, confTh, output);
		clock_t end = clock();
		std::cout << "总时间:" << end - start << "ms" << std::endl;
		cv::namedWindow("output.jpg", 0);
		cv::imshow("output.jpg", dst);
		cv::waitKey(0);

	}


	分割测试
	//string engine_filename = "D://Users//6536//Desktop//TR_YOLOV8_DLL//zy_Xray_inspection//models//yolov8n-seg.engine";
	//string img_filename = "D://Users//6536//Desktop//TR_YOLOV8_DLL//zy_Xray_inspectionimgs//bus.jpg";
	//predictClasse.LoadYoloV8SegEngine(engine_filename);
	//Mat src = imread(img_filename, 1);
	//Mat dst;
	//predictClasse.YoloV8SegPredict(src, dst, channel, classe, input_h, input_w, segChannels, segWidth, segHeight, Num_box);
	//cv::imshow("output.jpg", dst);
	//cv::waitKey(0);
	return 0;
}


3.2.2 segmentationModel.cpp

#include "pch.h"
#include "segmentationModel.h"


#define DEVICE 0  // GPU id

static const float CONF_THRESHOLD = 0.25;
static const float NMS_THRESHOLD = 0.5;
static const float MASK_THRESHOLD = 0.5;
const char* INPUT_BLOB_NAME = "images";
const char* OUTPUT_BLOB_NAME = "output0";//detect
const char* OUTPUT_BLOB_NAME1 = "output1";//mask

static Logger gLogger;

IRuntime* runtimeYolov8Seg;
ICudaEngine* engineYolov8Seg;
IExecutionContext* contextYolov8Seg;




MODELDLL::MODELDLL()
{

}
MODELDLL::~MODELDLL()
{
   
}
//yolov8检测推理
bool MODELDLL::LoadYoloV8DetectEngine(const std::string& engineName)
{
    // create a model using the API directly and serialize it to a stream
    char* trtModelStream{ nullptr }; //char* trtModelStream==nullptr;  开辟空指针后 要和new配合使用,比如 trtModelStream = new char[size]
    size_t size{ 0 };//与int固定四个字节不同有所不同,size_t的取值range是目标平台下最大可能的数组尺寸,一些平台下size_t的范围小于int的正数范围,又或者大于unsigned int. 使用Int既有可能浪费,又有可能范围不够大。

    std::ifstream file(engineName, std::ios::binary);
    if (file.good()) {
        std::cout << "load engine success" << std::endl;
        file.seekg(0, file.end);//指向文件的最后地址
        size = file.tellg();//把文件长度告诉给size
        //std::cout << "\nfile:" << argv[1] << " size is";
        //std::cout << size << "";

        file.seekg(0, file.beg);//指回文件的开始地址
        trtModelStream = new char[size];//开辟一个char 长度是文件的长度
        assert(trtModelStream);//
        file.read(trtModelStream, size);//将文件内容传给trtModelStream
        file.close();//关闭
    }
    else {
        std::cout << "load engine failed" << std::endl;
        return 1;
    }

    runtimeYolov8Seg = createInferRuntime(gLogger);
    assert(runtimeYolov8Seg != nullptr);
    bool didInitPlugins = initLibNvInferPlugins(nullptr, "");
    engineYolov8Seg = runtimeYolov8Seg->deserializeCudaEngine(trtModelStream, size, nullptr);
    assert(engineYolov8Seg != nullptr);
    contextYolov8Seg = engineYolov8Seg->createExecutionContext();
    assert(contextYolov8Seg != nullptr);
    delete[] trtModelStream;

    return true;
}



bool MODELDLL::YoloV8DetectPredict(const Mat& src, Mat& dst, const int& channel, const int& classe, const int& input_h, const int& input_w, const int& Num_box, float& CONF_THRESHOLD, vector<ObjectTR>& output)
{
    cudaSetDevice(DEVICE);

    if (src.empty()) { std::cout << "image load faild" << std::endl; return 1; }
    int img_width = src.cols;
    int img_height = src.rows;
    std::cout << "宽高:" << img_width << " " << img_height << std::endl;
    // Subtract mean from image
    float* data = new float[channel * input_h * input_w];

    Mat pr_img0, pr_img;
    std::vector<int> padsize;
    Mat tempImg = src.clone(); 
    pr_img = preprocess_img(tempImg, input_h, input_w, padsize);       // Resize
    int newh = padsize[0], neww = padsize[1], padh = padsize[2], padw = padsize[3];
    float ratio_h = (float)src.rows / newh;
    float ratio_w = (float)src.cols / neww;
    int i = 0;// [1,3,INPUT_H,INPUT_W]
    //std::cout << "pr_img.step" << pr_img.step << std::endl;
    clock_t start_p = clock();
    for (int row = 0; row < input_h; ++row) {
        uchar* uc_pixel = pr_img.data + row * pr_img.step;//pr_img.step=widthx3 就是每一行有width个3通道的值
        for (int col = 0; col < input_w; ++col)
        {

            data[i] = (float)uc_pixel[2] / 255.0;
            data[i + input_h * input_w] = (float)uc_pixel[1] / 255.0;
            data[i + 2 * input_h * input_w] = (float)uc_pixel[0] / 255.;
            uc_pixel += 3;
            ++i;
        }
    }

//优化一:从30多ms降速到20多,仅提速10ms左右,效果不明显
//#pragma omp parallel for
//    for (int row = 0; row < input_h; ++row) {
//        const uchar* uc_pixel = pr_img.data + row * pr_img.step;
//        int i = row * input_w;
//        for (int col = 0; col < input_w; ++col) {
//            float r = static_cast<float>(uc_pixel[2]) / 255.0f;
//            float g = static_cast<float>(uc_pixel[1]) / 255.0f;
//            float b = static_cast<float>(uc_pixel[0]) / 255.0f;
//            data[i] = r;
//            data[i + input_h * input_w] = g;
//            data[i + 2 * input_h * input_w] = b;
//            uc_pixel += 3;
//            ++i;
//        }
//    }


    clock_t end_p = clock();
    std::cout << "preprocess_img时间:" << end_p - start_p << "ms" << std::endl;
    // Run inference
    static const int OUTPUT_SIZE = Num_box * (classe + 4);//output0
    float* prob = new float[OUTPUT_SIZE];


    //for (int i = 0; i < 10; i++) {//计算10次的推理速度
    //       auto start = std::chrono::system_clock::now();
    //       doInference(*context, data, prob, prob1, 1);
    //       auto end = std::chrono::system_clock::now();
    //       std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;
    //   }
    //auto start = std::chrono::system_clock::now();
    clock_t start = clock();
    //推理
    int batchSize = 1;
    const ICudaEngine& engine = (*contextYolov8Seg).getEngine();

    // Pointers to input and output device buffers to pass to engine.
    // Engine requires exactly IEngine::getNbBindings() number of buffers.
    assert(engine.getNbBindings() == 3);
    void* buffers[3];

    // 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()
    const int inputIndex = engine.getBindingIndex(INPUT_BLOB_NAME);
    const int outputIndex = engine.getBindingIndex(OUTPUT_BLOB_NAME);
  
    // Create GPU buffers on device
    CHECK(cudaMalloc(&buffers[inputIndex], batchSize * 3 * input_h * input_w * sizeof(float)));//
    CHECK(cudaMalloc(&buffers[outputIndex], batchSize * OUTPUT_SIZE * sizeof(float)));
    // cudaMalloc分配内存 cudaFree释放内存 cudaMemcpy或 cudaMemcpyAsync 在主机和设备之间传输数据
    // cudaMemcpy cudaMemcpyAsync 显式地阻塞传输 显式地非阻塞传输 
    // Create stream
    cudaStream_t stream;
    CHECK(cudaStreamCreate(&stream));

    // DMA input batch data to device, infer on the batch asynchronously, and DMA output back to host
    CHECK(cudaMemcpyAsync(buffers[inputIndex], data, batchSize * 3 * input_h * input_w * sizeof(float), cudaMemcpyHostToDevice, stream));
    (*contextYolov8Seg).enqueue(batchSize, buffers, stream, nullptr);
    CHECK(cudaMemcpyAsync(prob, buffers[outputIndex], batchSize * OUTPUT_SIZE * sizeof(float), cudaMemcpyDeviceToHost, stream));
    cudaStreamSynchronize(stream);

    // Release stream and buffers
    cudaStreamDestroy(stream);
    CHECK(cudaFree(buffers[inputIndex]));
    CHECK(cudaFree(buffers[outputIndex]));
    //
    //auto end = std::chrono::system_clock::now();
    clock_t end = clock();
    //std::cout << "推理时间:" << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;
    std::cout << "推理时间:" << end - start << "ms" << std::endl;

    std::vector<int> classIds;//结果id数组
    std::vector<float> confidences;//结果每个id对应置信度数组
    std::vector<cv::Rect> boxes;//每个id矩形框
    // 处理box
    int net_length = classe + 4;
    cv::Mat out1 = cv::Mat(net_length, Num_box, CV_32F, prob);

    //start = std::chrono::system_clock::now();
    start = clock();
    for (int i = 0; i < Num_box; i++) {
        //输出是1*net_length*Num_box;所以每个box的属性是每隔Num_box取一个值,共net_length个值
        cv::Mat scores = out1(Rect(i, 4, 1, classe)).clone();
        Point classIdPoint;
        double max_class_socre;
        minMaxLoc(scores, 0, &max_class_socre, 0, &classIdPoint);
        max_class_socre = (float)max_class_socre;
        if (max_class_socre >= CONF_THRESHOLD) {
            float x = (out1.at<float>(0, i) - padw) * ratio_w;  //cx
            float y = (out1.at<float>(1, i) - padh) * ratio_h;  //cy
            float w = out1.at<float>(2, i) * ratio_w;  //w
            float h = out1.at<float>(3, i) * ratio_h;  //h
            int left = MAX((x - 0.5 * w), 0);
            int top = MAX((y - 0.5 * h), 0);
            int width = (int)w;
            int height = (int)h;
            if (width <= 0 || height <= 0) { continue; }

            classIds.push_back(classIdPoint.y);
            confidences.push_back(max_class_socre);
            boxes.push_back(Rect(left, top, width, height));
        }
    }
    //执行非最大抑制以消除具有较低置信度的冗余重叠框(NMS)
    std::vector<int> nms_result;
    cv::dnn::NMSBoxes(boxes, confidences, CONF_THRESHOLD, NMS_THRESHOLD, nms_result);
    ObjectTR result;
    Rect holeImgRect(0, 0, src.cols, src.rows);
    for (int i = 0; i < nms_result.size(); ++i) {
        int idx = nms_result[i];
        result.classid = classIds[idx];
        result.prob = confidences[idx];
        result.rect = boxes[idx] & holeImgRect;
        output.push_back(result);
    }

    //end = std::chrono::system_clock::now();
    end = clock();
    std::cout << "后处理时间:" << end - start << "ms" << std::endl;

    Mat finalImg = src.clone();
    DrawPredDetect(finalImg, classe, output);
    dst = finalImg.clone();

    // Destroy the engine
   /* contextYolov8Seg->destroy();
    engineYolov8Seg->destroy();
    runtimeYolov8Seg->destroy();*/
    delete data;
    delete prob;
    return 0;

}




void MODELDLL::DrawPredDetect(const Mat& img, const int& classe, std::vector<ObjectTR> result) {
    //生成随机颜色
    std::vector<Scalar> color;
    srand(time(0));
    for (int i = 0; i < classe; i++) {
        int b = rand() % 256;
        int g = rand() % 256;
        int r = rand() % 256;
        color.push_back(Scalar(b, g, r));
    }

    for (int i = 0; i < result.size(); i++) {
        int left, top;
        left = result[i].rect.x;
        top = result[i].rect.y;
        int color_num = i;
        rectangle(img, result[i].rect, Scalar(0, 0, 255), 2, 8);
        //rectangle(img, result[i].box, color[result[i].id], 2, 8);

        char label[100];
        sprintf(label, "%d:%.2f", result[i].classid, result[i].prob);

        int baseLine;
        Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
        top = max(top, labelSize.height);
        /*putText(img, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 1, color[result[i].id], 2);*/
        putText(img, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2);
    }

 
}






// yolov8分割推理
bool MODELDLL::LoadYoloV8SegEngine(const std::string& engineName)
{
    // create a model using the API directly and serialize it to a stream
    char* trtModelStream{ nullptr }; //char* trtModelStream==nullptr;  开辟空指针后 要和new配合使用,比如 trtModelStream = new char[size]
    size_t size{ 0 };//与int固定四个字节不同有所不同,size_t的取值range是目标平台下最大可能的数组尺寸,一些平台下size_t的范围小于int的正数范围,又或者大于unsigned int. 使用Int既有可能浪费,又有可能范围不够大。

    std::ifstream file(engineName, std::ios::binary);
    if (file.good()) {
        std::cout << "load engine success" << std::endl;
        file.seekg(0, file.end);//指向文件的最后地址
        size = file.tellg();//把文件长度告诉给size
        //std::cout << "\nfile:" << argv[1] << " size is";
        //std::cout << size << "";

        file.seekg(0, file.beg);//指回文件的开始地址
        trtModelStream = new char[size];//开辟一个char 长度是文件的长度
        assert(trtModelStream);//
        file.read(trtModelStream, size);//将文件内容传给trtModelStream
        file.close();//关闭
    }
    else {
        std::cout << "load engine failed" << std::endl;
        return 1;
    }
	
    runtimeYolov8Seg = createInferRuntime(gLogger);
    assert(runtimeYolov8Seg != nullptr);
    bool didInitPlugins = initLibNvInferPlugins(nullptr, "");
    engineYolov8Seg = runtimeYolov8Seg->deserializeCudaEngine(trtModelStream, size, nullptr);
    assert(engineYolov8Seg != nullptr);
    contextYolov8Seg = engineYolov8Seg->createExecutionContext();
    assert(contextYolov8Seg != nullptr);
    delete[] trtModelStream;

    return true;
}



bool MODELDLL::YoloV8SegPredict(const Mat& src, Mat& dst, const int& channel, const int& classe, const int& input_h, const int& input_w, 
    const int& segChannels, const int& segWidth, const int& segHeight, const int& Num_box)
{
    cudaSetDevice(DEVICE);

    if (src.empty()) { std::cout << "image load faild" << std::endl; return 1; }
    int img_width = src.cols;
    int img_height = src.rows;
    std::cout << "宽高:" << img_width << " " << img_height << std::endl;
    // Subtract mean from image
    float* data = new float[channel * input_h * input_w];
  
    Mat pr_img0, pr_img;
    std::vector<int> padsize;
    Mat tempImg = src.clone();
    pr_img = preprocess_img(tempImg, input_h, input_w, padsize);       // Resize
    int newh = padsize[0], neww = padsize[1], padh = padsize[2], padw = padsize[3];
    float ratio_h = (float)src.rows / newh;
    float ratio_w = (float)src.cols / neww;
    int i = 0;// [1,3,INPUT_H,INPUT_W]
    //std::cout << "pr_img.step" << pr_img.step << std::endl;
    for (int row = 0; row < input_h; ++row) {
        uchar* uc_pixel = pr_img.data + row * pr_img.step;//pr_img.step=widthx3 就是每一行有width个3通道的值
        for (int col = 0; col < input_w; ++col)
        {

            data[i] = (float)uc_pixel[2] / 255.0;
            data[i + input_h * input_w] = (float)uc_pixel[1] / 255.0;
            data[i + 2 * input_h * input_w] = (float)uc_pixel[0] / 255.;
            uc_pixel += 3;
            ++i;
        }
    }

    // Run inference
    static const int OUTPUT_SIZE = Num_box * (classe + 4 + segChannels);//output0
    static const int OUTPUT_SIZE1 = segChannels * segWidth * segHeight;//output1
    float* prob = new float[OUTPUT_SIZE];
    float* prob1 = new float[OUTPUT_SIZE1];

    //for (int i = 0; i < 10; i++) {//计算10次的推理速度
    //       auto start = std::chrono::system_clock::now();
    //       doInference(*context, data, prob, prob1, 1);
    //       auto end = std::chrono::system_clock::now();
    //       std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;
    //   }
    auto start = std::chrono::system_clock::now();
    //推理
    int batchSize = 1;
    const ICudaEngine& engine = (*contextYolov8Seg).getEngine();

    // Pointers to input and output device buffers to pass to engine.
    // Engine requires exactly IEngine::getNbBindings() number of buffers.
    assert(engine.getNbBindings() == 3);
    void* buffers[3];

    // 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()
    const int inputIndex = engine.getBindingIndex(INPUT_BLOB_NAME);
    const int outputIndex = engine.getBindingIndex(OUTPUT_BLOB_NAME);
    const int outputIndex1 = engine.getBindingIndex(OUTPUT_BLOB_NAME1);

    // Create GPU buffers on device
    CHECK(cudaMalloc(&buffers[inputIndex], batchSize * 3 * input_h * input_w * sizeof(float)));//
    CHECK(cudaMalloc(&buffers[outputIndex], batchSize * OUTPUT_SIZE * sizeof(float)));
    CHECK(cudaMalloc(&buffers[outputIndex1], batchSize * OUTPUT_SIZE1 * sizeof(float)));
    // cudaMalloc分配内存 cudaFree释放内存 cudaMemcpy或 cudaMemcpyAsync 在主机和设备之间传输数据
    // cudaMemcpy cudaMemcpyAsync 显式地阻塞传输 显式地非阻塞传输 
    // Create stream
    cudaStream_t stream;
    CHECK(cudaStreamCreate(&stream));

    // DMA input batch data to device, infer on the batch asynchronously, and DMA output back to host
    CHECK(cudaMemcpyAsync(buffers[inputIndex], data, batchSize * 3 * input_h * input_w * sizeof(float), cudaMemcpyHostToDevice, stream));
    (*contextYolov8Seg).enqueue(batchSize, buffers, stream, nullptr);
    CHECK(cudaMemcpyAsync(prob, buffers[outputIndex], batchSize * OUTPUT_SIZE * sizeof(float), cudaMemcpyDeviceToHost, stream));
    CHECK(cudaMemcpyAsync(prob1, buffers[outputIndex1], batchSize * OUTPUT_SIZE1 * sizeof(float), cudaMemcpyDeviceToHost, stream));
    cudaStreamSynchronize(stream);

    // Release stream and buffers
    cudaStreamDestroy(stream);
    CHECK(cudaFree(buffers[inputIndex]));
    CHECK(cudaFree(buffers[outputIndex]));
    CHECK(cudaFree(buffers[outputIndex1]));
    //
    auto end = std::chrono::system_clock::now();
    std::cout << "推理时间:" << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;

    std::vector<int> classIds;//结果id数组
    std::vector<float> confidences;//结果每个id对应置信度数组
    std::vector<cv::Rect> boxes;//每个id矩形框
    std::vector<cv::Mat> picked_proposals;  //后续计算mask
        // 处理box
    int net_length = classe + 4 + segChannels;
    cv::Mat out1 = cv::Mat(net_length, Num_box, CV_32F, prob);

    start = std::chrono::system_clock::now();
    for (int i = 0; i < Num_box; i++) {
        //输出是1*net_length*Num_box;所以每个box的属性是每隔Num_box取一个值,共net_length个值
        cv::Mat scores = out1(Rect(i, 4, 1, classe)).clone();
        Point classIdPoint;
        double max_class_socre;
        minMaxLoc(scores, 0, &max_class_socre, 0, &classIdPoint);
        max_class_socre = (float)max_class_socre;
        if (max_class_socre >= CONF_THRESHOLD) {
            cv::Mat temp_proto = out1(Rect(i, 4 + classe, 1, segChannels)).clone();
            picked_proposals.push_back(temp_proto.t());
            float x = (out1.at<float>(0, i) - padw) * ratio_w;  //cx
            float y = (out1.at<float>(1, i) - padh) * ratio_h;  //cy
            float w = out1.at<float>(2, i) * ratio_w;  //w
            float h = out1.at<float>(3, i) * ratio_h;  //h
            int left = MAX((x - 0.5 * w), 0);
            int top = MAX((y - 0.5 * h), 0);
            int width = (int)w;
            int height = (int)h;
            if (width <= 0 || height <= 0) { continue; }

            classIds.push_back(classIdPoint.y);
            confidences.push_back(max_class_socre);
            boxes.push_back(Rect(left, top, width, height));
        }

    }
    //执行非最大抑制以消除具有较低置信度的冗余重叠框(NMS)
    std::vector<int> nms_result;
    cv::dnn::NMSBoxes(boxes, confidences, CONF_THRESHOLD, NMS_THRESHOLD, nms_result);
    std::vector<cv::Mat> temp_mask_proposals;
    std::vector<OutputSeg> output;
    Rect holeImgRect(0, 0, src.cols, src.rows);
    for (int i = 0; i < nms_result.size(); ++i) {
        int idx = nms_result[i];
        OutputSeg result;
        result.id = classIds[idx];
        result.confidence = confidences[idx];
        result.box = boxes[idx] & holeImgRect;
        output.push_back(result);
        temp_mask_proposals.push_back(picked_proposals[idx]);
    }

    // 处理mask
    Mat maskProposals;
    for (int i = 0; i < temp_mask_proposals.size(); ++i)
        maskProposals.push_back(temp_mask_proposals[i]);

    Mat protos = Mat(segChannels, segWidth * segHeight, CV_32F, prob1);
    Mat matmulRes = (maskProposals * protos).t();//n*32 32*25600 A*B是以数学运算中矩阵相乘的方式实现的,要求A的列数等于B的行数时
    Mat masks = matmulRes.reshape(output.size(), { segWidth, segHeight});//n*160*160

    std::vector<Mat> maskChannels;
    cv::split(masks, maskChannels);
    Rect roi(int((float)padw / input_w * segWidth), int((float)padh / input_h * segHeight), int(segWidth - padw / 2), int(segHeight - padh / 2));
    for (int i = 0; i < output.size(); ++i) {
        Mat dest, mask;
        cv::exp(-maskChannels[i], dest);//sigmoid
        dest = 1.0 / (1.0 + dest);//160*160
        dest = dest(roi);
        resize(dest, mask, cv::Size(src.cols, src.rows), INTER_NEAREST);
        //crop----截取box中的mask作为该box对应的mask
        Rect temp_rect = output[i].box;
        mask = mask(temp_rect) > MASK_THRESHOLD;
        output[i].boxMask = mask;
    }
    end = std::chrono::system_clock::now();
    std::cout << "后处理时间:" << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;

    Mat finalImg = src.clone();
    DrawPred(finalImg, classe, output);
    dst = finalImg.clone();

    // Destroy the engine
    contextYolov8Seg->destroy();
    engineYolov8Seg->destroy();
    runtimeYolov8Seg->destroy();
    delete data;
    delete prob;
    delete prob1;
    return 0;

}




void MODELDLL::DrawPred(const Mat& img, const int& classe, std::vector<OutputSeg> result) {
    //生成随机颜色
    std::vector<Scalar> color;
    srand(time(0));
    for (int i = 0; i < classe; i++) {
        int b = rand() % 256;
        int g = rand() % 256;
        int r = rand() % 256;
        color.push_back(Scalar(b, g, r));
    }
    Mat mask = img.clone();
    for (int i = 0; i < result.size(); i++) {
        int left, top;
        left = result[i].box.x;
        top = result[i].box.y;
        int color_num = i;
        rectangle(img, result[i].box, color[result[i].id], 2, 8);

        mask(result[i].box).setTo(color[result[i].id], result[i].boxMask);
        char label[100];
        sprintf(label, "%d:%.2f", result[i].id, result[i].confidence);

        //std::string label = std::to_string(result[i].id) + ":" + std::to_string(result[i].confidence);
        int baseLine;
        Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
        top = max(top, labelSize.height);
        putText(img, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 1, color[result[i].id], 2);
    }

    addWeighted(img, 0.5, mask, 0.8, 1, img); //将mask加在原图上面


}

完整项目:https://download.csdn.net/download/qq_44747572/88791748

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

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

相关文章

谷粒商城【成神路】-【3】——三级分类

目录 &#x1f37f;1.查询三级分类 &#x1f9c2;2.前端页面搭建 &#x1f35f;3.添加网关 &#x1f373;4.解决跨域 &#x1f9c7;5.显示分类 &#x1f95e;6.显示复选框 1.查询三级分类 1.controller 直接调用service层的接口 RequestMapping("/list/tree&qu…

02.PostgreSQL运算符

1. 算术运算符 算术运算符 描述 示例 + 加法运算符 SELECT A+B - 减法运算符 SELECT A-B * 乘法运算符 SELECT A*B / 除法运算符 SELECT A/B % 取余运算符 SELECT A%B 1.1 加法与减法操作符 SELECT 100,100+11,100-11,100+23.0,100-23.0 运算结果 由此得出结论: 一个整数加上…

C语言·贪吃蛇游戏(下)

上节我们将要完成贪吃蛇游戏所需的前置知识都学完了&#xff0c;那么这节我们就开始动手写代码了 1. 程序规划 首先我们应该规划好我们的代码文件&#xff0c;设置3个文件&#xff1a;snack.h 用来声明游戏中实现各种功能的函数&#xff0c;snack.c 用来实现函数&#xff0c;t…

javaScript的序列化与反序列化

render函数的基本实现 javaScript的序列化与反序列化 一&#xff0c;js中的序列化二&#xff0c;序列化三&#xff0c;反序列化四&#xff0c;总结 一&#xff0c;js中的序列化 js中序列化就是对象转换成json格式的字符串&#xff0c;使用JSON对象的stringify方法&#xff0c;…

R-YOLO

Abstract 提出了一个框架&#xff0c;名为R-YOLO&#xff0c;不需要在恶劣天气下进行注释。考虑到正常天气图像和不利天气图像之间的分布差距&#xff0c;我们的框架由图像翻译网络&#xff08;QTNet&#xff09;和特征校准网络&#xff08;FCNet&#xff09;组成&#xff0c;…

vue3-深入组件-依赖注入

Prop 逐级透传问题 通常情况下&#xff0c;当我们需要从父组件向子组件传递数据时&#xff0c;会使用 props。 如果是多层级嵌套的组件&#xff0c;如何从一级传递到 3 级甚至更远呢。 若使用 props 则必须将其沿着组件链逐级传递下去&#xff0c;这会非常麻烦&#xff0c;所…

canvas路径剪裁clip(图文示例)

查看专栏目录 canvas实例应用100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

jupyter notebook显示的扩展很少,只有四五个--解决方案

如下&#xff1a;安装好只有四五个扩展 可以先删除 conda remove jupyter_nbextensions_configurator 然后使用pip安装 pip install jupyter_contrib_nbextensions jupyter contrib nbextensions install --user pip install jupyter_nbextensions_configurator jupyter nbex…

MySQL-运维-主从复制

一、概述 二、原理 三、搭建 1、服务器准备 2、主库配置 &#xff08;1&#xff09;、修改配置文件/etc/my.cnf &#xff08;2&#xff09;、重启MySQL服务器 &#xff08;3&#xff09;、登录mysql&#xff0c;创建远程链接的账号&#xff0c;并授予主从复制权限 &#xff0…

Kafka-服务端-GroupCoordinator

在每一个Broker上都会实例化一个GroupCoordinator对象&#xff0c;Kafka按照Consumer Group的名称将其分配给对应的GroupCoordinator进行管理&#xff1b; 每个GroupCoordinator只负责管理Consumer Group的一个子集&#xff0c;而非集群中全部的Consumer Group。 请注意与Kaf…

华媒舍:10个与汽车媒体国外传播有关的方向

随着近年来汽车销售市场的不断增加与发展&#xff0c;汽车媒体国外传播已经成为汽车行业里至关重要的一环。下面我们就详细介绍10个与汽车媒体国外传播有关的发展方向&#xff0c;并讨论这些趋势对全世界汽车行业的影响。 1.智能化媒体的兴起伴随着互联网的发展与发展&#xff…

2024年第4届IEEE软件工程与人工智能国际会议(SEAI 2024)

2024年第4届IEEE软件工程与人工智能国际会议(SEAI 2024)将于2024年6月21-23日在中国厦门举办。 SEAI旨在为软件工程与人工智能领域搭建高端前沿的交流平台&#xff0c;推动产业发展。本次会议将汇聚海内外的知名专家、学者和产业界优秀人才&#xff0c;共同围绕国际热点话题、核…

地理坐标系、空间坐标系、epsg查询网站

坐标系可用范围和详细信息的查询网站 简介 epsg.ruiduobao.com是一个可以查询gdal中所有坐标系信息的网站&#xff0c;可查询到坐标系的基准面、椭球体、中央子午线等相关信息&#xff0c;并对每个坐标系的可用范围在地图中进行了显示。详细信息可以看操作视频&#xff1a; e…

bank conflict

前置知识&#xff1a; shared memory 被分成 32 个 bank一个 warp 32 个线程每个 bank 4 byte如果同一 warp 中不同线程访问同一 bank 的不同地址则发生 bank conflict 请注意需要是一个 warp 中的不同线程&#xff01;如果一个线程访问 shared memory 的两个元素&#xff0c;…

【ArcGIS Pro】从0开始

1.导入excel&#xff0c;需要安装驱动程序 安装用于 Microsoft Excel 文件的驱动程序 https://pro.arcgis.com/zh-cn/pro-app/latest/help/data/excel/prepare-to-work-with-excel-in-arcgis-pro.htm 2.修改投影坐标系 点到地图图标上&#xff0c;右键才能设置坐标系。 3.…

MSVC++远程调试

1. 介绍 MSVC的调试功能非常强大&#xff0c;可以下断点&#xff0c;单步调试&#xff0c;查看堆栈变量信息等。实际用于生产的电脑环境复杂&#xff0c;更容易发生Bug。生产电脑&#xff0c;由于各种原因有些可能无法安装MSVC用来现场调试。基于打印日志&#xff0c;查看日志…

Elasticsearch:将文档级安全性 (DLS) 添加到你的内部知识搜索

作者&#xff1a;来自 Elastic Sean Story 你的企业很可能淹没在内部数据中。 你拥有问题跟踪、笔记记录、会议记录、维基页面、视频录制、聊天以及即时消息和私信。 并且不要忘记电子邮件&#xff01; 难怪如此多的企业都在尝试创造工作场所搜索体验 - 为员工提供集中、一站…

如何部署Docker Registry并实现无公网ip远程连接本地镜像仓库

文章目录 1. 部署Docker Registry2. 本地测试推送镜像3. Linux 安装cpolar4. 配置Docker Registry公网访问地址5. 公网远程推送Docker Registry6. 固定Docker Registry公网地址 Docker Registry 本地镜像仓库,简单几步结合cpolar内网穿透工具实现远程pull or push (拉取和推送)…

java程序读取并控制串口设备

监听串口&#xff0c;接收它们发过来的数据&#xff0c;进行处理。 一、概况 前不久做的一个项目&#xff0c;需要读取水下传感器的数据。这些传感器通过串口与外界交互。我们写了一个java程序&#xff0c;接收传感器传送的数据&#xff0c;同时也下发命令&#xff0c;控制部…

车载电子电器架构 —— IP地址获取策略

车载电子电器架构 —— IP地址获取策略 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自…