13.4 目标检测锚框标注 非极大值抑制

锚框的形状计算公式

假设原图的高为H,宽为W
在这里插入图片描述

锚框形状详细公式推导

在这里插入图片描述

以每个像素为中心生成不同形状的锚框

请添加图片描述

# s是缩放比,ratio是宽高比
def multibox_prior(data, sizes, ratios):"""生成以每个像素为中心具有不同形状的锚框"""in_height,in_width = data.shape[-2:] # 取出最后两个元素,即h和wdevice,num_sizes,num_ratios = data.device,len(sizes),len(ratios)boxes_per_pixel = (num_sizes+num_ratios -1) # 以某个像素坐标为中心的锚框为n+m-1size_tensor = torch.tensor(sizes,device=device) # 将缩放比例列表sizes转为tensor, device参数指定设备ratio_tensor = torch.tensor(ratios,device=device)# 为了将锚点移动到像素的中心,需要设置偏移量。# 因为一个像素的高为1且宽为1,我们选择偏移我们的中心0.5offset_h, offset_w = 0.5, 0.5steps_h = 1.0 / in_height # 在y轴上缩放步⻓steps_w = 1.0 / in_width # 在x轴上缩放步⻓print(f'steps_h,steps_w = {steps_h,steps_w}')# 生成锚框的所有中心点center_h = (torch.arange(in_height, device=device) + offset_h) * steps_hcenter_w = (torch.arange(in_width, device=device) + offset_w) * steps_wprint(f'center_h,center_w={center_h,center_w}')#网格化中心点坐标shift_y,shift_x = torch.meshgrid(center_h,center_w)#reshape成一维,shift_y和shift_x坐标一一对应shift_y,shift_x = shift_y.reshape(-1),shift_x.reshape(-1)print(f'shift_y, shift_x={shift_y, shift_x}') ##norm=√(H/W),这个就是个标号,方便计算norm = torch.sqrt(torch.tensor(in_height)/torch.tensor(in_width))# 生成“boxes_per_pixel”个高和宽,#只考虑包含s1或r1的组合,因此S*r1 与s1*R合并即为n+m-1个锚框w = torch.cat((size_tensor * torch.sqrt(ratio_tensor[0]),size_tensor[0] * torch.sqrt(ratio_tensor[1:]))) * normh = torch.cat((size_tensor / torch.sqrt(ratio_tensor[0]),size_tensor[0] / torch.sqrt(ratio_tensor[1:]))) / norm# 获得归一化后的锚框的w,h的一半,形成偏移量,为了让归一化后的锚框根据中心点 + 偏移量找到 左上角和右下角坐标anchor_manipulations = torch.stack((-w, -h, w, h)).T.repeat(in_height * in_width, 1) / 2# 每个中心点都将有“boxes_per_pixel”个锚框,# 所以生成含所有锚框中心的网格,重复了“boxes_per_pixel”次out_grid = torch.stack([shift_x, shift_y, shift_x, shift_y],dim=1).repeat_interleave(boxes_per_pixel, dim=0)# 每个中心点都将有“boxes_per_pixel”个锚框,# 所以生成含所有锚框中心的网格,重复了“boxes_per_pixel”次out_grid = torch.stack([shift_x, shift_y, shift_x, shift_y],dim=1).repeat_interleave(boxes_per_pixel, dim=0)#(x_min,y_min,x_max,y_max) =  归一化后的锚框中心点 + 往左上角和右下角走的偏移量output = out_grid + anchor_manipulationsreturn output.unsqueeze(0)
# 将锚框变量Y的形状更改为(图像高度,图像宽度,以同一像素为中心的锚框的数量,4)
boxes = Y.reshape(h, w, 5, 4)#                                                  此处的5由 缩放的数量n + 宽高比的数量m -1 而得
# 访问以(250,250)为中心的第一个锚框。它有四个元素:锚框左上⻆的(x, y)轴坐标和右下⻆的(x, y)轴坐标
boxes[250, 250, 0, :] # 输出的坐标是归一化后的,即归一化前的锚框 w/in_weight 和 h/in_height
img = d2l.plt.imread('../data/images/cat_and_dog.jpg')
h, w = img.shape[:2]
print(h, w)
X = torch.rand(size=(1, 3, h, w))
# 返回的锚框变量Y的形状是(批量大小,锚框的数量,4 (表示锚框的左上角右下角坐标))。
Y = multibox_prior(X, sizes=[0.75, 0.5, 0.25], ratios=[1, 2, 0.5])
Y.shape

