使用OpenCVSharp Dnn部署FastestDet

FastestDet:轻量级目标检测 实时Anchor-free目标检测算法,虽然精度稍逊,但FastestDet在速度和参数量上有显著优势,适用于资源有限的设备。
FastDet项目链接:https://github.com/dog-qiuqiu/FastestDet

第一次写文章,不知道该写些什么,直接上代码吧,不足之处还望海涵

onnx模型直接使用作者给的模型 FastestDet-main/example/onnx-runtime/FastestDet.onnx,模型大小不到1MB大小,不得不说,这是真的轻量👍

模型信息
Inputs
-------------------------
name:input.1
tensor:Float[1, 3, 352, 352]
---------------------------------------------------------------

Outputs
-------------------------
name:758
tensor:Float[1, 85, 22, 22]
---------------------------------------------------------------

代码参考 FastestDet-main/example/ncnn/FastestDet.cpp

经多次测试,单次平均推理速度在15ms左右,速度是真的块

其中NMS(非极大值抑制)用的是作者给的函数,也可以使用OpenCV自带的CvDnn.NMSBoxes函数

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Dnn;
using Point = OpenCvSharp.Point;
using Size = OpenCvSharp.Size;


namespace OPENCV
{
    public partial class FastDetect : Form
    {
        public FastDetect()
        {
            InitializeComponent();
        }
        string fileFilter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";
        string image_path = "";
        string[] class_names;
        // 阈值
        float thresh = 0.65f;
        // 模型输入宽高
        int input_width = 352;
        int input_height = 352;
        Net net;
        float Sigmoid(float x)
        {
            return (float)(1.0f / (1.0f + Math.Exp(-x)));
        }

