赞
踩
1.使用yolov8.2版本,yolov8-ncnn github代码放在最后
https://github.com/ultralytics/ultralytics.git
2.训练自己的数据,修改data.yaml,类别数80,修改第一个类别为自己的类别
3.运行训练脚本
yolo detect train model= yolov8n.pt data= ultralytics/cfg/datasets/cuoceng.yaml epochs=200 batch=128 device=2,3
4.训练完成得到pt模型文件
1.修改 ultralytics/nn/modules/head.py中的Class Detect forward
def forward(self, x):
shape = x[0].shape # BCHW
for i in range(self.nl):
x[i] = torch.cat((self.cv2[i](x[i]), self.cv3[i](x[i])), 1)
if self.training:
return x
elif self.dynamic or self.shape != shape:
self.anchors, self.strides = (x.transpose(0, 1) for x in make_anchors(x, self.stride, 0.5))
self.shape = shape
# x_cat = torch.cat([xi.view(shape[0], self.no, -1) for xi in x], 2)
# if self.export and self.format in ('saved_model', 'pb', 'tflite', 'edgetpu', 'tfjs'): # avoid TF FlexSplitV ops
# box = x_cat[:, :self.reg_max * 4]
# cls = x_cat[:, self.reg_max * 4:]
# else:
# box, cls = x_cat.split((self.reg_max * 4, self.nc), 1)
# dbox = dist2bbox(self.dfl(box), self.anchors.unsqueeze(0), xywh=True, dim=1) * self.strides
# y = torch.cat((dbox, cls.sigmoid()), 1)
# return y if self.export else (y, x)
pred = torch.cat([xi.view(shape[0], self.no, -1) for xi in x], 2).permute(0, 2, 1)
return pred
2.修改ultralytics/nn/modules/block.py中的Class C2f forward
class C2f(nn.Module):
export = False # export mode
# CSP Bottleneck with 2 convolutions
def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
super().__init__()
self.c = int(c2 * e) # hidden channels
self.cv1 = Conv(c1, 2 * self.c, 1, 1)
self.cv2 = Conv((2 + n) * self.c, c2, 1) # optional act=FReLU(c2)
self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n))
def forward(self, x):
if not self.export:
y = list(self.cv1(x).chunk(2, 1))
y.extend(m(y[-1]) for m in self.m)
return self.cv2(torch.cat(y, 1))
x = self.cv1(x)
x = [x, x[:, self.c:, ...]]
x.extend(m(x[-1]) for m in self.m)
x.pop(1)
return self.cv2(torch.cat(x, 1))
def forward_split(self, x):
if not self.export:
print("------------> c2f forward_split:")
y = list(self.cv1(x).split((self.c, self.c), 1))
y.extend(m(y[-1]) for m in self.m)
return self.cv2(torch.cat(y, 1))
x = self.cv1(x)
x = [x, x[:, self.c:, ...]]
x.extend(m(x[-1]) for m in self.m)
x.pop(1)
return self.cv2(torch.cat(x, 1))
3.export转换onnx
yolo export model=best.pt format=onnx opset=12 simplify=True
../tools/onnx/onnx2ncnn model/best.onnx model/warp32.param model/warp32.bin
../tools/ncnnoptimize model/warp32.param model/warp32.bin model/warp16.param model/warp16.bin 65536
find /data/wangshuai/DATA_ws/DATA_1st_warping/DataSet_ZYB/images/train -type f > imagelist.txt
../tools/quantize/ncnn2table model/warp16.param model/warp16.bin imagelist.txt warp.table mean=[104,117,123] norm=[0.017,0.017,0.017] shape=[320,320,3] pixel=BGR thread=8 method=kl
../tools/quantize/ncnn2int8 model/warp16.param model/warp16.bin model/warp8-warp.param model/warp8-warp.bin warp.table
1.yolov8.cpp
// Tencent is pleased to support the open source community by making ncnn available.
//
// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// https://opensource.org/licenses/BSD-3-Clause
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//modified 1-14-2023 Q-engineering
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <stdio.h>
#include <vector>
#include "net.h"
struct Object
{
cv::Rect_<float> rect;
int label;
float prob;
};
struct GridAndStride
{
int grid0;
int grid1;
int stride;
};
class YoloV8
{
public:
YoloV8();
int load(int target_size);
int detect(const cv::Mat& rgb, std::vector<Object>& objects, float prob_threshold = 0.4f, float nms_threshold = 0.5f);
int draw(cv::Mat& rgb, const std::vector<Object>& objects);
private:
ncnn::Net yolo;
int target_size;
float mean_vals[3];
float norm_vals[3];
};
const char* class_names[] = {
"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light",
"fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow",
"elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee",
"skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard",
"tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
"sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch",
"potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone",
"microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear",
"hair drier", "toothbrush"
};
static float fast_exp(float x)
{
union {
uint32_t i;
float f;
} v{};
v.i = (1 << 23) * (1.4426950409 * x + 126.93490512f);
return v.f;
}
static float sigmoid(float x)
{
return 1.0f / (1.0f + fast_exp(-x));
}
static float intersection_area(const Object& a, const Object& b)
{
cv::Rect_<float> inter = a.rect & b.rect;
return inter.area();
}
static void qsort_descent_inplace(std::vector<Object>& faceobjects, int left, int right)
{
int i = left;
int j = right;
float p = faceobjects[(left + right) / 2].prob;
while (i <= j)
{
while (faceobjects[i].prob > p)
i++;
while (faceobjects[j].prob < p)
j--;
if (i <= j)
{
// swap
std::swap(faceobjects[i], faceobjects[j]);
i++;
j--;
}
}
// #pragma omp parallel sections
{
// #pragma omp section
{
if (left < j) qsort_descent_inplace(faceobjects, left, j);
}
// #pragma omp section
{
if (i < right) qsort_descent_inplace(faceobjects, i, right);
}
}
}
static void qsort_descent_inplace(std::vector<Object>& faceobjects)
{
if (faceobjects.empty())
return;
qsort_descent_inplace(faceobjects, 0, faceobjects.size() - 1);
}
static void nms_sorted_bboxes(const std::vector<Object>& faceobjects, std::vector<int>& picked, float nms_threshold)
{
picked.clear();
const int n = faceobjects.size();
std::vector<float> areas(n);
for (int i = 0; i < n; i++)
{
areas[i] = faceobjects[i].rect.width * faceobjects[i].rect.height;
}
for (int i = 0; i < n; i++)
{
const Object& a = faceobjects[i];
int keep = 1;
for (int j = 0; j < (int)picked.size(); j++)
{
const Object& b = faceobjects[picked[j]];
// intersection over union
float inter_area = intersection_area(a, b);
float union_area = areas[i] + areas[picked[j]] - inter_area;
// float IoU = inter_area / union_area
if (inter_area / union_area > nms_threshold)
keep = 0;
}
if (keep)
picked.push_back(i);
}
}
static void generate_grids_and_stride(const int target_w, const int target_h, std::vector<int>& strides, std::vector<GridAndStride>& grid_strides)
{
for (int i = 0; i < (int)strides.size(); i++)
{
int stride = strides[i];
int num_grid_w = target_w / stride;
int num_grid_h = target_h / stride;
for (int g1 = 0; g1 < num_grid_h; g1++)
{
for (int g0 = 0; g0 < num_grid_w; g0++)
{
GridAndStride gs;
gs.grid0 = g0;
gs.grid1 = g1;
gs.stride = stride;
grid_strides.push_back(gs);
}
}
}
}
static void generate_proposals(std::vector<GridAndStride> grid_strides, const ncnn::Mat& pred, float prob_threshold, std::vector<Object>& objects)
{
const int num_points = grid_strides.size();
const int num_class = 80;
const int reg_max_1 = 16;
for (int i = 0; i < num_points; i++)
{
const float* scores = pred.row(i) + 4 * reg_max_1;
// find label with max score
int label = -1;
float score = -FLT_MAX;
for (int k = 0; k < num_class; k++)
{
float confidence = scores[k];
if (confidence > score)
{
label = k;
score = confidence;
}
}
float box_prob = sigmoid(score);
if (box_prob >= prob_threshold)
{
ncnn::Mat bbox_pred(reg_max_1, 4, (void*)pred.row(i));
{
ncnn::Layer* softmax = ncnn::create_layer("Softmax");
ncnn::ParamDict pd;
pd.set(0, 1); // axis
pd.set(1, 1);
softmax->load_param(pd);
ncnn::Option opt;
opt.num_threads = 1;
opt.use_packing_layout = false;
softmax->create_pipeline(opt);
softmax->forward_inplace(bbox_pred, opt);
softmax->destroy_pipeline(opt);
delete softmax;
}
float pred_ltrb[4];
for (int k = 0; k < 4; k++)
{
float dis = 0.f;
const float* dis_after_sm = bbox_pred.row(k);
for (int l = 0; l < reg_max_1; l++)
{
dis += l * dis_after_sm[l];
}
pred_ltrb[k] = dis * grid_strides[i].stride;
}
float pb_cx = (grid_strides[i].grid0 + 0.5f) * grid_strides[i].stride;
float pb_cy = (grid_strides[i].grid1 + 0.5f) * grid_strides[i].stride;
float x0 = pb_cx - pred_ltrb[0];
float y0 = pb_cy - pred_ltrb[1];
float x1 = pb_cx + pred_ltrb[2];
float y1 = pb_cy + pred_ltrb[3];
Object obj;
obj.rect.x = x0;
obj.rect.y = y0;
obj.rect.width = x1 - x0;
obj.rect.height = y1 - y0;
obj.label = label;
obj.prob = box_prob;
objects.push_back(obj);
}
std::cout<<"*********good*********"<<std::endl;
}
printf("return is ok");
}
YoloV8::YoloV8()
{
}
int YoloV8::load(int _target_size)
{
yolo.clear();
yolo.opt = ncnn::Option();
yolo.opt.num_threads = 4;
yolo.load_param("/home/wangshuai/WS/new_kl/kl_ncnn_yolov5/model/cuo-64-1.param");
yolo.load_model("/home/wangshuai/WS/new_kl/kl_ncnn_yolov5/model/cuo-64-1.bin");
std::cout<<"load_models"<<std::endl;
target_size = _target_size;
mean_vals[0] = 103.53f;
mean_vals[1] = 116.28f;
mean_vals[2] = 123.675f;
norm_vals[0] = 1.0 / 255.0f;
norm_vals[1] = 1.0 / 255.0f;
norm_vals[2] = 1.0 / 255.0f;
return 0;
}
int YoloV8::detect(const cv::Mat& rgb, std::vector<Object>& objects, float prob_threshold, float nms_threshold)
{
int width = rgb.cols;
int height = rgb.rows;
// pad to multiple of 32
int w = width;
int h = height;
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(rgb.data, ncnn::Mat::PIXEL_RGB2BGR, width, height, w, h);
// pad to target_size rectangle
int wpad = (w + 31) / 32 * 32 - w;
int hpad = (h + 31) / 32 * 32 - h;
ncnn::Mat in_pad;
ncnn::copy_make_border(in, in_pad, hpad / 2, hpad - hpad / 2, wpad / 2, wpad - wpad / 2, ncnn::BORDER_CONSTANT, 0.f);
in_pad.substract_mean_normalize(0, norm_vals);
ncnn::Extractor ex = yolo.create_extractor();
ex.input("images", in_pad);
std::vector<Object> proposals;
ncnn::Mat out;
ex.extract("output0", out);
std::vector<int> strides = {8, 16, 32}; // might have stride=64
std::vector<GridAndStride> grid_strides;
generate_grids_and_stride(in_pad.w, in_pad.h, strides, grid_strides);
std::cout<<"*********5*********"<<std::endl;
generate_proposals(grid_strides, out, prob_threshold, proposals);
std::cout<<"*********4*********"<<std::endl;
// sort all proposals by score from highest to lowest
qsort_descent_inplace(proposals);
// apply nms with nms_threshold
std::vector<int> picked;
nms_sorted_bboxes(proposals, picked, nms_threshold);
std::cout<<"*********6*********"<<std::endl;
int count = picked.size();
objects.resize(count);
for (int i = 0; i < count; i++)
{
objects[i] = proposals[picked[i]];
// adjust offset to original unpadded
float x0 = (objects[i].rect.x - (wpad / 2)) / scale;
float y0 = (objects[i].rect.y - (hpad / 2)) / scale;
float x1 = (objects[i].rect.x + objects[i].rect.width - (wpad / 2)) / scale;
float y1 = (objects[i].rect.y + objects[i].rect.height - (hpad / 2)) / scale;
// clip
x0 = std::max(std::min(x0, (float)(width - 1)), 0.f);
y0 = std::max(std::min(y0, (float)(height - 1)), 0.f);
x1 = std::max(std::min(x1, (float)(width - 1)), 0.f);
y1 = std::max(std::min(y1, (float)(height - 1)), 0.f);
objects[i].rect.x = x0;
objects[i].rect.y = y0;
objects[i].rect.width = x1 - x0;
objects[i].rect.height = y1 - y0;
}
std::cout<<"*********7*********"<<std::endl;
// sort objects by area
struct
{
bool operator()(const Object& a, const Object& b) const
{
return a.rect.area() > b.rect.area();
}
} objects_area_greater;
std::sort(objects.begin(), objects.end(), objects_area_greater);
return 0;
}
int YoloV8::draw(cv::Mat& rgb, const std::vector<Object>& objects)
{
for (size_t i = 0; i < objects.size(); i++)
{
const Object& obj = objects[i];
// fprintf(stderr, "%d = %.5f at %.2f %.2f %.2f x %.2f\n", obj.label, obj.prob,
// obj.rect.x, obj.rect.y, obj.rect.width, obj.rect.height);
cv::rectangle(rgb, obj.rect, cv::Scalar(255, 0, 0));
char text[256];
sprintf(text, "%s %.1f%%", class_names[obj.label], obj.prob * 100);
int baseLine = 0;
cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
int x = obj.rect.x;
int y = obj.rect.y - label_size.height - baseLine;
if (y < 0)
y = 0;
if (x + label_size.width > rgb.cols)
x = rgb.cols - label_size.width;
cv::rectangle(rgb, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)),
cv::Scalar(255, 255, 255), -1);
cv::putText(rgb, text, cv::Point(x, y + label_size.height),
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));
}
return 0;
}
YoloV8 yolov8;
int target_size = 320; //416; //320; must be divisible by 32.
int main(int argc, char** argv)
{
const char* imagepath = argv[1];
if (argc != 2)
{
fprintf(stderr, "Usage: %s [imagepath]\n", argv[0]);
return -1;
}
cv::Mat m = cv::imread(imagepath, 1);
if (m.empty())
{
fprintf(stderr, "cv::imread %s failed\n", imagepath);
return -1;
}
yolov8.load(target_size); //load model (once) see yoloyV8.cpp line 246
std::vector<Object> objects;
yolov8.detect(m, objects); //recognize the objects
yolov8.draw(m, objects); //show the outcome
// cv::imshow("RPi4 - 1.95 GHz - 2 GB ram",m);
cv::imwrite("/home/wangshuai/WS/new_kl/kl_ncnn_yolov5/img_dir/v8/bus1.jpg",m);
cv::imshow("image",m);
cv::waitKey(0);
//cv::imwrite("/home/wangshuai/WS/new_kl/kl_ncnn_yolov5/img_dir/v8/bus.jpg",m);
// cv::waitKey(0);
return 0;
}
2.编译推理
cd ncnn_dir
mkdir build & cd build
cmake ..
make
./examples/yolov8 ../img_dir/test/cuoceng.jpg
3.推理结果
git clone https://github.com/protocolbuffers/protobuf.git
cd protobuf/
git submodule update --init --recursive
./autogen.sh
./configure -prefix=/usr/local/
sudo make #要编译很久
sudo make check
sudo make install
sudo vim /etc/profile
export PATH=$PATH:/usr/local/bin/
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig/
source /etc/profile
sudo vim /etc/ld.so.conf
/usr/local/protobuf/lib
ldconfig
protoc --version
WangShuai771216/yolov8-ncnn (github.com)
1.模型训练时,data.yaml修改数据集路径,类别数80不做修改,只将前面的类别名改为自己的类别名即可;
2.yolov8,ncnn,opset,pytorch等不同版本存在差异,需要选对版本,可尝试更改版本来解决问题,yolov8为8.2,ncnn为20220729,export时的opset为12,pytorch为1.10;
3.pt转onnx时,需要修改源码的head.py和block.py,修改模型网络结构和输出节点;
4.模型量化时,参数mean,norm必须保持和推理时一致,否则精度会下降;
5.protobuf安装后,ncnn的量化工具才能编译
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。