Yolov3源码解析

1. 制作数据集

我做的是三目标检测,人狗马目标检测

然后使用精力标注助手对图像进行目标标注。到出为xml文件。

得到这样格式的数据集

1. 1 读取xml文件,求建议框,向量数据库

import os
from xml.dom.minidom import parse
from kmeans import kmeans, avg_iou
import numpy as np
'''
minidom.parse(filename)                     #加载和读取xml文件
doc.documentElement                         #获取xml文档对象
node.getAttribute(AttributeName)            #获取xml节点属性值
node.getElementsByTagName(TagName)          #获取xml节点对象集合
#.getElementsByTagName(),根据name查找根目录下的子节点
node.childNodes                             #获取子节点列表
node.childNodes[index].nodeValue        #获取xml节点值
node.firstChild                             #访问第一个节点
n.childNodes[0].data                        #获得文本值
node.childNodes[index].nodeValue        #获取XML节点值
doc=minidom.parse(filename)
doc.toxml('utf-8')                          #返回Node节点的xml表示的文本
'''
dirpath=r'D:/AI/study_ai/processed_images/outputs/'#文件地址
file_name_list=os.listdir(dirpath)#获取文件地址下的所有文件名称
# print(file_name)
dataset = []
for file_name in file_name_list:#循环列表中的所有文件名j=file_name.split('.')[0]xml_doc=os.path.join(dirpath,file_name)#拼接文件地址# print(xml_doc)dom=parse(xml_doc)#解析address文件,返回DOM对象,address为文件地址root=dom.documentElement# 创建根节点。每次都要用DOM对象来创建任何节点,获取根节点,赋值给root作为节点名称img_name=root.getElementsByTagName('path')[0].childNodes[0].data#获取某个元素节点的文本内容,先获取子文本节点,然后通过“data”属性获取文本内容,这里获取图片名字# print(img_name)img_size=root.getElementsByTagName('size')[0]#获取图片尺寸大小,返回一个列表的迭代器# print(img_size)objects=root.getElementsByTagName('object')#获取项目节点名称img_w=img_size.getElementsByTagName('width')[0].childNodes[0].data#获取图片的宽# print(img_w)img_h=img_size.getElementsByTagName('height')[0].childNodes[0].data#获取图片的高img_c=img_size.getElementsByTagName('depth')[0].childNodes[0].data#获取图片的通道数# print(img_name)# print(img_w,img_h,img_c)f = open("Parse_label.txt", 'a', encoding='utf-8')  # 打开txt文件f.writelines("D:/AI/study_ai/processed_images/%s.jpg" % j)  # 将"images/%s.jpg" % j写入txt文件for box in objects:#循环objects的所有标注的框# print(len(box.getElementsByTagName("name")))# print(len(box.getElementsByTagName("name")))for i in range(len(box.getElementsByTagName("name"))):#循环框的类别名字的长度方便下面判断cls_name=box.getElementsByTagName('name')[i].childNodes[0].data#获取框的类别的名字if cls_name=='人':#判断名字和分类是否相同cls_num=0#给每类编号elif cls_name=='马':cls_num=1elif cls_name=='狗':cls_num=2else:cls_num=3# print(cls_name,cls_num)x1=abs(int(box.getElementsByTagName('xmin')[i].childNodes[0].data))#获取框的左上角x1的坐标y1=abs(int(box.getElementsByTagName('ymin')[i].childNodes[0].data))#获取框的左上角y1的坐标x2=abs(int(box.getElementsByTagName('xmax')[i].childNodes[0].data))#获取框的右下角角x2的坐标y2=abs(int(box.getElementsByTagName('ymax')[i].childNodes[0].data))#获取框的右下角角y2的坐标print(cls_name,(x1,y1,x2,y2))box_w=x2-x1#计算框的宽高box_h=y2-y1cx=int(x1+box_w/2)#计算框的中心点坐标并转成int类型cy=int(y1+box_h/2)print(cls_name,(cx,cy,box_w,box_h))dataset.append([box_w, box_h]) #将图片宽高加入集合f.writelines(" {} {} {} {} {} \t".format(cls_num, cx, cy, box_w, box_h))#按行将起写入txt文件f.writelines("\n")#换行f.flush()##flush() 方法是用来刷新缓冲区的,即将缓冲区中的数据立刻写入文件,同时清空缓冲区,不需要是被动的等待输出缓冲区写入。#一般情况下,文件关闭后会自动刷新缓冲区,但有时你需要在关闭前刷新它,这时就可以使用 flush() 方法。f.close()dataset = np.array(dataset)
out = kmeans(dataset, 9)
# clusters =
print(out)
#data里有很多框,out里面只有9个框,avg_iou得出的是
print(f"Accuracy: {100*avg_iou(dataset, out):.2f}%")  # 相似度比较