根据真实框来标注生成的锚框

请添加图片描述
请添加图片描述

# 计算IOU
def box_iou(boxes1,boxes2):''':param boxes1: shape = (boxes1的数量,4):param boxes2: shape = (boxes2的数量,4):param areas1: boxes1中每个框的面积 ,shape = (boxes1的数量):param areas2: boxes2中每个框的面积 ,shape = (boxes2的数量):return:'''# 定义一个Lambda函数,输入boxes,内容是计算得到框的面积box_area = lambda  boxes:((boxes[:,2] - boxes[:,0]) * (boxes[:,3] - boxes[:,0]))# 计算面积areas1 = box_area(boxes1)areas2 = box_area(boxes2)# 计算交集 要把所有锚框的左上角坐标 与 真实框的所有左上角坐标 作比较,大的就是交集的左上角 ,加个None 可以让锚框与所有真实框作对比inter_upperlefts = torch.max(boxes1[:,None,:2],boxes2[:,:2])# 把所有锚框的右下角坐标 与 真实框的所有右下角坐标 作比较,小的就是交集的右下角坐标 ,加个None 可以让锚框与所有真实框作对比inter_lowerrights = torch.min(boxes1[:,None,2:],boxes2[:,2:])# 如果右下角-左上角有元素小于0,那就说明没有交集,clamp(min-0)会将每个元素与0比较,小于0的元素将会被替换成0inters = (inter_lowerrights - inter_upperlefts).clamp(min=0) # 得到w和hinter_areas = inters[:,:,0] * inters[:,:,1] # 每个样本的 w*h# 求锚框与真实框的并集# 将所有锚框与真实框相加,他们会多出来一个交集的面积,所以要减一个交集的面积union_areas = areas1[:,None] * areas2 - inter_areasreturn inter_areas/union_areas
# 每个真实框都要跟所有锚框计算iou,Iou数量等于,真实框数量 * 锚框的数量
def assign_anchor_to_bbox(ground_truth,anchors,devices,iou_threshold=0.5):# 得到锚框和真实框的个数num_anchors,num_gt_boxes = anchors.shape[0],ground_truth.shape[0]# jaccard是计算 所有锚框anchors和真实框ground_truth的交并比jaccard = box_iou(anchors,ground_truth)# torch.full(size,fill_value,dtype,device),如下代码生产成一个一位数组,长度为锚框的个数,值为-1anchors_bbox_map = torch.full((num_anchors,),-1,dtype=torch.long,device=devices)# 对行取最大值,得到每个真实框对应的最大IOU的锚框max_ious,indices = torch.max(jaccard,dim=1)# 返回张量中非0元素的索引,即Max_iou>设定的阈值,位于第i行和第j列的元素x_ij是锚框i和真实边界框j的IoUanc_i = torch.nonzero(max_ious>=iou_threshold).reshape(-1)box_j = indices[max_ious>=iou_threshold]anchors_bbox_map[anc_i] = box_jcol_discard = torch.full((num_anchors,), -1)row_discard = torch.full((num_gt_boxes,), -1)for _ in range(num_gt_boxes):max_idx = torch.argmax(jaccard)box_idx = (max_idx % num_gt_boxes).long()anc_idx = (max_idx / num_gt_boxes).long()anchors_bbox_map[anc_idx] = box_idxjaccard[:, box_idx] = col_discardjaccard[anc_idx, :] = row_discardreturn anchors_bbox_map
# 标注类别和偏移量
def offset_boxes(anchors, assigned_bb, eps=1e-6):"""对锚框偏移量的转换"""c_anc = d2l.box_corner_to_center(anchors)c_assigned_bb = d2l.box_corner_to_center(assigned_bb)offset_xy = 10 * (c_assigned_bb[:, :2] - c_anc[:, :2]) / c_anc[:, 2:]offset_wh = 5 * torch.log(eps + c_assigned_bb[:, 2:] / c_anc[:, 2:])offset = torch.cat([offset_xy, offset_wh], axis=1)return offset
'''如果一个锚框没有被分配真实边界框,将锚框的类别标记为背景。背景类别的锚框通常被称为负类锚框,其余的被称为正类锚框。我们使用真实边界框(labels参数)实现以下multibox_target函数,来标记锚框的类别和偏移量(anchors参数)。此函数将背景类别的索引设置为零,然后将新类别的整数索引递增一。
'''
def multibox_target(anchors, labels):"""使用真实边界框标记锚框"""batch_size, anchors = labels.shape[0], anchors.squeeze(0)batch_offset, batch_mask, batch_class_labels = [], [], []device, num_anchors = anchors.device, anchors.shape[0]for i in range(batch_size):label = labels[i, :, :]anchors_bbox_map = assign_anchor_to_bbox(label[:, 1:], anchors, device)bbox_mask = ((anchors_bbox_map >= 0).float().unsqueeze(-1)).repeat(1, 4)# 将类标签和分配的边界框坐标初始化为零class_labels = torch.zeros(num_anchors, dtype=torch.long,device=device)assigned_bb = torch.zeros((num_anchors, 4), dtype=torch.float32,device=device)# 使用真实边界框来标记锚框的类别。# 如果一个锚框没有被分配,标记其为背景(值为零)indices_true = torch.nonzero(anchors_bbox_map >= 0)bb_idx = anchors_bbox_map[indices_true]class_labels[indices_true] = label[bb_idx, 0].long() + 1assigned_bb[indices_true] = label[bb_idx, 1:]# 使用真实边界框来标记锚框的类别。# 如果一个锚框没有被分配,标记其为背景(值为零)indices_true = torch.nonzero(anchors_bbox_map >= 0)bb_idx = anchors_bbox_map[indices_true]class_labels[indices_true] = label[bb_idx, 0].long() + 1assigned_bb[indices_true] = label[bb_idx, 1:]# 偏移量转换offset = offset_boxes(anchors, assigned_bb) * bbox_maskbatch_offset.append(offset.reshape(-1))batch_mask.append(bbox_mask.reshape(-1))batch_class_labels.append(class_labels)bbox_offset = torch.stack(batch_offset)bbox_mask = torch.stack(batch_mask)class_labels = torch.stack(batch_class_labels)return (bbox_offset, bbox_mask, class_labels)
# 第一个元素表示类别,0代表狗,1代表猫。其余四个元素是左下角坐标和右上角坐标(归一化后的介于0-1之间),归一化的方法是,x坐标 / 宽,y坐标/高
ground_truth = torch.tensor([[0, 0.1, 0.08, 0.52, 0.92],[1, 0.55, 0.2, 0.9, 0.88]])
# 锚框
anchors = torch.tensor([[0, 0.1, 0.2, 0.3], [0.15, 0.2, 0.4, 0.4],[0.63, 0.05, 0.88, 0.98], [0.66, 0.45, 0.8, 0.8],[0.57, 0.3, 0.92, 0.9]])
bbox_scale = torch.tensor((w, h, w, h))
# img = d2l.plt.imread('../data/images/cat_dog.png')
img = d2l.plt.imread('../data/images/cat_and_dog.jpg')
fig = d2l.plt.imshow(img)
# 画出真实框 :(坐标轴,归一化*bbox_scale得到原图规模的坐标,标签,颜色)
show_bboxes(fig.axes,ground_truth[:,1:] * bbox_scale,['dog','cat'],'k') # k最后画出来是黑色
# 画出设置的锚框,把锚框类别标记为0-4
show_bboxes(fig.axes, anchors * bbox_scale, ['0', '1', '2', '3', '4']);
'''labels[0]:labels[1]:掩码,形状为(批量大小,锚框数的4倍),对应每个锚框的4个偏移量(负类掩码为0),通过元素乘法,将负类的偏移量过滤掉labels[2]:锚框对应的标签
'''
labels = multibox_target(anchors.unsqueeze(dim=0),ground_truth.unsqueeze(dim=0))

