20230508MTCNN1

news2024/10/5 19:10:45

多目标检测思路


  • 单目标检测:图片 输入到 模型,模型输出 4个值

  • 为什么模型只能检测单个目标?
    因为模型 固定输出4个值,表示 一个目标


  • 如何实现多目标检测?思路:一个一个地数
    • 模型要能够 认识目标(单目标检测)

    • 设计一个算法,让模型 一个一个的找;不能重复 不能漏掉的方式去找目标


    • 模型怎么处理这个问题?

      • 模型不知道 人脸在什么地方,所以从左上角开始检测
      • 抠出来的区域 输入到模型, 模型输出5个值,cls x1 y1 x2 y2 (cls有没有目标 置信度、 x1 y1左上、 x2 y2右下坐标)

  • 我们通过设计一个 单目标检测网络
    一个一个 去统计图像上的 目标区域

  • 思考:我们如何实现多目标检测?
    • 认识目标 (提取特征)
    • 一个一个数 (单目标检测)(怎么数?从上到下,从左到右,把目标传进 单目标检测模型,模型输出5个值)
    • 统计结果

图像金字塔 & NMS


思考:框的大小多大?框的步长多大?



  • 一个框 把某块区域 截取出来

  • 输入的框的 大小如何确定?
    如何设计一个框,这个框可以包含所有尺寸的人脸
    因为框的大小是固定 (因为单目标检测模型 输入的数据是固定尺寸的图片)


  • 框的大小要定死
    如果定小了,可能框不住目标
    如果定大了,可能漏掉目标 (一个框框住了两个目标,就漏掉了一个目标,应该一个框对应一个目标)
    但又必须要 固定框的尺寸

框不能动,那就动图片

图像金字塔

  • 图像金字塔

    图片每缩小一次,这个固定大小的框都是 从左到右,从上到下,去扫描图片


  • 图像金字塔
    • 做 多目标检测 时候,利用 框在原图上 不断的滑动 去检测人脸,框的大小是 固定的。框的大小是 12*12,(正方形,人脸这个目标 刚好接近于正方形)
    • 没法检测 大一点的人脸,因此引入了 图像金字塔 的概念
    • 不断的 缩小图片 以适应 框的大小,当下一次图片的 最小边长 小于或等于 框的大小时,图片就 停止缩放

在这里插入图片描述


使用图像金字塔,每次框的目标的个数,最多只有一个目标,或者没有目标

在这里插入图片描述

  • 框的步长为多大?

    • 框的步长大了,会丢脸
    • 步长,步长2个像素
      步长调小了, 可能检测到目标,但会重复检测目标
    • 去重,选出最好的框, 非极大值抑制 (NMS)

非极大值抑制 (NMS) 思路

  • 非极大值抑制 (NMS) 思路:
    • 置信度进行排序,先保留置信度 最大,移动到新的集合
    • 置信度 最大的框剩余4个 交并比(iou),有4个iou值,和阈值 作比较,大于阈值时,就删除 (阈值是重合度,自定义值,一般偏大的值,比如 0.6)
    • 再对剩下进行排序又保留 置信度最大的框,再求iou,再删除 大于阈值
    • 直到原集合数据为空

在这里插入图片描述
-

在这里插入图片描述

在这里插入图片描述


IOU & NMS代码实现


utils.py


"""
计算交并比

rec1 是第1个矩形的 x1,y1,x2,y2
rec2 是第2个矩形的 x1,y1,x2,y2

得到最大的 x1,y1  ->  interX1,interY1
得到最小的 x2,y2  ->  interX2,interY2


交集矩形宽 w = max(0,interX2-interX1)
交集矩形高 h = max(0,interY2 - interX2)

交集面积 interArea = w * h
并集面积 twoArew = 两个矩形面积和

交并比 = 交集面积 / (并集面积 - 交集面积)

"""
import numpy as np


def iou(rec1, rec2):
    # x1 y1 x2 y2
    # 0 1   2 3
    interX1, interY1 = np.maximum(rec1[0:2], rec2[0:2])
    interX2, interY2 = np.minimum(rec1[2:4], rec2[2:4])
    w = max(0, interX2 - interX1)
    h = max(0, interY2 - interY1)
    interArea = w * h
    area1 = (rec1[2] - rec1[0]) * (rec1[3] - rec1[1])
    area2 = (rec2[2] - rec2[0]) * (rec2[3] - rec2[1])
    twoArew = area1 + area2
    return interArea / (twoArew - interArea)