1.2 得到建议框

'建议框和图片尺寸'
IMG_HEIGHT = 416
IMG_WIDTH = 416
CLASS_NUM = 4"anchor box是对coco数据集聚类获得的建议框"
ANCHORS_GROUP_KMEANS = {52: [[10, 13], [16, 30], [33, 23]],26: [[30, 61], [62, 45], [59, 119]],13: [[116, 90], [156, 198], [373, 326]]}'自定义的建议框'
ANCHORS_GROUP = {13: [[321, 209], [247, 365], [213, 159]],26: [[204, 94], [196, 211], [154, 127]],52: [[140, 233], [75, 187], [51, 108]]}# 计算建议框的面积
ANCHORS_GROUP_AREA = {13: [x * y for x, y in ANCHORS_GROUP[13]],26: [x * y for x, y in ANCHORS_GROUP[26]],52: [x * y for x, y in ANCHORS_GROUP[52]],
}
if __name__ == '__main__':for feature_size, anchors in ANCHORS_GROUP.items():  # 循环字典里面的键值对,并将键和值分别赋值给两个参数print(feature_size)print(anchors)for feature_size, anchor_area in ANCHORS_GROUP_AREA.items():print(feature_size)print(anchor_area)

3.得到向量数据库

2. 自定义数据集

import torch
from torch.utils.data import Dataset,DataLoader
import torchvision
import numpy as np
import cfg
import os
from PIL import Image
import mathLABEL_FILE_PATH = "data/Parse_label.txt"  # 标签数据地址
IMG_BASE_DIR = "D:/AI/study_ai/processed_images"  # 数据总地址transforms = torchvision.transforms.Compose([torchvision.transforms.ToTensor()
])#对数据进行处理def one_hot(cls_num, i):#做一个onehot分类函数b = np.zeros(cls_num)#编一个类别数的一维o数组b[i] = 1.#在指定的位置填充1return bclass MyDataset(Dataset):def __init__(self):with open(LABEL_FILE_PATH,encoding="UTF-8") as f:self.dataset = f.readlines()def __len__(self):return len(self.dataset)def __getitem__(self, index):labels = {}line = self.dataset[index]# images/01.jpg 2 163 218 237 251 1 290 205 170 268strs = line.split()_img_data = Image.open(os.path.join(strs[0]))#打开图片数据img_data = transforms(_img_data)_boxes = np.float32(strs[1:])boxes = np.split(_boxes,len(_boxes)//5)for feature_size, anchors in cfg.ANCHORS_GROUP.items():#设置labels是的格式labels[feature_size] = np.zeros(shape=(feature_size, feature_size, 3, 5 + cfg.CLASS_NUM),dtype=np.float32)for box in boxes:#循环框的个数cls, cx, cy, w, h = boxcx_offset, cx_index = math.modf(cx * feature_size / cfg.IMG_WIDTH)cy_offset, cy_index = math.modf(cy * feature_size / cfg.IMG_WIDTH)for i, anchor in enumerate(anchors):#循环3种建议框,索引分别赋值给i和anchoranchor_area = cfg.ANCHORS_GROUP_AREA[feature_size][i]p_w, p_h = w / anchor[0], h / anchor[1]true_area = w * h#实际标签框的面积iou = min(true_area, anchor_area) / max(true_area, anchor_area)labels[feature_size][int(cy_index), int(cx_index), i] = np.array([iou, cx_offset, cy_offset, np.log(p_w), np.log(p_h),*one_hot(cfg.CLASS_NUM, int(cls))])return labels[13], labels[26], labels[52], img_data
if __name__ == '__main__':# x=one_hot(4,2)# print(x)data = MyDataset()dataloader = DataLoader(data,3,shuffle=True)for i,x in enumerate(dataloader):print(x[0].shape)print(x[1].shape)print(x[2].shape)print(x[3].shape)for i,(target_13, target_26, target_52, img_data) in enumerate(dataloader):print(target_13.shape)print(target_26.shape)print(target_52.shape)print(img_data.shape)

labels的格式:

labels[feature_size] = np.zeros(shape=(feature_size, feature_size, 3, 5 + cfg.CLASS_NUM),dtype=np.float32)

解释:这个是在做labels的格式,往矩阵中全部塞0

labels[feature_size][int(cy_index), int(cx_index), i] = np.array([iou, cx_offset, cy_offset, np.log(p_w), np.log(p_h),*one_hot(cfg.CLASS_NUM, int(cls))])

解释:这个是往有目标的网格中塞值,值为 [建议框和真实框的iou,中心点偏移量,宽高偏移量,类别] 5+class_nums个值

3.训练模型

import os
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from MyDatasets1 import MyDataset
from Net_Module import MainNet# ' torch.argmax(output, dim=1)'
class Trainner:#定义训练类def __init__(self):self.save_path='models/pet.pth'#实例化保存的地址self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")  # 判断用cudaself.net=MainNet().to(self.device)#实例化网络if os.path.exists(self.save_path):#判断是否有之前训练过的网络参数,有的话就加载参数接着训练self.net.load_state_dict(torch.load(self.save_path))self.traindata=MyDataset()#实例化制作的数据集self.trainloader=DataLoader(self.traindata,batch_size=2,shuffle=True)#加载数据集self.conf_loss_fn=nn.BCEWithLogitsLoss()#定义自信度的损失函数self.crood_loss_fn=nn.MSELoss()#定义偏移量的均方差损失函数self.cls_loss_fn=nn.CrossEntropyLoss()#定义多分类的交叉熵损失函数self.optimzer=optim.Adam(self.net.parameters())#定义网络优化器def loss_fn(self,output,target,alpha)# 数据形状[N,C,H,W]-->>标签形状[N,H,W,C]output=output.permute(0,2,3,1)#将形状转成和标签一样#通过reshape变换形状 [N,H,W,C]即[N,H,W,45]-->>[N,H,W,3,15],分成3个建议框每个建议框有15个值output=output.reshape(output.size(0),output.size(1),output.size(2),3,-1)# output=output.cuda().double()#将数据转入cuda并转成double(),因为交叉熵输入类型是double双精度类型mask_obj=target[...,0]>0#拿到自信大于0的掩码值返回其索引,省略号代表前面不变在最后的15个值里面取第0个,第0个为自信度# print(target.shape)# print(mask_obj.shape)output_obj=output[mask_obj]#通过掩码获取对应位置的输出值# print(output_obj.shape)target_obj=target[mask_obj]#通过掩码获取对应位置的标签# print(target_obj.shape)# exit()loss_obj_conf=self.conf_loss_fn(output_obj[:,0],target_obj[:,0])#算自信都损失loss_obj_crood=self.crood_loss_fn(output_obj[:,1:5],target_obj[:,1:5])#算偏移量损失loss_obj_cls=self.cls_loss_fn(output_obj[:,5:],torch.argmax(target_obj[:,5:],dim=1))#可以用这个也可以用下面这个,# 或用下面这个也可以,用下面这个不需要对标签取最大值所以,用上面这个要对标签取最大值所以,因为输出是标量# loss_obj_cls=self.conf_loss_fn(output_obj[:,5:],target_obj[:,5:])loss_obj=loss_obj_conf+loss_obj_crood+loss_obj_cls#正样本总的损失mask_noobj=target[...,0]==0#获取负样本的掩码output_noobj=output[mask_noobj]#根据掩码获取数据target_noobj=target[mask_noobj]#根据掩码获取标签loss_noobj=self.conf_loss_fn(output_noobj[:,0],target_noobj[:,0])#计算负样本损失,负样本自信度为0,因此这里差不多就是和0 做损失loss=alpha*loss_obj+(1-alpha)*loss_noobj  #这里权重调整正负样本训练程度,如果负样本多久可以将权重给大点,负样本少就可以把权重给小点return loss#将损失返回给调用方def train(self):#定义训练函数self.net.train()#这个可用可不用表示训练,网络如果用到batchnormal及dropout等,但测试里面必须添加self.net.eval()epochs=0#训练批次while True:print(epochs)for target_13,target_26,target_52,img_data in self.trainloader:#循环trainloader,将3个返回值分别复制给对应变量target_13,target_26,target_52,img_data=(target_13.to(self.device),target_26.to(self.device),target_52.to(self.device),img_data.to(self.device))#将数据和标签转入cudaoutput_13,output_26,output_52=self.net(img_data)#将数据传入网络获得输出'''output_13 : N 45  13  13 output_26 : N  45  26  26 output_52: N  45  52 52 '''loss_13=self.loss_fn(output_13,target_13,0.7)loss_26=self.loss_fn(output_26,target_26,0.7)loss_52=self.loss_fn(output_52,target_52,0.7)loss=loss_13+loss_26+loss_52#总损失self.optimzer.zero_grad()#清空梯度loss.backward()#反向求导self.optimzer.step()#更新梯度print(loss.item())# torch.save(self.net.state_dict(),self.save_path)if epochs % 10==0:torch.save(self.net.state_dict(),self.save_path.format(epochs))#保存网络参数epochs+=1
if __name__ == '__main__':obj = Trainner()obj.train()