非极大值抑制

请添加图片描述

'''在预测时,我们先为图像生成多个锚框,再为这些锚框一一预测类别和偏移量。一个预测好的边界框则根据其中某个带有预测偏移量的锚框而生成。下面我们实现了offset_inverse函数,该函数将锚框和偏移量预测作为输入,并应用逆偏移变换来返回预测的边界框坐标。输入: 锚框 和 偏移量预测输出:根据锚框的原始坐标和预测的偏移量 计算出的 预测的边界框坐标
'''
def offset_inverse(anchors, offset_preds):"""根据带有预测偏移量的锚框来预测边界框"""anc = d2l.box_corner_to_center(anchors)pred_bbox_xy = (offset_preds[:, :2] * anc[:, 2:] / 10) + anc[:, :2]pred_bbox_wh = torch.exp(offset_preds[:, 2:] / 5) * anc[:, 2:]pred_bbox = torch.cat((pred_bbox_xy, pred_bbox_wh), axis=1)predicted_bbox = d2l.box_center_to_corner(pred_bbox)return predicted_bbox
'''按降序对置信度进行排序并返回其索引'''
#@save
def nms(boxes, scores, iou_threshold):"""对预测边界框的置信度进行排序"""B = torch.argsort(scores, dim=-1, descending=True)keep = []# 保留预测边界框的指标while B.numel() > 0:i = B[0]keep.append(i)if B.numel() == 1: breakiou = box_iou(boxes[i, :].reshape(-1, 4),boxes[B[1:], :].reshape(-1, 4)).reshape(-1)inds = torch.nonzero(iou <= iou_threshold).reshape(-1)B = B[inds + 1]return torch.tensor(keep, device=boxes.device)
def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5,pos_threshold=0.009999999):"""使用非极大值抑制来预测边界框"""device, batch_size = cls_probs.device, cls_probs.shape[0]anchors = anchors.squeeze(0)num_classes, num_anchors = cls_probs.shape[1], cls_probs.shape[2]out = []for i in range(batch_size):cls_prob, offset_pred = cls_probs[i], offset_preds[i].reshape(-1, 4)conf, class_id = torch.max(cls_prob[1:], 0)'''调用offset_inverse'''predicted_bb = offset_inverse(anchors, offset_pred)'''调用nms'''keep = nms(predicted_bb, conf, nms_threshold)# 找到所有的non_keep索引,并将类设置为背景all_idx = torch.arange(num_anchors, dtype=torch.long, device=device)combined = torch.cat((keep, all_idx))uniques, counts = combined.unique(return_counts=True)non_keep = uniques[counts == 1]all_id_sorted = torch.cat((keep, non_keep))class_id[non_keep] = -1class_id = class_id[all_id_sorted]conf, predicted_bb = conf[all_id_sorted], predicted_bb[all_id_sorted]# pos_threshold是一个用于非背景预测的阈值below_min_idx = (conf < pos_threshold)class_id[below_min_idx] = -1conf[below_min_idx] = 1 - conf[below_min_idx]pred_info = torch.cat((class_id.unsqueeze(1),conf.unsqueeze(1),predicted_bb), dim=1)out.append(pred_info)return torch.stack(out)

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

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