"""
计算某个box,与其它多个box分别求iou
box 的坐标(x1,y1,x2,y2)
boxes
[
    [x1,y1,x2,y2],
    [x1,y1,x2,y2],
    [x1,y1,x2,y2],
    [x1,y1,x2,y2],
]
"""
def iou2(box,boxes):
    # box = [0 0 4 4]

    # 求框的面积
    box_area = (box[2] - box[0]) * (box[3] - box[1])
    """
    boxes = 
    [
        [6,6,7,7]
        [1,1,5,5]
    ]
    
    # [ 1 16]
    """
    boxes_area = (boxes[:,2] - boxes[:,0])  * (boxes[:,3] - boxes[:,1]) # [ 1 16]
    # 求交集的面积
    xx1 = np.maximum(box[0],boxes[:,0])         # np.maximum(5,[1,2,5,6,8])   ->  [5, 5, 5, 6, 8]
    yy1 = np.maximum(box[1],boxes[:,1])
    xx2 = np.minimum(box[2],boxes[:,2])
    yy2 = np.minimum(box[3],boxes[:,3])

    w = np.maximum(0,xx2 - xx1)
    h = np.maximum(0,yy2 - yy1)

    inv = w * h
    iou = inv / (box_area+boxes_area - inv)

    return iou

"""
非极大值抑制
boxes 框
thresh 阈值
boxes.shape[n,5]
"""
def nms(boxes,thresh=0.3):
    # 先对boxes 的置信度进行排序
    # argsort 按照从小到大进行排序,并按照对应的索引值输出
    new_index = np.argsort(-boxes[:,0]) # 得到排序的索引 负号表示倒序 # np.argsort([3,4,1,6,2]) -> array([2, 4, 0, 1, 3])
    new_boxes = boxes[new_index]
    # 最终输出所有的框
    boxes_result = []

    while len(new_boxes)>1:
        box = new_boxes[0]
        # 直接将置信度最大的box加入结果中
        boxes_result.append(box)
        new_boxes = new_boxes[1:]
        # 置信度最大的框和其他框分别求iou
        iou = iou2(box[1:],new_boxes[:,1:])
        # 取出和第一个box的iou小于阈值的框
        box_index = np.where(iou<thresh)
        new_boxes = new_boxes[box_index]

    if len(new_boxes) > 0:
        boxes_result.append(new_boxes[0])

    return boxes_result



if __name__ == '__main__':
    # 测试 iou2
    # box = np.array([0,0,4,4])
    # boxes = np.array([[6,6,7,7],[1,1,5,5]])
    #
    # iou = iou2(box,boxes)
    # print(iou)

    # 测试 nms
    boxes = np.array([
                        [0.2, 1,2,4,4],
                        [0.9, 8,8,9,9],
                        [0.7, 1,1,4,4]
                      ])
    boxes = nms(boxes)
    print(boxes)



MTCNN网络结构介绍


在这里插入图片描述
级联: 把一个复杂的问题, 拆多个步骤
好处:高效


P网络 R网络 O网络结构图
在这里插入图片描述

  • p网络数据量是最大
    • 图像金字塔相当于把一张图片变成了很多张图片
    • 网络越浅,网络越小,速度越快

  • p网络是 卷积层 作为输出层;r网络和o网络是 全连接层作为输出层;
  • p r o 网络 分别是 3 4 5 层网络
  • 输出2 表示 有无目标的概率;输出4 表示框的左上 右下坐标值;输出10 表示5个关键点 坐标

在这里插入图片描述


mtcnn 代码

PNet.py

import torch
import torch.nn as nn