解释: 将数据传入网络获得输出三种侦测头的结果,然后求三种侦测头的损失结果求和,再进行梯度下降不断学习

3.1 损失函数

    def loss_fn(self,output,target,alpha)# 数据形状[N,C,H,W]-->>标签形状[N,H,W,C]output=output.permute(0,2,3,1)#将形状转成和标签一样#通过reshape变换形状 [N,H,W,C]即[N,H,W,45]-->>[N,H,W,3,15],分成3个建议框每个建议框有15个值output=output.reshape(output.size(0),output.size(1),output.size(2),3,-1)# output=output.cuda().double()#将数据转入cuda并转成double(),因为交叉熵输入类型是double双精度类型mask_obj=target[...,0]>0#拿到自信大于0的掩码值返回其索引,省略号代表前面不变在最后的15个值里面取第0个,第0个为自信度# print(target.shape)# print(mask_obj.shape)output_obj=output[mask_obj]#通过掩码获取对应位置的输出值# print(output_obj.shape)target_obj=target[mask_obj]#通过掩码获取对应位置的标签# print(target_obj.shape)# exit()loss_obj_conf=self.conf_loss_fn(output_obj[:,0],target_obj[:,0])#算自信都损失loss_obj_crood=self.crood_loss_fn(output_obj[:,1:5],target_obj[:,1:5])#算偏移量损失loss_obj_cls=self.cls_loss_fn(output_obj[:,5:],torch.argmax(target_obj[:,5:],dim=1))#可以用这个也可以用下面这个,# 或用下面这个也可以,用下面这个不需要对标签取最大值所以,用上面这个要对标签取最大值所以,因为输出是标量# loss_obj_cls=self.conf_loss_fn(output_obj[:,5:],target_obj[:,5:])loss_obj=loss_obj_conf+loss_obj_crood+loss_obj_cls#正样本总的损失mask_noobj=target[...,0]==0#获取负样本的掩码output_noobj=output[mask_noobj]#根据掩码获取数据target_noobj=target[mask_noobj]#根据掩码获取标签loss_noobj=self.conf_loss_fn(output_noobj[:,0],target_noobj[:,0])#计算负样本损失,负样本自信度为0,因此这里差不多就是和0 做损失loss=alpha*loss_obj+(1-alpha)*loss_noobj  #这里权重调整正负样本训练程度,如果负样本多久可以将权重给大点,负样本少就可以把权重给小点return loss#将损失返回给调用方

解释:

1.模型输出的预测结果格式为(N,C,H,W)转换为(N,H,W,3,5+class_nums)的格式,why?因为保持和labels也就是真实标签格式一致。

2.对真实标签中target[...,0]>0得到的是置信度大于0的部分返回其索引(其实就是将有目标位置作为正样本,返回正样本的索引),索引的格式是(N,H,W,3)这样就能定位到具体哪个网格上,坐标为多少的格子上去。得到例如下图的掩码

3. output_obj = output[mask_obj] # 通过正样本掩码获取模型预测结果对应位置的输出值 target_obj=target[mask_obj]#通过正样本掩码获取真实结果对应位置的标签

4. 对output_obj切片,分别对自信度,偏移量,分类进行求损失,再求正样本的损失和

5. 同理对负样本做以上同一操作

6.loss=alpha*loss_obj+(1-alpha)*loss_noobj这个的意思是,因为往往负样本数量远远大于正样本,为了让模型正确学习。所有使用一个参数来平衡正负样本的影响。

3.2 yolov3的不同

我认为yolov3相对于目标分类问题,变化在于

1. 在一张图片上画了网格,让卷积不再只是对整张图片卷积,而是具体到对单个格子卷积。