相关文章

一文搞懂深度信念网络!DBN概念介绍与Pytorch实战

目录 一、概述1.1 深度信念网络的概述1.2 深度信念网络与其他深度学习模型的比较结构层次学习方式训练和优化应用领域 1.3 应用领域图像识别与处理自然语言处理推荐系统语音识别无监督学习与异常检测药物发现与生物信息学 二、结构2.1 受限玻尔兹曼机&#xff08;RBM&#xff0…

LLMs训练的算力优化Computational challenges of training LLMs

当您尝试训练大型语言模型时&#xff0c;您仍然经常遇到的最常见问题之一是内存不足。如果您曾尝试在Nvidia GPU上训练或甚至只是加载模型&#xff0c;那么这个错误消息可能看起来很熟悉。 CUDA&#xff0c;即Compute Unified Device Architecture的缩写&#xff0c;是为Nvid…

【rust/egui】(六)看看template的app.rs:TextEdit

说在前面 rust新手&#xff0c;egui没啥找到啥教程&#xff0c;这里自己记录下学习过程环境&#xff1a;windows11 22H2rust版本&#xff1a;rustc 1.71.1egui版本&#xff1a;0.22.0eframe版本&#xff1a;0.22.0上一篇&#xff1a;这里 TextEdit 文本编辑框 其定义为&#…