class PNet(nn.Module):
    def __init__(self):
        super(PNet, self).__init__()
        self.conv1 = nn.Conv2d(in_channels=3,out_channels=10,kernel_size=3,stride=1)
        self.prelu1 = nn.PReLU()
        self.pool1 = nn.MaxPool2d(kernel_size=2,stride=2,ceil_mode=True)  # ceil_mode :计算输出形状,布尔类型,为True,用向上取整的方法;默认是向下取整。

        self.conv2 = nn.Conv2d(in_channels=10,out_channels=16,kernel_size=3)
        self.prelu2 = nn.PReLU()

        self.conv3 = nn.Conv2d(in_channels=16,out_channels=32,kernel_size=3)
        self.prelu3 = nn.PReLU()

        # 三个输出层;分别是 置信值,框的左上 右下 坐标 ,五个关键点坐标
        self.outConv1 = nn.Conv2d(32,2,kernel_size=1)
        self.outConv2 = nn.Conv2d(32,4,kernel_size=1)
        self.outConv3 = nn.Conv2d(32,10,kernel_size=1)

        # todo
        for m in self.modules():
            print(f"m的值:{m}")
            if isinstance(m,nn.Conv2d):
                nn.init.kaiming_normal(m.weight,mode='fan_out', nonlinearity="relu")

    def forward(self, x):
        # 三层卷积
        x = self.pool1(self.prelu1(self.conv1(x))) # 有池化
        x = self.prelu2(self.conv2(x))
        x = self.prelu3(self.conv3)

        """
        torch.squeeze
        不指定维度时,删除所有大小为1的维度
        指定维度的大小为1,删除指定维度
        指定的维度大小不为1,不做任何改变
        """

        # 分类是否人脸的卷积输出层
        class_out = self.outConv1(x)
        class_out = torch.squeeze(class_out, dim=2) #         todo  debug
        class_out = torch.squeeze(class_out, dim=2)

        # 人脸box的回归卷积输出层
        bbox_out = self.outConv2(x)
        bbox_out = torch.squeeze(bbox_out, dim=2)
        bbox_out = torch.squeeze(bbox_out, dim=2)

        # 5个关键点的回归卷积输出层
        landmark_out = self.outConv3(x)
        landmark_out = torch.squeeze(landmark_out, dim=2)
        landmark_out = torch.squeeze(landmark_out, dim=2)
        return class_out, bbox_out, landmark_out


RNet.py

import torch
import torch.nn as nn

class RNet(nn.Module):
    def __init__(self):
        super(RNet, self).__init__()
        self.conv1 = nn.Sequential(
            nn.Conv2d(in_channels=3,out_channels=28,kernel_size=3),nn.PReLU(),
            nn.MaxPool2d(kernel_size=3,stride=2)
        )
        self.conv2 = nn.Sequential(
            nn.Conv2d(in_channels=28,out_channels=48,kernel_size=3),nn.PReLU(),
            nn.MaxPool2d(kernel_size=3,stride=2)
        )
        self.conv3 = nn.Sequential(
            nn.Conv2d(in_channels=48,out_channels=64,kernel_size=2),nn.PReLU(),
        )
        self.flatten = nn.Flatten() # 展平
        # 全连接层
        self.fc = nn.Linear(in_features=576, out_features=128)
        # 三个输出层
        self.class_fc = nn.Linear(in_features=128, out_features=2)
        self.bbox_fc = nn.Linear(in_features=128, out_features=4)
        self.landmark_fc = nn.Linear(in_features=128, out_features=10)

        for m in self.modules():
            print(f"m的值:{m}")
            if isinstance(m,nn.Conv2d):
                nn.init.kaiming_normal(m.weight,mode='fan_out', nonlinearity="relu")

    def forward(self, x):
        # 三层卷积 一层全连接
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        # 展平
        x = self.flatten(x)
        x = self.fc(x)
        # 分类是否人脸的全连接输出层
        class_out = self.class_fc(x)
        # 人脸box的回归全连接输出层
        bbox_out = self.bbox_fc(x)
        # 5个关键点的回归全连接输出层
        landmark_out = self.landmark_fc(x)
        return class_out, bbox_out, landmark_out


        pass

ONet.py

import torch
import torch.nn as nn

class ONet(nn.Module):
    def __init__(self):
        super(ONet, self).__init__()
        self.conv1 = nn.Sequential(
            nn.Conv2d(3,32,3),nn.PReLU(),
            nn.MaxPool2d(3,2,ceil_mode=True)
        )
        self.conv2 = nn.Sequential(
            nn.Conv2d(32, 64, 3), nn.PReLU(),
            nn.MaxPool2d(3,2,ceil_mode=True)
        )
        self.conv3 = nn.Sequential(
            nn.Conv2d(64,64,3),nn.PReLU(),
            nn.MaxPool2d(2,2,ceil_mode=True)
        )
        self.conv4 = nn.Sequential(
            nn.Conv2d(64, 128, 2),nn.PReLU()
        )
        self.flatten = nn.Flatten() # 展平
        # 全连接层
        self.fc = nn.Linear(in_features=1152, out_features=256)
        # 三个输出层
        self.class_fc = nn.Linear(256, 2)
        self.bbox_fc = nn.Linear(256, 4)
        self.landmark_fc = nn.Linear(256, 10)

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = self.conv4(x)
        x = self.flatten(x)
        x = self.fc(x)
        # 分类是否人脸的 全连接输出层
        class_out = self.class_fc(x)
        # 人脸box的回归 全连接输出层
        bbox_out = self.bbox_fc(x)
        # 5个关键点的回归 全连接输出层
        landmark_out = self.landmark_fc(x)
        return class_out, bbox_out, landmark_out

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LiangGaRy-学习笔记-Day12