2.优化了主干网络,会有三种输出,即三种侦测头。使大目标用13的侦测头,小目标用52的侦测头

3. 优化了标签的格式,由原来的labels格式一维[0,1,2,1,1]用来只做分类,到yolov3中标签格式

不仅是从一维升维到4维度,且从数组变为了字典(k:v)k是侦测头类型,v为具体标签。

这样不仅可以将原本只能做分类问题,变为既可以做分类同时也能检测到目标具体位置。

4. 测试

from Net_Module import *
import cfg
import torch
import numpy as np
import PIL.Image as pimg
import PIL.ImageDraw as draw
import tool1
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
import matplotlib.pyplot as plt
from PIL import Image,ImageFont,ImageDrawdevice = "cuda" if torch.cuda.is_available() else "cpu"class Detector(torch.nn.Module):#定义侦测模块def __init__(self,save_path):super(Detector, self).__init__()self.net = MainNet()#实例化网络self.net.load_state_dict(torch.load(save_path,map_location=device))#加载网络参数self.net.eval()#固化参数即去掉batchnormal的作用def forward(self, input, thresh, anchors):#定义前向运算,并给三个参数分别是输入数据,自信度阀值,及建议框output_13, output_26, output_52 = self.net(input)#将数据传入网络并获得输出#(n,h,w,3,15)其中n,h,w,3做为索引,即这里的idxs_13,表示定义在那个格子上idxs_13, vecs_13 = self._filter(output_13, thresh)#赛选获取13*13特侦图自信度合格的自信度的索引和15个值,赛选函数下面自己定义的boxes_13 = self._parse(idxs_13, vecs_13, 32, anchors[13])#对上面输出的两个值进行解析,获得最终的输出框,解析函数在下面idxs_26, vecs_26 = self._filter(output_26, thresh)#赛选获取26*26特侦图自信度合格的自信度的索引和15个值,赛选函数下面自己定义的boxes_26 = self._parse(idxs_26, vecs_26, 16, anchors[26])#对上面输出的两个值进行解析,获得最终的输出框,解析函数在下面idxs_52, vecs_52 = self._filter(output_52, thresh)#赛选获取52*52特侦图自信度合格的自信度的索引和15个值,赛选函数下面自己定义的boxes_52 = self._parse(idxs_52, vecs_52, 8, anchors[52])#对上面输出的两个值进行解析,获得最终的输出框,解析函数在下面return torch.cat([boxes_13, boxes_26, boxes_52], dim=0)#将三种框在0维度按顺序拼接在一起并返回给调用方def _filter(self, output, thresh):#赛选过滤自信度函数,将自信度合格留下来output = output.permute(0, 2, 3, 1) # 数据形状[N,C,H,W]-->>标签形状[N,H,W,C],,因此这里通过换轴# 通过reshape变换形状 [N,H,W,C]即[N,H,W,45]-->>[N,H,W,3,15],分成3个建议框每个建议框有15个值output = output.reshape(output.size(0), output.size(1), output.size(2), 3, -1)mask = output[..., 0] > thresh#获取输出自信度大于自信度阀值的目标值的掩码(即布尔值),idxs = mask.nonzero()#将索引取出来其形状(N,H,W,3)vecs = output[mask]#通过掩码获取对应的数据# print(vecs)return idxs, vecs#将索引和数据返回给调用方def _parse(self, idxs, vecs, t, anchors):#定义解析函数,并给4个参数分别是上面筛选合格的框的索引,9个值(中心点偏移和框的偏移即类别数),# t是每个格子的大小,t=总图大小/特征图大小,anchors建议框anchors = torch.Tensor(anchors)#将建议框转为Tensor类型#idx形状(n,h,w,3)a = idxs[:, 3]#表示拿到3个框对应的索引confidence = torch.sigmoid(vecs[:, 0])#获取自信度vecs里面有5+类别数个元素,第一个为自信度,因此取所有的第0个,输出的置信度会大于1,这里可以用其压缩到0-1之间。_classify = vecs[:, 5:]#获取分类的类别数if len(_classify) == 0:#判断类别数的长的是否为0为0返回空,避免代码报错classify = torch.Tensor([])else:classify = torch.argmax(_classify, dim=1).float()#如果不为0,返回类别最大值的索引,这个索引就代表类别# idx形状(n,h,w,3),vecs(iou,p_x,p_y,p_w,p_h,类别)这里p_x,p_y代表中心点偏移量,p_w,p_h框偏移量cy = (idxs[:, 1].float() + vecs[:, 2]) * t#计算中心点cy(h+p_y)cx = (idxs[:, 2].float() + vecs[:, 1]) * t#计算中心点cx(h+p_x)#a = idxs[:, 3]  # 表示拿到3个对应的索引w = anchors[a, 0] * torch.exp(vecs[:, 3])#计算实际框的宽为w,w=建议框的宽*框的偏移量p_wh = anchors[a, 1] * torch.exp(vecs[:, 4])#计算实际框的高为h,h=建议框的高*框的偏移量p_hx1 = cx - w / 2#计算框左上角x的坐标y1 = cy - h / 2#计算框左上角y的坐标x2 = x1 + w#计算框右下角x的坐标y2 = y1 + h#计算框右下角y的坐标out = torch.stack([confidence,x1,y1,x2,y2,classify], dim=1)#将自信度坐标和类别按照一轴即列的方向重组堆叠在一起return outif __name__ == '__main__':save_path = "models/pet.pth"detector = Detector(save_path)#实例化侦测模块img_path = 'D:\AI\study_ai\processed_images'img_name_list = os.listdir(img_path)name = {0: '人', 1: '马', 2: '狗'}color = {0: "red", 1: "blue", 2: "green"}font = ImageFont.truetype("simsun.ttc", 18, encoding="unic")  # 设置字体for image_file in img_name_list:im =  Image.open(os.path.join(img_path,image_file))# y = detector(torch.randn(3, 3, 416, 416), 0.3, cfg.ANCHORS_GROUP)# print(y.shape)# img1 = pimg.open(r'data\imageS\06.jpg')#打开图片img = im.convert('RGB')#将类型转成rgbimg = np.array(img) / 255#归一化img = torch.Tensor(img)#将数据转成Tensorimg = img.unsqueeze(0)#图片的形状为(h,w,c)在0维度加一个轴变成(1,h,w,c)即(n,h,w,c)的形状img = img.permute(0, 3, 1, 2)#换轴将nhwc变成nchw# img = img.cuda()#这里不能转cuda不然会报错out_value = detector(img, 0.3, cfg.ANCHORS_GROUP)#调用侦测函数并将数据,自信度阀值和建议框传入boxes = []#定义空列表用来装框for j in range(4):#循环判断类别数classify_mask = (out_value[..., -1] == j)#输出的类别如果和类别相同就做nms删掉iou的的框留下iou小的表示这不是同一个物体# ,如果不用这个可能会将不同类别因iou大于目标值而不被删除,因此这里做个判断,只在同一类别中做nms,这里是获取同个类别的掩码_boxes = out_value[classify_mask]#更具掩码索引对应的输出作为框boxes.append(tool1.nms(_boxes))#对同一类别做nms删掉不合格的框,并将框添加进列表for box in boxes:#3种特征图的框循环框torch.cat([boxes_13, boxes_26, boxes_52], dim=0),循环3次# print(i)try:# print(box.shape)img_draw = draw.ImageDraw(im)#制作画笔# cls=int(box[5])for i in range(len(box)):#循环单个特征图的框,循环框的个数次c,x1, y1, x2, y2,cls = box[i, :]#将自信度和坐标及类别分别解包出来# print(c,x1, y1, x2, y2)# print(int(cls.item()))# print(round(c.item(),4))#取值并保留小数点后4位img_draw.rectangle((x1, y1, x2, y2),outline=color[int(cls.item())],width=2)#画框img_draw.text((max(x1, 0) + 3, max(y1, 0) + 3), fill=color[int(cls.item())],text=str(int(cls.item())), font=font,width=2)img_draw.text((max(x1, 0) + 15, max(y1, 0) + 3), fill=color[int(cls.item())],text=name[int(cls.item())], font=font,width=2)img_draw.text((max(x1, 0) + 3, max(y1, 0) + 20), fill=color[int(cls.item())],text=str(round(c.item(),4)), font=font, width=2)except:continue# im.save(os.path.join('Detector_results/results1_img/',image_file))plt.clf()plt.ion()plt.axis('off')plt.imshow(im)plt.show()plt.pause(3)plt.close()# im.show()'============================================================================================================='
'''
nonzero(a) 
nonzero函数是numpy中用于得到数组array中非零元素的位置(数组索引)的函数。它的返回值是一个长度为a.ndim(数组a的轴数)的元组,元组的每个元素都是一个整数数组,其值为非零元素的下标在对应轴上的值。(1)只有a中非零元素才会有索引值,那些零值元素没有索引值;(2)返回的索引值数组是一个2维tuple数组,该tuple数组中包含一维的array数组。其中,一维array向量的个数与a的维数是一致的。(3)索引值数组的每一个array均是从一个维度上来描述其索引值。比如,如果a是一个二维数组,则索引值数组有两个array,第一个array从行维度来描述索引值;第二个array从列维度来描述索引值。(4)transpose(np.nonzero(x))函数能够描述出每一个非零元素在不同维度的索引值。(5)通过a[nonzero(a)]得到所有a中的非零值
'''