Grounded Language-Image Pre-training论文笔记

Title&#xff1a;Grounded Language-Image Pre-training Code 文章目录 1. 背景2. 方法&#xff08;1&#xff09;Unified Formulation传统目标检测grounding目标检测 &#xff08;2&#xff09;Language-Aware Deep Fusion&#xff08;3&#xff09;Pre-training with Scala…

【golang】派生数据类型---指针 标识符、关键字等

1、指针 对比C/C中的指针&#xff0c;go语言中的指针显得极为简洁&#xff0c;只是简单的获取某个空间的地址 或者 根据指针变量中的内容 获取对应存储空间的内容等操作。 具体示例如下&#xff1a; go中使用指针需要注意的点&#xff1a; 可以通过指针改变它所指向的内存空…

【CSS】轮播图案例开发 ( 基本设置 | 子绝父相 | 浏览器水平居中 | 圆角设置 | 绝对定位居中设置 )

代码示例 : <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Banner 轮播</title><style>/* 取消浏览器或者其它标签的默认的内外边距 */* {margin: 0;padding: 0;}/* 取消列表样式 主要是…

数据采集:selenium 获取某网站CDN 商家排名信息

写在前面 工作中遇到&#xff0c;简单整理理解不足小伙伴帮忙指正 对每个人而言&#xff0c;真正的职责只有一个&#xff1a;找到自我。然后在心中坚守其一生&#xff0c;全心全意&#xff0c;永不停息。所有其它的路都是不完整的&#xff0c;是人的逃避方式&#xff0c;是对大…

【内网穿透】搭建我的世界Java版服务器,公网远程联机

目录 前言 1. 搭建我的世界服务器 1.1 服务器安装java环境 1.2 配置服务端 2. 测试局域网联机 3. 公网远程联机 3.1 安装cpolar内网穿透 3.1.1 windows系统 3.1.2 linux系统&#xff08;支持一键自动安装脚本&#xff09; 3.2 创建隧道映射内网端口 3.3 测试公网远程…

【电源专题】18650圆柱电芯内部结构及器件

18650圆柱锂离子电池是一种直径为18mm、高度为65mm的锂离子电池,它最大的特点是拥有非常高的能量密度,它是比较成熟的锂离子电池,各方面系统质量稳定性较好,广泛适用于10千瓦时左右的电池容量场合,例如在、在手机、笔记本电脑等小型电器上。 常见的18650电池分为锂离子电池…

亚马逊云科技 re:Inforce 大会云安全合规与技术实践及 Security Jam 大赛,快来报名吧!...

‍‍ 2023年8月31日在北京 亚马逊云科技 re:Inforce 大会 首次登陆中国&#xff01; 我们期待您的莅临&#xff0c; 并与您一起迎接 AI 时代&#xff0c; 开启全面智能的安全旅程&#xff01; 在13:00-17:00的 培训与动手实验环节中 云安全合规与技术实践 及 Security Jam 大赛…

Python3 列表

Python3 列表 序列是 Python 中最基本的数据结构。 序列中的每个值都有对应的位置值&#xff0c;称之为索引&#xff0c;第一个索引是 0&#xff0c;第二个索引是 1&#xff0c;依此类推。 Python 有 6 个序列的内置类型&#xff0c;但最常见的是列表和元组。 列表都可以进…

韶音骨传导耳机值得入手吗,韶音骨传导耳机可以水洗吗

韶音家的代表作可以说是OpenRun Pro骨传导耳机&#xff0c;在发声单元位置上采用了开孔的处理&#xff0c;佩戴上耳的时候发声单元可以贴合耳道&#xff0c;在低频延伸性&#xff0c;但在中高频的时候整体会出现震感&#xff0c;纤细的耳挂在佩戴的时候是有着不错的舒适度的&am…

农村农产品信息展示网站的设计与实现(论文+源码)_kaic

摘 要 随着软件技术的迅速发展,农产品信息展示的平台越来越多,传统的农产品显示方法将被计算机图形技术取代。这种网站技术主要把农产品的描述、农产品价格、农产品图片等内容&#xff0c;通过计算机网络的开发技术&#xff0c;在互联网上进行展示&#xff0c;然后通过计算机网…

单片机TVS/ESD二极管防护

TVS 瞬态电压抑制二极管Transient Voltage Suppressor ESD 静电释放二极管 Electro-Static discharge 这两种本质上都是二极管。都是利用了二极管正向导通、反向截止的特性。二极管在反向截止截止条件下&#xff0c;如果电压继续增大&#xff0c;将会引发雪崩&#xff0c;使得…

ubuntu20.04安装gcc5.4 g++5.4

在进行ubuntu20.04的系统中安装gcc g5.4中&#xff0c;会出现安装问题 1、pip安装&#xff0c;失败 2、使用apt-get install 进行安装时&#xff0c;提示没有候选项&#xff1b; 原因&#xff1a;ubuntu20.04的系统下&#xff0c;系统默认安装的gcc9.0的版本&#xff0c;默认…

7.Oracle视图创建与使用

1、视图的创建与使用 在所有进行的SQL语句之中&#xff0c;查询是最复杂的操作&#xff0c;而且查询还和具体的开发要求有关&#xff0c;那么在开发过程之中&#xff0c;程序员完成的并不是是和数据库的所有内容&#xff0c;而更多的是应该考虑到程序的设计结构。可以没有一个项…

WordPress使用子主题插件 Child Theme Wizard,即使主题升级也能够保留以前主题样式

修改WordPress网站样式&#xff0c;主题升级会导致自己定义设置的网站样式丢失&#xff0c;还需要重新设置&#xff0c;很繁琐工作量大&#xff0c;发现在WordPress 中有Child Theme Wizard子主题插件&#xff0c;使用Child Theme Wizard子主题插件&#xff0c;即使主题升级&am…

HTML-常见标签、HTML5新特性

HTML 软件架构 1.C/S架构 (1) C/S架构即Client/Server&#xff08;客户机/服务器&#xff09;结构。 (2) C/S 架构特点 ​ C/S结构在技术上很成熟&#xff0c;它的主要特点是交互性强、具有安全的存取模式、网络通信量低、响应速度快、利于处理大量数据。但是该结构的程序是…

8年测试经验之谈 —— 接口自动化测试requests

1.什么是requests&#xff1f; requests是一个Python第三方库&#xff0c;处理URL资源特别方便 2.安装requests pip3 install requests 如果遇到Permission denied安装失败&#xff0c;请加上sudo重试 3.使用requests 3.1get请求方法 3.1.1基本的get请求 import reques…

如何使用CSS实现一个3D旋转效果?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 3D效果实现⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域…