1、作业回顾 1.1、判断磁盘利用率 要求&#xff1a; 判断磁盘的使用率&#xff0c;如果超过了90%就警告 [rootNode1 sh]# vim disk_check.sh #!/bin/bash #Author By LiangGaRy #2023年5月9日 #Usage:检测硬盘的使用率 ########################################### #定义一…

蓝奥声核心技术分享——用电插座的安全保护技术(安全计量插座)

1.技术背景 用电插座的安全保护技术主要针对在用电负载接入接出&#xff08;即插拔&#xff09;用电插座的过程&#xff0c;解决瞬态异常监控及安全保护问题。该项技术涉及物联网智能硬件设备与测控技术领域&#xff0c;尤其涉及电能信号监测与用电安全监控的技术领域。 随着…

【Redis高级应用】分布式缓存

文章目录 单机Redis存在的问题Redis持久化RDB持久化执行时机RDB原理 AOF持久化AOF原理AOF配置AOF文件重写 RDB与AOF对比 Redis主从搭建主从架构主从数据同步原理全量同步增量同步repl_backlog原理 主从同步优化小结 Redis哨兵哨兵原理集群结构和作用集群监控原理集群故障恢复原…

MySQL多列字段去重的案例实践

同事提了个需求&#xff0c;如下测试表&#xff0c;有code、cdate和ctotal三列&#xff0c; select * from tt;现在要得到code的唯一值&#xff0c;但同时带着cdate和ctotal两个字段。 提起"唯一值"&#xff0c;想到的就是distinct。distinct关键字可以过滤多余的重…

Machine Learning-Ex8(吴恩达课后习题)Anomaly Detection and Recommender Systems

1. Anomaly detection 内容&#xff1a;使用高斯模型来检测数据集中异常的数据&#xff08;概率低的&#xff09;&#xff0c;先在2维数据中进行实验。样本具有两个特征&#xff1a;a. 服务器响应的吞吐量&#xff08;mb/s&#xff09; b. 延迟&#xff08;ms&#xff09;。 …

【linux网络】正则表达式

一、正则表达式 1.1作用范围 通常用于判断语句中&#xff0c;用来检查某一个字符串是否满足某一格式 1.2正则表达式的组成 普通字符包括大小写字母、数字、标点符号及其它符号元字符元字符是指在正则表达式中具有特殊意思的专用字符&#xff0c;可以用来规定其导字符&#…

【P18】JMeter JSON JMESPath Extractor

文章目录 一、准备工作二、测试计划设计 一、准备工作 慕慕生鲜&#xff1a; http://111.231.103.117/#/login 进入网页后&#xff0c;登录&#xff0c;页面提供了账户和密码 搜索框输入“虾” 右键检查或按F12&#xff0c;打开调试工具&#xff0c;点击搜索 二、测试计划设…

Bean的存取、五大注解、对象的注入方式、Bean的作用域和生命周期

一、Bean 的创建、存储和使用 PS&#xff1a;Java语言中的对象也叫作 Bean。 1、创建一个maven项目 PS&#xff1a;要在 pom.xml 中添加 spring 框架支持 PS&#xff1a;引入 lombok 依赖&#xff08;可以帮助实现 get 和 set 方法&#xff09; 2、存对象 2.1、创建类 启…

Linux 蜂鸣器驱动实验

蜂鸣器驱动原理 ①、在设备树中添加 SNVS_TAMPER1 引脚的 pinctrl 信息。 ②、在设备树中创建蜂鸣器节点&#xff0c;在蜂鸣器节点中加入 GPIO 信息。 1、修改设备树文件 添加 pinctrl 节点 I.MX6U-ALPHA开发板上的BEEP使用了SNVS_TAMPER1这个PIN&#xff0c;打开imx6ull-alien…

