yolov8是yolo的最新版本,可做圖像分類,目標(biāo)檢測,實(shí)例分割,姿態(tài)估計(jì)。
主頁地址
這里測試一個(gè)分割模型。
模型如下
選yolov8n-seg模型,轉(zhuǎn)成onnx,再轉(zhuǎn)ncnn測試。
yolov8s-seg的ncnn版可以直接用這個(gè)
如果用python版的onnx,可以直接用notebook轉(zhuǎn),然后下載。
python版onnx代碼參考
但是用notebook版本的onnx轉(zhuǎn)ncnn之后,測試圖片時(shí)會報(bào)錯(cuò)。
可以看一下轉(zhuǎn)出來的yolov8n-seg.param,里面有很多的MemoryData,不夠clean.
所以需要修改一些地方。(參考資料)
查了一些資料都說是要修改modules.py
.
然而本地并沒有modules.py
,而是找到了modules文件夾
那么只需要在這些文件里面找到3個(gè)class并做相同的修改就可以了。
于是需要修改block.py
和head.py
兩個(gè)文件。
要修改3個(gè)forward函數(shù)。
block.py
修改:
class C2f(nn.Module):
...
def forward(self, x):
"""Forward pass through C2f layer."""
#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:, ...]] #onnx不支持chunk操作?
x.extend(m(x[-1]) for m in self.m)
x.pop(1)
return self.cv2(torch.cat(x, 1))
head.py
中修改了Detect和Segment class.
class Detect(nn.Module):
...
def forward(self, x):
"""Concatenates and returns predicted bounding boxes and class probabilities."""
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) #精簡為這2句
return pred
class Segment(Detect):
...
def forward(self, x):
"""Return model outputs and mask coefficients if training, otherwise return outputs and mask coefficients."""
p = self.proto(x[0]) # mask protos
bs = p.shape[0] # batch size
mc = torch.cat([self.cv4[i](x[i]).view(bs, self.nm, -1) for i in range(self.nl)], 2) # mask coefficients
x = self.detect(self, x)
if self.training:
return x, mc, p
#return (torch.cat([x, mc], 1), p) if self.export else (torch.cat([x[0], mc], 1), (x[1], mc, p))
return (torch.cat([x, mc], 1).permute(0, 2, 1), p.view(bs, self.nm, -1)) if self.export else (
torch.cat([x[0], mc], 1), (x[1], mc, p))
改好了之后用如下代碼export onnx文件, onnx文件會導(dǎo)出到和pt文件同一文件夾。
from ultralytics import YOLO
# load model
model = YOLO("pt路徑/yolov8n-seg.pt")
# Export model
success = model.export(format="onnx", opset=12, simplify=True)
轉(zhuǎn)好onnx之后,再把onnx轉(zhuǎn)成ncnn,用onnx2ncnn命令(在build ncnn時(shí)會有這個(gè)命令)
onnx2ncnn一般在ncnn/build/tools/onnx文件夾下。
$ ./onnx2ncnn onnx路徑/yolov8n-seg.onnx 想保存的路徑/yolov8n-seg.param 想保存的路徑/yolov8n-seg.bin
對比一下修改前和修改后導(dǎo)出的參數(shù)yolov8n-seg.param。
可以看到clean了很多,修改前有MemoryData, 而修改之后沒有了。
下面來測試一下ncnn模型,
可以用這個(gè)代碼,
需要修改幾個(gè)地方:
ncnn::Mat out;
//ex.extract("output", out);
ex.extract("output0", out);
ncnn::Mat mask_proto;
//ex.extract("seg", mask_proto);
ex.extract("output1", mask_proto);
再改load_param
和load_model
的路徑。
然后就可以用啦(圖片來自COCO數(shù)據(jù)集)
下面是直接用yolov8n-seg.pt預(yù)測出的結(jié)果,
可以看到修改了之后效果是差了一點(diǎn)的(其中一個(gè)dog的框沒預(yù)測出來)。文章來源:http://www.zghlxwxcb.cn/news/detail-447333.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-447333.html
到了這里,關(guān)于yolov8seg模型轉(zhuǎn)onnx轉(zhuǎn)ncnn的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!