        float Tanh(float x)
        {
            return (float)(2.0f / (1.0f + Math.Exp(-2 * x)) - 1);
        }
        private float IntersectionArea(TargetBox a, TargetBox b)
        {
            if (a.x1 > b.x2 || a.x2 < b.x1 || a.y1 > b.y2 || a.y2 < b.y1)
            {
                // no intersection
                return 0.0f;
            }

            float inter_width = Math.Min(a.x2, b.x2) - Math.Max(a.x1, b.x1);
            float inter_height = Math.Min(a.y2, b.y2) - Math.Min(a.y1, b.y1);

            return inter_width * inter_height;
        }
        int scoreSort(TargetBox a, TargetBox b)
        {
            if (a.score > b.score) 
                return 1;
            else
                return 0;
        }
        int nmsHandle(List<TargetBox> src_boxes, List<TargetBox> dst_boxes)
        {
            List<int> picked = new List<int>();
            src_boxes.Sort((x,y)=> scoreSort(x, y)); 
            for (int i = 0; i < src_boxes.Count; i++)
            {
                int keep = 1;
                for (int j = 0; j < picked.Count; j++)
                {
                    //交集
                    float inter_area = IntersectionArea(src_boxes[i], src_boxes[picked[j]]);
                    //并集
                    float union_area = src_boxes[i].area() + src_boxes[picked[j]].area() - inter_area;
                    float IoU = inter_area / union_area;

                    if (IoU > 0.45 && src_boxes[i].category == src_boxes[picked[j]].category)
                    {
                        keep = 0;
                        break;
                    }
                }
                if (keep > 0)
                {
                    picked.Add(i);
                }
            }
            for (int i = 0; i < picked.Count; i++)
            {
                dst_boxes.Add(src_boxes[picked[i]]);
            }
            return 0;
        }
        private void FastDetect_Load(object sender, EventArgs e)
        {
            net = CvDnn.ReadNetFromOnnx("model/FastestDet.onnx");
            net.SetPreferableBackend(Backend.OPENCV);
            net.SetPreferableTarget(Target.CPU);
            // 类别标签
            class_names = File.ReadAllLines("model/coco.names");
        }
        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = fileFilter;
            if (ofd.ShowDialog() != DialogResult.OK) return;
            pictureBox1.Image = null;
            image_path = ofd.FileName;
            pictureBox1.Image = new Bitmap(image_path);
        }
        private void button2_Click(object sender, EventArgs e)
        {
            // 类别数量
            int class_num = class_names.Length;
            Mat img = Cv2.ImRead(image_path);
            //输入图像的原始宽高
            int img_width = img.Cols;
            int img_height = img.Rows;
            var blob = CvDnn.BlobFromImage(img, 1 / 255.0, new Size(input_width, input_height), 0, true, false);
            DateTime start = DateTime.Now;
            net.SetInput(blob);
            Mat pred = net.Forward();
            List<TargetBox> target_boxes = new List<TargetBox>();
            float[] output = new float[22 * 22 * 85];
            Marshal.Copy(pred.Data,output,0,output.Length);
            for (int h = 0; h < 22; h++)
            {
                for (int w = 0; w < 22; w++)
                {
                    // 前景概率
                    int obj_score_index = (0 * 22 * 22) + (h * 22) + w;
                    float obj_score = output[obj_score_index];

                    // 解析类别
                    int category = 0;
                    float max_score = 0.0f;
                    for (int i = 0; i < class_num; i++)
                    {
                        obj_score_index = ((5 + i) * 22 * 22) + (h * 22) + w;
                        float cls_score = output[obj_score_index];
                        if (cls_score > max_score)
                        {
                            max_score = cls_score;
                            category = i;
                        }
                    }
                    float score = (float)(Math.Pow(max_score, 0.4) * Math.Pow(obj_score, 0.6));

                    // 阈值筛选
                    if (score > thresh)
                    {
                        // 解析坐标
                        int x_offset_index = (1 * 22 * 22) + (h * 22) + w;
                        int y_offset_index = (2 * 22 * 22) + (h * 22) + w;
                        int box_width_index = (3 * 22 * 22) + (h * 22) + w;
                        int box_height_index = (4 * 22 * 22) + (h * 22) + w;

                        float x_offset = Tanh(output[x_offset_index]);
                        float y_offset = Tanh(output[y_offset_index]);
                        float box_width = Sigmoid(output[box_width_index]);
                        float box_height = Sigmoid(output[box_height_index]);

                        float cx = (w + x_offset) / 22;
                        float cy = (h + y_offset) / 22;

                        int x1 = (int)((cx - box_width * 0.5) * img_width);
                        int y1 = (int)((cy - box_height * 0.5) * img_height);
                        int x2 = (int)((cx + box_width * 0.5) * img_width);
                        int y2 = (int)((cy + box_height * 0.5) * img_height);

                        target_boxes.Add(new TargetBox(x1, y1, x2, y2, category, score));
                    }
                }
            }
            // NMS处理
            List<TargetBox> nms_boxes = new List<TargetBox>();
            nmsHandle(target_boxes, nms_boxes);
            // 打印耗时
            DateTime end = DateTime.Now;
            double time = (end - start).TotalMilliseconds;
            textBox1.AppendText(string.Format("Time:{0} ms\r\n", time));
            // draw result
            for (int i = 0; i < nms_boxes.Count; i++)
            {
                TargetBox box = nms_boxes[i];
                string s_score = box.score.ToString("P0");
                Cv2.Rectangle(img, new Point(box.x1, box.y1), new Point(box.x2, box.y2), new Scalar(0, 0, 255), 2);
                Cv2.PutText(img, class_names[box.category]+":"+s_score, new Point(box.x1, box.y1), HersheyFonts.HersheySimplex, 0.65, new Scalar(0, 255, 0), 1);
            }
            pictureBox2.Image?.Dispose();
            pictureBox2.Image = Bitmap.FromStream(img.ToMemoryStream());
            blob.Dispose();
            pred.Dispose();
            img.Dispose();
        } 
    } 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值