【01】C++的第一个程序Hello World

C的第一个应用程序&#xff08;Hello World程序&#xff09; 引言一、代码二、代码解释三、注意事项总结 引言 &#x1f4a1; 作者简介&#xff1a;专注于C/C高性能程序设计和开发&#xff0c;理论与代码实践结合&#xff0c;让世界没有难学的技术。 &#x1f449; &#x1f39…

报表生成工具Stimulsoft Reports.JS如何减少产品脚本的加载时间

Stimulsoft Reports 是一款报告编写器&#xff0c;主要用于在桌面和Web上从头开始创建任何复杂的报告。可以在大多数平台上轻松实现部署&#xff0c;如ASP.NET, WinForms, .NET Core, JavaScript, WPF, Angular, Blazor, PHP, Java等&#xff0c;在你的应用程序中嵌入报告设计器…

Django框架之视图HttpResponse 对象

本篇文章主要内容为&#xff1a;视图中HttpResponse对象的属性、方法及json、redirect子类包含使用cookie使用、跳转、json返回的示例。 概述 HttpResponse对象是对用户访问的响应&#xff0c;与HttpRequest对象由django创建&#xff0c;HttpResponse对象是由开发人员创建。Ht…

001+limou+MySQL的基础命令

0.前言 您好&#xff0c;这里是limou3434的一篇个人博文&#xff0c;感兴趣的话您也可以看看我的其他文章。本博文是借鉴于李小威前辈所著的书籍《SQL 基础教程》所成的博文笔记&#xff0c;这本书真的很适合新手学习数据库相关的内容。本次我想给您带来的是关于MySQL的一些基…

网站神奇工具Viewport Resizer,支持手机、pad和电脑等不同尺寸大小

标题&#xff1a;Viewport Resizer&#xff1a;让网站适应不同设备的神奇工具&#xff01; 导语&#xff1a; 在互联网世界中&#xff0c;我们常常需要在不同设备上浏览网站。为了让用户在手机、平板或电脑上都能看到美观易用的页面&#xff0c;网站开发者们努力优化网站的适应…

qemu-虚拟机

qemu 官网下载地址 https://www.qemu.org/ 跨平台虚拟机&#xff0c;类型vmware&#xff0c;执行效率比vmware高 官方参考文档&#xff1a;https://www.qemu.org/docs/master/system/introduction.html kvm&#xff0c;轻量级虚拟机&#xff0c;可以加速qemu的执行 qemu-e…

微信云开发技术架构

&#xff08;仅有把抱怨环境的情绪&#xff0c;化为上进的力量&#xff0c;才是成功的保证。——罗曼罗兰&#xff09; 微信云开发 官方文档 文本只用来概述微信云开发的技术架构&#xff0c;并结合实战场景进行说明。更详细的请直接查看微信官方文档。 背景 微信云开发是微…

Oracle11g全新讲解之游标

游标 游标的作用&#xff1a;处理多行数据&#xff0c;类似与java中的集合 1.隐式游标 一般是配合显示游标去使用的&#xff0c;不需要显示声明&#xff0c;打开&#xff0c;关闭&#xff0c;系统自定维护,名称为&#xff1a;sql 常用属性&#xff1a; sql%found:语句影响了…

快速分隔文件(split),合并文件(paste)的命令;eval(先扫描输出在执行)命令

split快速分割文件&#xff0c;paste快速合并文件&#xff1b;eval命令 split命令快速分隔文件paste命令快速合并文件eval命令 split命令快速分隔文件 语法格式&#xff1a; split 【选项】 参数 原始文件 拆分后文件名前缀 常用选项 -l&#xff1a;以行数拆分 -b&#xf…

7.对象模型

对象模型 信号和槽 信号和槽是一种用于对象之间通信的机制。信号是对象发出的通知&#xff0c;槽是用于接收这些通知的函数。 当对象的状态发生变化时[按钮被点击]&#xff0c;它会发出一个信号[clicked()]&#xff0c;然后与该对象连接的槽函数将被自动调用。 若某个信号与多…

图像分割之SAM(Segment Anything Model)

论文&#xff1a;Segment Anything Github&#xff1a;https://github.com/facebookresearch/segment-anything 论文从zero-shot主干网络的基础出发&#xff0c;提出了SAM&#xff08;Segment Anything Model&#xff09;模型。该模型有别于传统的分割模型。传统分割模型只能输…