解释:

1. 先打开图片,然后格式变为NCHW,传入训练好的模型卷积得出3种输出结果。

2. 将输出结果,通过一个阈值筛选出合格的框(这一步相当于过滤结果)

3. 再将合格框的数据中自信度进行归一化到0到1之间,在通过中心点和宽高算出左上角右下角的坐标,再将分类进行反onehot得到具体类别。

4.将框相同目标的多个框进行nms运算(nms算法就是将高度重合的多个框,取自信度最高的为最终框)

5. 最后就是根据数据在图上画框

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/492299.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《变形金刚:赛博坦的陨落》游戏启动难题:‘buddha.dll’缺失的七大修复策略

《变形金刚:赛博坦的陨落》游戏启动时提示buddha.dll缺失:原因与解决方案 作为一名软件开发从业者,我在日常工作中经常遇到电脑游戏运行时出现的各种问题,如文件丢失、文件损坏和系统报错等。今天,我们就来探讨一下《…

java全栈day16--Web后端实战(数据库)

一、数据库介绍 二、Mysql安装(自行在网上找,教程简单) 安装好了进行Mysql连接 连接语法:winr输入cmd,在命令行中再输入mysql -uroot -p密码 方法二:winr输入cmd,在命令行中再输入mysql -uroo…

简单配置,全面保护:HZERO审计服务让安全触手可及

