版本聲明
yolov5-seg:官方地址:https://github.com/ultralytics/yolov5/tree/v6.2
TensorRT:8.x.x
語言:C++
系統(tǒng):ubuntu18.04
一、數(shù)據(jù)集制作:圖像 Json轉(zhuǎn)txt
前言:由于yolo倉中提供了標(biāo)準(zhǔn)coco的json文件轉(zhuǎn)txt代碼,因此需要將labelme的json文件轉(zhuǎn)為coco json.
-
labelme JSON 轉(zhuǎn)COCO JSON
使用labelme的CreatePolygons按鈕開始繪制多邊形,然后保存為json格式。
https://github.com/wkentaro/labelme/tree/master/examples/instance_segmentation.
在該鏈接中有個labelme2coco.py腳本,將該腳本下載下來后,執(zhí)行以下指令即可。其中data_annotated是剛剛標(biāo)注保存的json標(biāo)簽文件夾,data_dataset_coco是生成MS COCO數(shù)據(jù)類型的目錄。
python labelme2coco.py data_annotated data_dataset_coco --labels label.txt
注意:由于自定義的數(shù)據(jù)集里面標(biāo)簽從0開始 不包括背景 直接轉(zhuǎn)換會報錯。修改72行。
生成三個文件JPEGImages、 Visualization 、annotations.json
JPEGImages中為原圖,annotations.json里面是coco格式的文件:
Visualization中的圖如下:
轉(zhuǎn)換前需要自定義label.txt
-
COCO JSON轉(zhuǎn)txt
coco128-seg提供了標(biāo)準(zhǔn)的訓(xùn)練格式,我們下載下來看看。[label]+[points]
下載鏈接link:https://github.com/ultralytics/JSON2YOLO
找到general_json2yolo.py文件,修改路徑后直接運(yùn)行會報錯:
No such file or directory xxx/xxxxx/xxx.txt
排查過后發(fā)現(xiàn)是我們生成的annotations.json和標(biāo)準(zhǔn)的coco json有出入:(多了JPEGImages/),修改代碼313行:
標(biāo)準(zhǔn)的:
我們的:
再次運(yùn)行,報下一個錯誤:
TypeError: must be real number, not NoneType
錯誤指向:
觀察文件夾中,已經(jīng)生成一個xxx.txt且有部分?jǐn)?shù)據(jù),打印line之后發(fā)現(xiàn)數(shù)據(jù)里有[None,point…point]這樣的數(shù)據(jù)。 大體知道了:應(yīng)該是生成了背景類且沒有標(biāo)簽。修改代碼跳過這些標(biāo)簽:
再次運(yùn)行報錯消失,執(zhí)行完畢沒有報錯。以為成功了打開txt一個最大的標(biāo)簽僅僅為13,應(yīng)該是到15(我的數(shù)據(jù)集一共十六類),中間有幾類被消除了,排查錯誤。應(yīng)該是這個地方把91–>80類的函數(shù)的問題。修改一番,兩個地方。(若只修改第二處 會出現(xiàn)-1標(biāo)簽,最高到14)
也可以只修改第二處:再修改代碼:
下面展示一些 內(nèi)聯(lián)代碼片
。
cls = coco80[ann['category_id'] - 1] if cls91to80 else ann['category_id'] - 1 # class
cls = coco80[ann['category_id']] if cls91to80 else ann['category_id'] - 1 # class
coco91_to_coco80_class()函數(shù):
排除完畢以上錯誤時,再次運(yùn)行,沒有錯誤了。
import contextlib
import json
import cv2
import pandas as pd
from PIL import Image
from collections import defaultdict
from utils import *
# Convert INFOLKS JSON file into YOLO-format labels ----------------------------
def convert_infolks_json(name, files, img_path):
# Create folders
path = make_dirs()
# Import json
data = []
for file in glob.glob(files):
with open(file) as f:
jdata = json.load(f)
jdata['json_file'] = file
data.append(jdata)
# Write images and shapes
name = path + os.sep + name
file_id, file_name, wh, cat = [], [], [], []
for x in tqdm(data, desc='Files and Shapes'):
f = glob.glob(img_path + Path(x['json_file']).stem + '.*')[0]
file_name.append(f)
wh.append(exif_size(Image.open(f))) # (width, height)
cat.extend(a['classTitle'].lower() for a in x['output']['objects']) # categories
# filename
with open(name + '.txt', 'a') as file:
file.write('%s\n' % f)
# Write *.names file
names = sorted(np.unique(cat))
# names.pop(names.index('Missing product')) # remove
with open(name + '.names', 'a') as file:
[file.write('%s\n' % a) for a in names]
# Write labels file
for i, x in enumerate(tqdm(data, desc='Annotations')):
label_name = Path(file_name[i]).stem + '.txt'
with open(path + '/labels/' + label_name, 'a') as file:
for a in x['output']['objects']:
# if a['classTitle'] == 'Missing product':
# continue # skip
category_id = names.index(a['classTitle'].lower())
# The INFOLKS bounding box format is [x-min, y-min, x-max, y-max]
box = np.array(a['points']['exterior'], dtype=np.float32).ravel()
box[[0, 2]] /= wh[i][0] # normalize x by width
box[[1, 3]] /= wh[i][1] # normalize y by height
box = [box[[0, 2]].mean(), box[[1, 3]].mean(), box[2] - box[0], box[3] - box[1]] # xywh
if (box[2] > 0.) and (box[3] > 0.): # if w > 0 and h > 0
file.write('%g %.6f %.6f %.6f %.6f\n' % (category_id, *box))
# Split data into train, test, and validate files
split_files(name, file_name)
write_data_data(name + '.data', nc=len(names))
print(f'Done. Output saved to {os.getcwd() + os.sep + path}')
# Convert vott JSON file into YOLO-format labels -------------------------------
def convert_vott_json(name, files, img_path):
# Create folders
path = make_dirs()
name = path + os.sep + name
# Import json
data = []
for file in glob.glob(files):
with open(file) as f:
jdata = json.load(f)
jdata['json_file'] = file
data.append(jdata)
# Get all categories
file_name, wh, cat = [], [], []
for i, x in enumerate(tqdm(data, desc='Files and Shapes')):
with contextlib.suppress(Exception):
cat.extend(a['tags'][0] for a in x['regions']) # categories
# Write *.names file
names = sorted(pd.unique(cat))
with open(name + '.names', 'a') as file:
[file.write('%s\n' % a) for a in names]
# Write labels file
n1, n2 = 0, 0
missing_images = []
for i, x in enumerate(tqdm(data, desc='Annotations')):
f = glob.glob(img_path + x['asset']['name'] + '.jpg')
if len(f):
f = f[0]
file_name.append(f)
wh = exif_size(Image.open(f)) # (width, height)
n1 += 1
if (len(f) > 0) and (wh[0] > 0) and (wh[1] > 0):
n2 += 1
# append filename to list
with open(name + '.txt', 'a') as file:
file.write('%s\n' % f)
# write labelsfile
label_name = Path(f).stem + '.txt'
with open(path + '/labels/' + label_name, 'a') as file:
for a in x['regions']:
category_id = names.index(a['tags'][0])
# The INFOLKS bounding box format is [x-min, y-min, x-max, y-max]
box = a['boundingBox']
box = np.array([box['left'], box['top'], box['width'], box['height']]).ravel()
box[[0, 2]] /= wh[0] # normalize x by width
box[[1, 3]] /= wh[1] # normalize y by height
box = [box[0] + box[2] / 2, box[1] + box[3] / 2, box[2], box[3]] # xywh
if (box[2] > 0.) and (box[3] > 0.): # if w > 0 and h > 0
file.write('%g %.6f %.6f %.6f %.6f\n' % (category_id, *box))
else:
missing_images.append(x['asset']['name'])
print('Attempted %g json imports, found %g images, imported %g annotations successfully' % (i, n1, n2))
if len(missing_images):
print('WARNING, missing images:', missing_images)
# Split data into train, test, and validate files
split_files(name, file_name)
print(f'Done. Output saved to {os.getcwd() + os.sep + path}')
# Convert ath JSON file into YOLO-format labels --------------------------------
def convert_ath_json(json_dir): # dir contains json annotations and images
# Create folders
dir = make_dirs() # output directory
jsons = []
for dirpath, dirnames, filenames in os.walk(json_dir):
jsons.extend(
os.path.join(dirpath, filename)
for filename in [
f for f in filenames if f.lower().endswith('.json')
]
)
# Import json
n1, n2, n3 = 0, 0, 0
missing_images, file_name = [], []
for json_file in sorted(jsons):
with open(json_file) as f:
data = json.load(f)
# # Get classes
# try:
# classes = list(data['_via_attributes']['region']['class']['options'].values()) # classes
# except:
# classes = list(data['_via_attributes']['region']['Class']['options'].values()) # classes
# # Write *.names file
# names = pd.unique(classes) # preserves sort order
# with open(dir + 'data.names', 'w') as f:
# [f.write('%s\n' % a) for a in names]
# Write labels file
for x in tqdm(data['_via_img_metadata'].values(), desc=f'Processing {json_file}'):
image_file = str(Path(json_file).parent / x['filename'])
f = glob.glob(image_file) # image file
if len(f):
f = f[0]
file_name.append(f)
wh = exif_size(Image.open(f)) # (width, height)
n1 += 1 # all images
if len(f) > 0 and wh[0] > 0 and wh[1] > 0:
label_file = dir + 'labels/' + Path(f).stem + '.txt'
nlabels = 0
try:
with open(label_file, 'a') as file: # write labelsfile
# try:
# category_id = int(a['region_attributes']['class'])
# except:
# category_id = int(a['region_attributes']['Class'])
category_id = 0 # single-class
for a in x['regions']:
# bounding box format is [x-min, y-min, x-max, y-max]
box = a['shape_attributes']
box = np.array([box['x'], box['y'], box['width'], box['height']],
dtype=np.float32).ravel()
box[[0, 2]] /= wh[0] # normalize x by width
box[[1, 3]] /= wh[1] # normalize y by height
box = [box[0] + box[2] / 2, box[1] + box[3] / 2, box[2],
box[3]] # xywh (left-top to center x-y)
if box[2] > 0. and box[3] > 0.: # if w > 0 and h > 0
file.write('%g %.6f %.6f %.6f %.6f\n' % (category_id, *box))
n3 += 1
nlabels += 1
if nlabels == 0: # remove non-labelled images from dataset
os.system(f'rm {label_file}')
# print('no labels for %s' % f)
continue # next file
# write image
img_size = 4096 # resize to maximum
img = cv2.imread(f) # BGR
assert img is not None, 'Image Not Found ' + f
r = img_size / max(img.shape) # size ratio
if r < 1: # downsize if necessary
h, w, _ = img.shape
img = cv2.resize(img, (int(w * r), int(h * r)), interpolation=cv2.INTER_AREA)
ifile = dir + 'images/' + Path(f).name
if cv2.imwrite(ifile, img): # if success append image to list
with open(dir + 'data.txt', 'a') as file:
file.write('%s\n' % ifile)
n2 += 1 # correct images
except Exception:
os.system(f'rm {label_file}')
print(f'problem with {f}')
else:
missing_images.append(image_file)
nm = len(missing_images) # number missing
print('\nFound %g JSONs with %g labels over %g images. Found %g images, labelled %g images successfully' %
(len(jsons), n3, n1, n1 - nm, n2))
if len(missing_images):
print('WARNING, missing images:', missing_images)
# Write *.names file
names = ['knife'] # preserves sort order
with open(dir + 'data.names', 'w') as f:
[f.write('%s\n' % a) for a in names]
# Split data into train, test, and validate files
split_rows_simple(dir + 'data.txt')
write_data_data(dir + 'data.data', nc=1)
print(f'Done. Output saved to {Path(dir).absolute()}')
def convert_coco_json(json_dir='../coco/annotations/', use_segments=False, cls91to80=False):
save_dir = make_dirs() # output directory
coco80 = coco91_to_coco80_class()
# Import json
for json_file in sorted(Path(json_dir).resolve().glob('*.json')):
fn = Path(save_dir) / 'labels' / json_file.stem.replace('instances_', '') # folder name
fn.mkdir()
with open(json_file) as f:
data = json.load(f)
print(data)
# Create image dict
images = {'%g' % x['id']: x for x in data['images']}
# Create image-annotations dict
imgToAnns = defaultdict(list)
for ann in data['annotations']:
imgToAnns[ann['image_id']].append(ann)
# Write labels file
for img_id, anns in tqdm(imgToAnns.items(), desc=f'Annotations {json_file}'):
img = images['%g' % img_id]
h, w, f = img['height'], img['width'], img['file_name']
bboxes = []
segments = []
for ann in anns:
if ann['iscrowd']:
continue
# The COCO box format is [top left x, top left y, width, height]
box = np.array(ann['bbox'], dtype=np.float64)
box[:2] += box[2:] / 2 # xy top-left corner to center
box[[0, 2]] /= w # normalize x
box[[1, 3]] /= h # normalize y
if box[2] <= 0 or box[3] <= 0: # if w <= 0 and h <= 0
continue
#cls = coco80[ann['category_id'] - 1] if cls91to80 else ann['category_id'] - 1 # class
'''這個地方把91類別轉(zhuǎn)80類別關(guān)了,因?yàn)樽约旱慕⒌臄?shù)據(jù)集不需要轉(zhuǎn)變'''
'''直接將cls=category_id'''
cls = ann['category_id']
box = [cls] + box.tolist()
if box not in bboxes:
bboxes.append(box)
# Segments
if use_segments:
if len(ann['segmentation']) > 1:
s = merge_multi_segment(ann['segmentation'])
s = (np.concatenate(s, axis=0) / np.array([w, h])).reshape(-1).tolist()
else:
s = [j for i in ann['segmentation'] for j in i] # all segments concatenated
s = (np.array(s).reshape(-1, 2) / np.array([w, h])).reshape(-1).tolist()
s = [cls] + s
if s not in segments:
segments.append(s)
# Write
print("fn/f==>",fn/f[11:])
print("fn==>",fn)
print("f==>",f)
with open((fn / f[11:]).with_suffix('.txt'), 'a') as file:
print(len(bboxes))
for i in range(len(bboxes)):
print("seg:",segments)
line = *(segments[i] if use_segments else bboxes[i]), # cls, box or segments
print("line:==>",line)
if(line[0]==None):
continue
file.write(('%g ' * len(line)).rstrip() % line + '\n')
def min_index(arr1, arr2):
"""Find a pair of indexes with the shortest distance.
Args:
arr1: (N, 2).
arr2: (M, 2).
Return:
a pair of indexes(tuple).
"""
dis = ((arr1[:, None, :] - arr2[None, :, :]) ** 2).sum(-1)
return np.unravel_index(np.argmin(dis, axis=None), dis.shape)
def merge_multi_segment(segments):
"""Merge multi segments to one list.
Find the coordinates with min distance between each segment,
then connect these coordinates with one thin line to merge all
segments into one.
Args:
segments(List(List)): original segmentations in coco's json file.
like [segmentation1, segmentation2,...],
each segmentation is a list of coordinates.
"""
s = []
segments = [np.array(i).reshape(-1, 2) for i in segments]
idx_list = [[] for _ in range(len(segments))]
# record the indexes with min distance between each segment
for i in range(1, len(segments)):
idx1, idx2 = min_index(segments[i - 1], segments[i])
idx_list[i - 1].append(idx1)
idx_list[i].append(idx2)
# use two round to connect all the segments
for k in range(2):
# forward connection
if k == 0:
for i, idx in enumerate(idx_list):
# middle segments have two indexes
# reverse the index of middle segments
if len(idx) == 2 and idx[0] > idx[1]:
idx = idx[::-1]
segments[i] = segments[i][::-1, :]
segments[i] = np.roll(segments[i], -idx[0], axis=0)
segments[i] = np.concatenate([segments[i], segments[i][:1]])
# deal with the first segment and the last one
if i in [0, len(idx_list) - 1]:
s.append(segments[i])
else:
idx = [0, idx[1] - idx[0]]
s.append(segments[i][idx[0]:idx[1] + 1])
else:
for i in range(len(idx_list) - 1, -1, -1):
if i not in [0, len(idx_list) - 1]:
idx = idx_list[i]
nidx = abs(idx[1] - idx[0])
s.append(segments[i][nidx:])
return s
def delete_dsstore(path='../datasets'):
# Delete apple .DS_store files
from pathlib import Path
files = list(Path(path).rglob('.DS_store'))
print(files)
for f in files:
f.unlink()
if __name__ == '__main__':
source = 'COCO'
if source == 'COCO':
convert_coco_json('寫自己的路徑', # directory with *.json
use_segments=True,
cls91to80=False)
elif source == 'infolks': # Infolks https://infolks.info/
convert_infolks_json(name='out',
files='../data/sm4/json/*.json',
img_path='../data/sm4/images/')
elif source == 'vott': # VoTT https://github.com/microsoft/VoTT
convert_vott_json(name='data',
files='../../Downloads/athena_day/20190715/*.json',
img_path='../../Downloads/athena_day/20190715/') # images folder
elif source == 'ath': # ath format
convert_ath_json(json_dir='../../Downloads/athena/') # images folder
# zip results
# os.system('zip -r ../coco.zip ../coco')
二、分割模型訓(xùn)練
訓(xùn)練的步驟和目標(biāo)檢測模型一致,下載模型 yolov5s-seg.pt,劃分?jǐn)?shù)據(jù)集 、修改配置文件、不再詳述了。
三 tensorRT部署
1 模型導(dǎo)出
使用官方的export.py文件直接導(dǎo)出時,netron可視化之后如下:
onnx比較混亂,需要進(jìn)一步修改,所有的修改如下,參考杜老的倉link:https://github.com/shouxieai/learning-cuda-trt/tree/main:
# line 55 forward function in yolov5/models/yolo.py
# bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85)
# x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()
# modified into:
bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85)
bs = -1
ny = int(ny)
nx = int(nx)
x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()
# line 70 in yolov5/models/yolo.py
# z.append(y.view(bs, -1, self.no))
# modified into:
z.append(y.view(bs, self.na * ny * nx, self.no))
############# for yolov5-6.0 #####################
# line 65 in yolov5/models/yolo.py
# if self.grid[i].shape[2:4] != x[i].shape[2:4] or self.onnx_dynamic:
# self.grid[i], self.anchor_grid[i] = self._make_grid(nx, ny, i)
# modified into:
if self.grid[i].shape[2:4] != x[i].shape[2:4] or self.onnx_dynamic:
self.grid[i], self.anchor_grid[i] = self._make_grid(nx, ny, i)
# disconnect for pytorch trace
anchor_grid = (self.anchors[i].clone() * self.stride[i]).view(1, -1, 1, 1, 2)
# line 70 in yolov5/models/yolo.py
# y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh
# modified into:
y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * anchor_grid # wh
# line 73 in yolov5/models/yolo.py
# wh = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh
# modified into:
wh = (y[..., 2:4] * 2) ** 2 * anchor_grid # wh
############# for yolov5-6.0 #####################
# line 77 in yolov5/models/yolo.py
# return x if self.training else (torch.cat(z, 1), x)
# modified into:
return x if self.training else torch.cat(z, 1)
# line 52 in yolov5/export.py
# torch.onnx.export(dynamic_axes={'images': {0: 'batch', 2: 'height', 3: 'width'}, # shape(1,3,640,640)
# 'output': {0: 'batch', 1: 'anchors'} # shape(1,25200,85) 修改為
# modified into:
torch.onnx.export(dynamic_axes={'images': {0: 'batch'}, # shape(1,3,640,640)
'output': {0: 'batch'} # shape(1,25200,85)
由于版本不同修改的地方也稍有改變
修改后:
導(dǎo)出指令:python export.py --weights runs/train-seg/exp3/weights/best.pt --include onnx --dynamic
2 onnx轉(zhuǎn)trtmodel
TRT::compile(
mode, // FP32、FP16、INT8
test_batch_size, // max batch size
onnx_file, // source
model_file, // save to
{},
int8process,
"inference"
);
文章來源:http://www.zghlxwxcb.cn/news/detail-459152.html
3 推理部分
static void inference(Type type, TRT::Mode mode, const string& model_file){
auto engine = TRT::load_infer(model_file);
if(engine == nullptr){
INFOE("Engine is nullptr");
return;
}
auto image = cv::imread("xxx.jpg");
//繪制結(jié)果
int col=image.cols; //1920
int row=image.rows; //1080
Mat mask_seg=image.clone();
Mat mask_box=image.clone();//3 channel
Mat cut_img=image.clone();
auto input = engine->tensor("images"); // engine->input(0);
auto output = engine->tensor("output0"); // engine->output(1);//[batch , 32130 , 53]
auto output1 = engine->tensor("output1"); // (batch, 32, 136, 240) ==>(16,32,136,240)
int num_bboxes = output->size(1);//32130
int num_classes = output->size(2) - 5 ;
float confidence_threshold = 0.5;
float nms_threshold = 0.45;
int MAX_IMAGE_BBOX = 1000;
int NUM_BOX_ELEMENT = 39; // left, top, right, bottom, confidence, class, keepflag ,32 mask
int netWidth = 640;
int netHeigh = 640;
int segWidth = 160;
int segHeight = 160;
float mask_thresh = 0.2;
TRT::Tensor output_array_device(TRT::DataType::Float);
// use max = 1 batch to inference.
int max_batch_size = 1;
input->resize_single_dim(0, max_batch_size).to_gpu();
output_array_device.resize(max_batch_size, 1 + MAX_IMAGE_BBOX * NUM_BOX_ELEMENT).to_gpu();
output_array_device.set_stream(engine->get_stream());
// set batch = 1 image
int ibatch = 0;
image_to_tensor(image, input, type, ibatch);
// do async 異步
engine->forward(false);
float* output_ptr = output1->cpu<float>();
//vector 2 mat
int size[]={32,segHeight,segWidth};
//cout<<"size"<<size[0]<<endl;
cv::Mat mask_protos = cv::Mat_<float>(3,size,CV_8UC1);
for(int iii=0;iii<32;iii++)
{
//unchar *data=mask_protos.ptr<unchar>(iii);
for(int jjj=0;jjj<segHeight;jjj++)
{
//unchar *data2=data.ptr<unchar>(jjj);
for(int kkk=0;kkk<segWidth;kkk++)
{
//data2[kkk]=output_ptr[iii*136*240+jjj*240+kkk];
mask_protos.at<float>(iii,jjj,kkk)=output_ptr[iii*segHeight*segWidth+jjj*segWidth+kkk];
}
}
}
float* d2i_affine_matrix = static_cast<float*>(input->get_workspace()->gpu());
Yolo::decode_kernel_invoker(
output->gpu<float>(ibatch),
num_bboxes, num_classes,
confidence_threshold,
d2i_affine_matrix, output_array_device.gpu<float>(ibatch),
MAX_IMAGE_BBOX, engine->get_stream()
);
Yolo::nms_kernel_invoker(
output_array_device.gpu<float>(ibatch),
nms_threshold,
MAX_IMAGE_BBOX, engine->get_stream()
);
float* parray = output_array_device.cpu<float>();
int num_box = min(static_cast<int>(*parray), MAX_IMAGE_BBOX);//取最小值
//new a mat and new a vector
Mat mask_proposals;
vector<OutputSeg> f_output;
vector<vector<float>>proposal; //[23,32] output0 =>mask
int num_box1=0;
Rect holeImgRect(0, 0, col, row);
for(int i = 0; i < num_box; ++i){ //遍歷所有的框
float* pbox = parray + 1 + i * NUM_BOX_ELEMENT;//+1+i*7 1:表示這個數(shù)組的元素?cái)?shù)量
int keepflag = pbox[6];
vector<float> temp;
OutputSeg result;
if(keepflag == 1 ){
num_box1+=1;
// left, top, right, bottom, confidence,class, keepflag
// pbox[0], pbox[1], pbox[2], pbox[3], pbox[4], pbox[5], pbox[6]
float left = pbox[0];
float top = pbox[1];
float right = pbox[2];
float bottom = pbox[3];
float confidence = pbox[4];
for(int ii=0;ii<32;ii++)
{
temp.push_back(pbox[ii+7]);
}
proposal.push_back(temp);
result.id=pbox[5];
result.confidence=pbox[4];
cv::Rect rect(left, top, right-left, bottom-top);
result.box=rect & holeImgRect;//; //x,y,w,h
f_output.push_back(result);
int label = static_cast<int>(pbox[5]);
uint8_t b, g, r;
tie(b, g, r) = iLogger::random_color(label);
cv::rectangle(image, cv::Point(left, top), cv::Point(right, bottom), cv::Scalar(b, g, r), 3);
auto name = cocolabels[label];
auto caption = iLogger::format("%s %.2f", name, confidence);
int width = cv::getTextSize(caption, 0, 1, 1, nullptr).width + 10;
cv::rectangle(image, cv::Point(left-3, top-33), cv::Point(left + width, top), cv::Scalar(b, g, r), -1);
cv::putText(image, caption, cv::Point(left, top-5), 0, 1, cv::Scalar::all(0), 2, 16);
}
//對應(yīng)于python中的process_mask
//vector2mat
for (int i = 0; i < proposal.size(); ++i)
{mask_proposals.push_back(Mat(proposal[i]).t());}
/獲取 proto 也就是output1的輸出
//邏輯 GetMask
Vec4d params; //根據(jù)實(shí)際圖片輸入 和 onnx模型輸入輸出 計(jì)算的,此處直接寫死
params[0]=0.5;
params[1]=0.5;
params[2]=0.0;
params[3]=2.0;
Mat protos = mask_protos.reshape(0, {32,136 * 240});
Mat matmulRes = ( mask_proposals * protos).t(); //23,32 * 32,32640 ==> 23,32640
Mat masks = matmulRes.reshape(proposal.size(),{136,240}); //上一步驟作轉(zhuǎn)置的原因://Mat Mat::reshape(int cn,int rows=0) const cn:表示通道數(shù)(channels),如果設(shè)置為0,則表示通道不變;
vector<Mat> maskChannels; //分離通道
split(masks, maskChannels);
for (int index = 0; index < f_output.size(); ++index) {
Mat dest,mask;
//sigmoid
cv::exp(-maskChannels[index],dest);//e^x
dest= 1.0/(1.0 + dest);
//_netWidth = 960; _netHeight=544; //ONNX圖片輸入寬度\高度 // const int _segWidth = 240;
Rect roi(int(params[2] / netWidth * segWidth), int(params[3] / netHeigh * segHeight), int(segWidth - params[2] / 2), int(segHeight- 0/2)); //136-params[3]/2最后一個參數(shù)改了 mask會有偏移
dest = dest(roi);
resize(dest, mask, cv::Size(col,row), INTER_LINEAR);//srcImgShape (1920,1080)//INTER_NEAREST 最近臨插值 PYTHON中用的就是 INTER_LINEAR - 雙線性插值
//crop
Rect temp_rect = f_output[index].box;
mask = mask(temp_rect) > mask_thresh; //mask_threshg mask閾值
f_output[index].boxMask =mask;
}
//DrawPred 繪制結(jié)果
for (int i=0;i<f_output.size();i++)
{
int lf, tp,wd,hg;
float confidence;
lf=f_output[i].box.x;
tp=f_output[i].box.y;
wd=f_output[i].box.width;
hg=f_output[i].box.height;
confidence=f_output[i].confidence;
int label = static_cast<int>(f_output[i].id);
//生成隨機(jī)顏色
uint8_t b, g, r;
tie(b, g, r) = iLogger::random_color(label);
cv::rectangle(mask_box, cv::Point(lf, tp), cv::Point(lf+wd, tp+hg), cv::Scalar(b, g, r), 3);//繪制box框
auto name = cocolabels[label];
auto caption = iLogger::format("%s %.2f", name, confidence);
int width = cv::getTextSize(caption, 0, 1, 1, nullptr).width + 10;
cv::rectangle(mask_box, cv::Point(lf-3, tp-33), cv::Point(lf + width, tp), cv::Scalar(b, g, r), -1);//繪制label的框
cv::putText(mask_box, caption, cv::Point(lf, tp-5), 0, 1, cv::Scalar::all(0), 2, 16);
mask_seg(f_output[i].box).setTo(cv::Scalar(b, g, r), f_output[i].boxMask);//繪制mask
}
addWeighted(mask_box, 0.6, mask_seg, 0.4, 0, mask_box); //將mask加在原圖上面
}
效果展示:文章來源地址http://www.zghlxwxcb.cn/news/detail-459152.html
到了這里,關(guān)于【深度學(xué)習(xí)】YOLOv5實(shí)例分割 數(shù)據(jù)集制作、模型訓(xùn)練以及TensorRT部署的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!