第一步:安装Opencv

安装Opencv参考链接:安装OpenCV

获取yolov5s.pt权值文件

使用的是轻量的yolov5s模型,在本地训练后,通过命令发送发到树莓派上:

  1. 首先查看树莓派地址:
    ifconfig  # 查看地址


连接的wify,可以看到所属的地址端口。

  1. 将本地导出的best.onnx权值文件发送到树莓派上:
    scp best.onnx <用户名>@<树莓派地址>:<保存文件地址>  
  1. 推理代码
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <chrono>  // 添加时间测量头文件

using namespace cv;
using namespace cv::dnn;

std::vector<std::string> classes = {
	"defective_insulator","broken_defect","good_insulator","flashover_defect"
};

int main() {
	// 1. 输入图片路径
    std::string img_path = "images/insulator.jpg"; // 输入图片路径
    Mat frame = imread(img_path);
    if(frame.empty()) {
        std::cerr << "Error: Could not read the image." << std::endl;
        return -1;
    }
	// 2. 加载模型
    Net net =  cv::dnn::readNetFromONNX("weights/ZJ/best.onnx");
	net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
	net.setPreferableTarget(cv::dnn::DNN_TARGET_OPENCL);
	// 打印模型信息
    // std::cout << "Network layers:" << std::endl;
    // for (int i = 0; i < net.getLayerNames().size(); i++) {
    //     std::cout << net.getLayerNames()[i] << std::endl;
    // }
	
    if (net.empty()) {
        std::cerr << "Error: Could not load the neural network." << std::endl;
        return -1;
    }


	// 3. 预处理参数设置
    const int input_width = 512;
    const int input_height = 512;
    float x_factor = frame.cols / (float)input_width;  // 修改为动态计算
    float y_factor = frame.rows / (float)input_height;


	// 预处理计时
	auto preprocess_start = std::chrono::high_resolution_clock::now();

	// 4. 图像预处理(增加letterbox处理)
    Mat resized;
    int new_width, new_height;
    if (frame.cols > frame.rows) {
        new_width = input_width;
        new_height = (int)(frame.rows * input_width / (float)frame.cols);
    } else {
        new_height = input_height;
        new_width = (int)(frame.cols * input_height / (float)frame.rows);
    }
    resize(frame, resized, Size(new_width, new_height));
    
    Mat blob = blobFromImage(resized, 1/255.0, Size(input_width, input_height), Scalar(0,0,0), true, false);
	// blob.convertTo(blob, CV_16F);  // 半精度计算
	// 预处理计时结束
	auto preprocess_end = std::chrono::high_resolution_clock::now();

	// 推理计时
	auto inference_start = std::chrono::high_resolution_clock::now();

    net.setInput(blob);

	// 5. 推理后处理
	cv::Mat preds =net.forward();

	// 推理计时结束
	auto inference_end = std::chrono::high_resolution_clock::now();

	// 后处理计时
    auto postprocess_start = std::chrono::high_resolution_clock::now();


	std::vector<int> class_ids;
	std::vector<float> confidences;
	std::vector<Rect> boxes;
	std::cout << "rows: "<< preds.size[1]<< " data: " << preds.size[2] << std::endl;
	// cv::Mat det_output(preds.size[1], preds.size[2], CV_32F, preds.ptr<float>());
	cv::Mat det_output = preds.reshape(1, preds.size[1]); // 避免内存复制
	//In a typical YOLO output, the format is [x_center, y_center, width, height, object_confidence, class_score1, class_score2, ..., class_scoreN] for each bounding box. 
	for (int i = 0; i < det_output.rows; i++) {
		float confidence = det_output.at<float>(i, 4);
		cv::Mat class_scores = det_output.row(i).colRange(5, 5 + classes.size());

		
		Point class_id_point;
		double max_class_score;
		minMaxLoc(class_scores, NULL, &max_class_score, NULL, &class_id_point);
		int class_id = class_id_point.x; 

		float final_confidence = confidence * max_class_score;

		// std::cout << "Final confidence: " << final_confidence << std::endl;

		if (final_confidence < 0.45) {
			continue;
		}
		
		float cx = det_output.at<float>(i, 0);
		float cy = det_output.at<float>(i, 1);
		float ow = det_output.at<float>(i, 2);
		float oh = det_output.at<float>(i, 3);
		int x = static_cast<int>((cx - 0.5 * ow) * x_factor);
		int y = static_cast<int>((cy - 0.5 * oh) * y_factor);
		int width = static_cast<int>(ow * x_factor);
		int height = static_cast<int>(oh * y_factor);
		
        boxes.push_back(Rect(x, y, width, height));
        confidences.push_back(final_confidence);
        class_ids.push_back(class_id);
	}


	std::vector<int> indices;
	dnn::NMSBoxes(boxes, confidences, 0.6, 0.3, indices); 

	// Draw the final bounding boxes
	for (size_t i = 0; i < indices.size(); ++i) {
		int idx = indices[i];
		Rect box = boxes[idx];
		cv::rectangle(frame, cv::Point(box.x, box.y), cv::Point(box.x + box.width, box.y + box.height), cv::Scalar(0, 0, 255), 2, 8);
	
	
	std::string label = classes[class_ids[idx]] + ": " + std::to_string(confidences[idx]);
	putText(frame, label.c_str(), Point(box.x, box.y - 10), FONT_HERSHEY_SIMPLEX, 0.9, Scalar(0, 255, 0), 2);
		}

	auto postprocess_end = std::chrono::high_resolution_clock::now();
	
	// 输出各阶段耗时(单位:ms)
	float preprocess_time = std::chrono::duration_cast<std::chrono::milliseconds>(preprocess_end - preprocess_start).count();
	float inference_time = std::chrono::duration_cast<std::chrono::milliseconds>(inference_end - inference_start).count();
	float postprocess_time = std::chrono::duration_cast<std::chrono::milliseconds>(postprocess_end - postprocess_start).count();

	// 计算总耗时(单位:ms)
	auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(postprocess_end - preprocess_start).count();
	std::cout << "预处理: " << preprocess_time << " ms" << std::endl;
	std::cout << "推理: " << inference_time << " ms" << std::endl; 
	std::cout << "后处理: " << postprocess_time << " ms" << std::endl;
	std::cout << "总耗时: " << duration << " ms" << std::endl;
	
	float fps = 1000.0 / duration;  // 计算FPS
	std::cout << "FPS: " << fps << std::endl;
	// 6. 显示结果(取消视频循环)
	imshow("Object Detection", frame);
	waitKey(0);  // 改为等待按键

    return 0;
}