HZERO技术平台,凭借多年企业资源管理实施经验,深入理解企业痛点,为您提供了一套高效易用的审计解决方案。这套方案旨在帮助您轻松应对企业开发中的审计挑战,确保业务流程的合规性和透明度。 接下来,我将为大家详细介绍…

Microi吾码|开源低代码.NET、VUE低代码项目,表单引擎介绍

Microi吾码|开源低代码.NET、VUE低代码项目,表单引擎介绍 一、摘要二、Microi吾码介绍2.1 功能介绍2.2 团队介绍2.3 上线项目案例 三、Microi吾码表单引擎是什么?四、Microi吾码表单引擎功能4.1 模块引擎 - 由表单引擎驱动4.2 流程引擎 - 由表…

Spring Cloud Sleuth 分布式链路追踪入门

您好,我是今夜写代码,今天学习下分布式链路组件Spring Cloud Sleuth。 本文内容 介绍了分布式链路的思想 Sleuth 和 Zipkin 简单集成Demo,并不涉及 Sleuth原理。 为什么要用链路追踪? 微服务架构下,一个复杂的电商应用,完成下…

低级计算机网络知识总结

1 应用层 1.1 HTTP(TCP) 浏览器访问WWW服务器过程:首先进行域名解析,然后通过TCP向服务器发送连接请求 HTTP本身是无连接,无状态的。无状态特性使服务器能够支持大量的并发HTTP请求。实际应用中,通常使用Cookie加数据库跟踪用户…

了解 SpringMVC 请求流程

文章目录 1. Spring 基础 - SpringMVC 请求流程1.1 引入1.2 什么是 MVC1.3 什么是 Spring MVC1.4 请求流程核心架构的具体流程步骤补充 1.5 案例**Maven 包引入****业务代码的编写**DaoServiceControllerwebapp 下的 web.xmlspringmvc.xmlJSP 视图 2. Spring 进阶 - Dispatcher…

基于Spring Boot的数码产品抢购系统

一、系统背景与目的 随着电子商务的快速发展,数码产品在线销售已成为主流趋势。然而,传统的数码产品销售方式存在抢购过程繁琐、库存管理混乱、用户体验不佳等问题。为了解决这些问题,基于Spring Boot的数码产品抢购系统应运而生。该系统旨在…

