Tic商业评论

关注微信公众号【站长自定义模块】,定时推送前沿、专业、深度的商业资讯。

 找回密码
 立即注册

QQ登录

只需一步,快速开始

微信登录

微信扫码,快速开始

yolov7 opencv c++部署

0
回复
4691
查看
[复制链接]
已绑定手机

49

主题

4

回帖

1228

积分

管理员

积分
1228
QQ
来源: 2023-2-14 17:41:59 显示全部楼层 |阅读模式
这里主要是OpenCV dnn部署yolov7, 在部署yolov7原版是没有问题的。这里直接训练出来yolov7模型,然后根据作者提供的export.py导出.onnx模型文件。

一,导出onnx文件

导出时后面不要添加 --grid ,否则opencv读取没问题,推理报错。nms我们要后期自己做处理。
命令如下

python export.py
当然了export.py里面的参数要设置好,下面是我设置的参数:
parser.add_argument('--weights', type=str, default='/home/fpga/Desktop/test/yolo/rgb/best.pt', help='weights path')

  parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='image size')  # height, width

  parser.add_argument('--batch-size', type=int, default=1, help='batch size')

  parser.add_argument('--dynamic', action='store_true', help='dynamic ONNX axes')

  parser.add_argument('--dynamic-batch', action='store_true', help='dynamic batch onnx for tensorrt and onnx-runtime')

  parser.add_argument('--grid', action='store_true', help='export Detect() layer grid')

  parser.add_argument('--end2end', action='store_true', help='export end2end onnx')

  parser.add_argument('--max-wh', type=int, default=None, help='None for tensorrt nms, int value for onnx-runtime nms')

  parser.add_argument('--topk-all', type=int, default=100, help='topk objects for every images')

  parser.add_argument('--iou-thres', type=float, default=0.45, help='iou threshold for NMS')

  parser.add_argument('--conf-thres', type=float, default=0.1, help='conf threshold for NMS')

  parser.add_argument('--device', default='cpu', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')

  parser.add_argument('--simplify', action='store_true', help='simplify onnx model')

  parser.add_argument('--include-nms', action='store_true', help='export end2end onnx')

  parser.add_argument('--fp16', action='store_true', help='CoreML FP16 half-precision export')

  parser.add_argument('--int8', action='store_true', help='CoreML INT8 quantization')

二. 模型部署:

如果采用的是yolov7-e6就需要对训练代码进行修改,主要是ReOrg算子OpenCV不支持,所以需要对其进行修改。ReOrg算子是silce操作,所以存在一定的问题。
修改后就可以按照原版进行转换模型导出。
具体修改如下:
```

class ReOrg(nn.Module):

  def __init__(self):

    super(ReOrg, self).__init__()



  def forward(self, x):  # x(b,c,w,h) -> y(b,4c,w/2,h/2)

    #origin code

    # return torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1)

    self.concat=Contract(gain=2)

    return self.concat(x)

```
这里Contract(gain=2)函数是在models/common.py里的,这里和yolov5 focus是类似的。部署修改方法都是一样的。


2.OpenCV版本不一致导致的问题:


OpenCV 4.6要添加如下代码:

std::sort(netOutputImg.begin(), netOutputImg.end(), [](Mat &A, Mat &B) {return A.size > B.size; });//opencv 4.6




OpenCV部署如下部分代码:
std::vector<int> classIds;//结果id数组

    std::vector<float> confidences;//结果每个id对应置信度数组

    std::vector<cv::Rect> boxes;//每个id矩形框

    float ratio_h = (float)netInputImg.rows / netHeight;

    float ratio_w = (float)netInputImg.cols / netWidth;

    int net_width = className.size() + 5;  //输出的网络宽度是类别数+5

    for (int stride = 0; stride < strideSize; stride++) {  //stride

        float* pdata = (float*)netOutputImg[stride].data;

        int grid_x = (int)(netWidth / netStride[stride]);

        int grid_y = (int)(netHeight / netStride[stride]);

        for (int anchor = 0; anchor < 3; anchor++) {    //anchors

            const float anchor_w = netAnchors[stride][anchor * 2];

            const float anchor_h = netAnchors[stride][anchor * 2 + 1];

            for (int i = 0; i < grid_y; i++) {

                for (int j = 0; j < grid_x; j++) {

                    float box_score = sigmoid_x(pdata[4]); ;//获取每一行的box框中含有某个物体的概率

                    if (box_score >= boxThreshold) {

                        cv::Mat scores(1, className.size(), CV_32FC1, pdata + 5);

                        Point classIdPoint;

                        double max_class_socre;

                        minMaxLoc(scores, 0, &max_class_socre, 0, &classIdPoint);

                        max_class_socre = sigmoid_x(max_class_socre);

                        if (max_class_socre >= classThreshold) {

                            float x = (sigmoid_x(pdata[0]) * 2.f - 0.5f + j) * netStride[stride];  //x

                            float y = (sigmoid_x(pdata[1]) * 2.f - 0.5f + i) * netStride[stride];   //y

                            float w = powf(sigmoid_x(pdata[2]) * 2.f, 2.f) * anchor_w;   //w

                            float h = powf(sigmoid_x(pdata[3]) * 2.f, 2.f) * anchor_h;  //h

                            int left = (int)(x - 0.5 * w) * ratio_w + 0.5;

                            int top = (int)(y - 0.5 * h) * ratio_h + 0.5;

                            classIds.push_back(classIdPoint.x);

                            confidences.push_back(max_class_socre * box_score);

                            boxes.push_back(Rect(left, top, int(w * ratio_w), int(h * ratio_h)));

                        }

                    }

                    pdata += net_width;//下一行

                }

            }

        }

    }







三,Python版本
Python版本和c++版本类似无非就是c++代码转化位Python代码,export导出onnx是相同的。


更多代码链接:

Screenshot from 2023-02-15 09-50-36.png
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册
电话咨询: 135xxxxxxx
关注微信