【MambaSR复现】【Windows系统下Mamba环境配置】triton 、causal conv1d和mamba_ssm模块配置保姆教程

注:在此之前确保环境中已经安装packaging模块 pip install packaging若此模块安装过程中出现图下报错:这是由于环境创建时候的权限问题导致的,具体解决方案见我另一篇博文! 1.Triton模块安装 注意:必须先安装triton&…

git branch -r(--remotes )显示你本地仓库知道的所有 远程分支 的列表

好的,git branch -r 这个命令用于列出远程分支。让我详细解释一下: 命令: git branch -rdgqdgqdeMac-mini ProductAuthentication % git branch -rorigin/main作用: 这个命令会显示你本地仓库知道的所有 远程分支 的列表。它不…

spring使用rabbitmq当rabbitmq集群节点挂掉 spring rabbitmq怎么保证高可用,rabbitmq网络怎么重新连接

##spring rabbitmq代码示例 Controller代码 import com.alibaba.fastjson.JSONObject; import com.newland.mi.config.RabbitDMMQConfig; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageProperties; import org.springframewo…

【人工智能学习之HDGCN18关键点修改】

【人工智能学习之HDGCN18关键点修改】 训练部分修改部分 训练部分 请参考文章:【人工智能学习之HDGCN训练自己的数据集】 修改部分 参考源码中25关键点的区域划分,我们将18关键点划分为: 头部: 鼻子左眼和左耳右眼和右耳 上肢…

面试题整理3----nc命令的常见用法

面试题整理3----nc命令的常见用法 1. NC是什么2. NC的常用参数2.1 开启指定端口TCP监听(-l小写的L)2.2 测试端口是否能访问(-v)2.3 开启指定端口UDP监听(-u)2.4 端口扫描(-z)2.5 指定超时时间(-w)2.6 指定本地端口号连接(-p)2.7 指定的命令(-e) 1. NC是什么 nc(Net…

C语言实现八大排序算法

目录 1.插入排序 1.1 直接插入排序 1.2 希尔排序 2. 选择排序 2.1 直接选择排序 2.2 堆排序 *TopK问题: 3. 交换排序 3.1 冒泡排序 3.2 快速排序 1. Hoare版本 2. 挖坑法 3. 前后指针法 4. 快速排序优化 5. 非递归快速排序 4.归并排序 1.递归式归并…

【昇腾】NPU ID:物理ID、逻辑ID、芯片映射关系

起因: https://www.hiascend.com/document/detail/zh/Atlas%20200I%20A2/23.0.0/re/npu/npusmi_013.html npu-smi info -l查询所有NPU设备: [naienotebook-npu-bd130045-55bbffd786-lr6t8 DCNN]$ npu-smi info -lTotal Count : 1NPU…

使用Python脚本进行编写批量根据源IP进行查询的语句用于态势感知攻击行为的搜索

使用Python脚本进行编写批量根据源IP进行查询的语句 以下根据ip-list集里面的IP地址(可以自行扩充),然后采用srcaddress "{ip}" or 的形式进行打印并存储在路径为:桌面的IOC结果.txt --------------------------代码如…

【Qt】信号、槽

目录 一、信号和槽的基本概念 二、connect函数:关联信号和槽 例子: 三、自定义信号和槽 1.自定义槽函数 2.自定义信号函数 例子: 四、带参的信号和槽 例子: 五、Q_OBJECT宏 六、断开信号和槽的连接 例子: …

揭开 Choerodon UI 拖拽功能的神秘面纱

01 引言 系统的交互方式主要由点击、选择等组成。为了提升 HZERO 系统的用户体验、减少部分操作步骤,组件库集成了卓越的拖拽功能,让用户可以更高效流畅的操作系统。 例如:表格支持多行拖拽排序、跨表数据调整、个性化调整列顺序&#xff1…

低代码企业管理的革命:Microi吾码产品深度测评

低代码平台Microi吾码:帮助企业快速构建自定义数据管理与自动化系统 在现代企业的数字化转型过程中,如何快速响应市场变化并高效管理内部数据,已成为各类企业面临的重要挑战。低代码平台作为一种创新的技术解决方案,为企业提供了…

机器学习之交叉熵

交叉熵(Cross-Entropy)是机器学习中用于衡量预测分布与真实分布之间差异的一种损失函数,特别是在分类任务中非常常见。它源于信息论,反映了两个概率分布之间的距离。 交叉熵的数学定义 对于分类任务,假设我们有&#…