yolov1笔记

论文地址:https://arxiv.org/pdf/1506.02640.pdf

Tensorflow版本yolo v1:https://github.com/gliese581gg/YOLO_tensorflow

参考博客

https://blog.csdn.net/m0_37192554/article/details/81092514

https://blog.csdn.net/qq_27825451/article/details/88941474

所要解决的问题:对象识别和定位

输入一张图片,要求输出其中所包含的对象的类别----(识别),以及每个对象的位置(就是包含对象的矩形框)--定位.

对象识别和定位,可以看成两个任务:

(1)找到图片中某个存在对象的区域,

(2)识别出该区域中具体是哪个对象.

如何做?

最简单的想法,

step 1:遍历图片中所有可能的位置(448x448=200704, 共20多万个位置)

step2: 在每个位置构造不同尺寸(就是宽高)的矩形,----这个不同尺寸大小就很难刻画了,无穷多的尺寸啊,比如现在常规的9种类型

step3: 判断下上面得到的矩形中有没有物体,若有,说下是啥物体,狗还是猫?

经过3个步骤,任务完成,但是,这个计算量200704*9=1806336(180万左右的矩形框啊),累不累?这效率太低了,蛮干啊!!!

RCNN 系如何做?

这个系列开创性的提出了候选区(region proposals)的方法,先从图片中搜索出一些可能存在对象的候选区(selective search),大概2000个左右,然后对这2000个候选区进行对象识别,大幅提升了对象识别和定位的效率.

YOLO如何做?

1. 把图片划分成SXS=7*7=49个网格,每个网格(grid)预测B=2个box矩形框和c个(具体多少个类别,根据实际需要决定,文中c=20)类别,这样对一张448x448图片总共预测49*2=98个bouding box,可以理解为98个候选区.

note:

1. 相比于原来需要判断180万左右的矩形框,这里我们只需要搞定98个框,这任务量可是断崖般下降啊.

2. 实际上,yolo并没有真正去掉候选区,而是采用了预定义的候选区,也就是98个框,它们很粗略的覆盖了图片的整个区域,

3. rcnn虽然会找到一些候选区,但毕竟是候选,等真正识别出其中的对象以后,还要对候选区进行微调,使之更接近真实的bounding box, 这个过程就是边框回归:将候选区bounding box调整到更接近真实的bounding box.既然反正最后都是要调整的,干嘛还要先费劲去找候选区呢?大致有个范围就行了,所以yolo就这么干了.------去掉候选区这个步骤.

yolo实现方案

将这个模型简单化为:

1.其实前面就是提取特征后,后面为7*7*30的输出,下面说一下模型输入输出,模型的输出为什么是7*7*30呢?

首先来看7×7表示的意思

其实对于每一个grid代表了(448/7)的区域表达,当然这里的7*7也是可以调节大小的,那30又是如何形成的通道大小呢?

A:2个bounding box的位置(8个通道)

     每个bouding box需要4个数值来表示其位置,(center_x,center_y,width,height),即bounding box的中心点的(x,y)坐标,boudning box的宽度,高度),2个bouding box共需要8个数值来表示其位置。

B:2个bounding box的置信度(2个通道)

    bouding box的置信度=该bounding box内存在对象的概率×该bounding box与该对象实际

  score=pr(class_i|object)*pr(object)*IOU_{pred}^{truth}=pr(class_i)*IOU_{pred}^{truth}

c:20分类概率(20个通道)

   下面我们来说一下剩下20维度的分类通道,每一个通道代表一个类别的分类概率,因为yolo支持识别20种不同的对象(人,鸟,猫等),所以这里有20个值表示该网格位置存在人和一种对象的概率。

因为只是一些常规的神经网络结构,所以,理解yolo的设计的时候,重要的是理解输入和输出的映射关系,

1.首先将图片resize到448x448,。主要是因为YOLO的网络中,卷积层最后接了两个全连接层,全连接层是要求固定大小的向量作为输入,所以倒推回去也就要求原始图像有固定的尺寸。那么YOLO设计的尺寸就是448*448。

2。将上述448x448的图片划分为SXS的网格(S=7),这里可以认为网络从一个448*448的原始图片提取出了一个7*7的特征图,如下图所示,从7*7特征图的角度来看原始的448x448图片,可以看成将448x448的图片分成了7*7个grid,每个小grid中有448/7=64,64x64个像素,物体的中心落在哪一个网格内,这个网格就负责预测该物体的置信度,类别以及位置。如下图

è¿éåå¾çæè¿°

 

3.网络最后的输出是 S×S×(B*5+C) 的数据块 ,其中,对于一个box,其负责5个预测值(cent_x,cent_y,w,h, p),每个grid对应一个B*5+C维张量输出.

x,y:box的中心坐标,注意这里是相对于自身网格的中心坐标,范围0-1,相对于是什么意思呢?相对于的体现在:比如图片尺寸448×448,一个box在这张图片上的box中心点位置为(120,220),可以计算得到这个box位于第3行第2列的grid中,我们可以称为第 16个grid, 则相对于这个grid,中心点位置为(120,220)的box相对于这个grid做偏移,error=(box-grid)/448

w,h:box的宽,高, 这里是相对于整张图片的宽高,w,h,所以范围也是0-1,相对于的体现在:比如图片尺寸448×448,一个box在这张图片上的宽=100,高=200,则相对于之后,这个box的宽=100/448=0.223, 这个box的高=200/448=0.446

p:每一个格子包含物体的置信度confidence=P_r(object)*IOU_{pred}^{truth}

C:格子中是某一个物体的概率probability;

备注:训练开始阶段,网络预测的bounding box可能都是乱来的.

这个30维的向量包含了那些信息呢?

问题:

1.置信度是刻画什么呢?是说这个box中有狗的概率值?还是这个box的位置与groud_truth 之间的iou较大?

答:这个置信度confidence scores反映了模型对于这个box的预测,包含了两个方面

    (1):该box里是否含有物体P_r(object),注意这里是不管什么类别的,即不管是狗还是猫的,先看下有检测目标的概率.

     (2):以及这个box的坐标预测的有多准IOU_{pred}^{truth},(多准是通过iou定义的,iou越大,说明越准,iou越小说明越不准).

置信度计算公式为:confidence=P_r(object)*IOU_{pred}^{truth},这个公式是什么意思呢?

若这个box中不存在检测目标,即P_r(object)=0,既然都不存在检测目标,自然不存在什么iou了,则confidence score自然=0,如果存检测目标的话,则P_r(object)=1,此时confidence score则为预测box与groud truth box之间的iou.

更具体的来讲: 若box中存在检测目标,则期望其置信度confidence score=iou.

因此: 本质上来讲,置信度刻画的是预测box与groud truth box之间的iou,没有去管这个box中是否有狗.

① 20个对象分类的概率
因为YOLO支持识别20种不同的对象(人、鸟、猫、汽车、椅子等),所以这里有20个值表示该网格grid位置存在任一种对象的概率。

P(C_1|object),......,P(C_i|object),......P(C_{20}|object)

,之所以写成条件概率,意思是如果该grid网格存在一个对象Object,那么它是类别c_i的概率是P(C_i|object).

② 2个bounding box的位置
每个bounding box需要4个数值来表示其位置,(Center_x,Center_y,width,height),即(bounding box的中心点的x坐标,y坐标,bounding box的宽度,高度),2个bounding box共需要8个数值来表示其位置。

③ 2个bounding box的置信度
bounding box的置信度 = 该bounding box内存在对象的概率 * 该bounding box与该对象实际bounding box的IOU
用公式来表示就是

confidence=P_r(object)*IOU_{pred}^{truth}

P_r(object)是bounding box内存在对象的概率,区别于上面第①点的 P(C_i|object)P_r(object)并不管是哪个对象,它体现的是 有或没有对象的概率。第①点中的P(C_i|object)意思是假设已经有检测目标在box中了,这个对象具体是哪一类的概率。IOU_{pred}^{truth}是预测的 bounding box 与 对象真实bounding box 的IOU(Intersection over Union,交并比)。要注意的是,现在讨论的30维向量中的bounding box是YOLO网络的输出,也就是预测的bounding box。所以体现了预测的bounding box与真实bounding box的接近程度。
还要说明的是,虽然有时说"预测"的bounding box,但这个IOU是在训练阶段计算的。等到了测试阶段(Inference),这时并不知道真实对象在哪里,只能完全依赖于网络的输出,这时已经不需要(也无法)计算IOU了,也就是说网络会自己预测一个值,这个值是如何计算得到的,是网络根据自身的权重和一系列激活函数计算出来的,我们给它起名字叫做置信度,并且期望这个值=P_r(object)*IOU_{pred}^{truth}.

另外论文中经常提到responsible。比如:Our system divides the input image into an S*S grid. If the center of an object falls into a grid cell, that grid cell is responsible for detecting that object. 这个 responsible 有点让人疑惑,对预测"负责"是啥意思。其实没啥特别意思,就是一个Object只由一个grid来进行预测,不要多个grid都抢着预测同一个Object。更具体一点说,就是在设置训练样本的时候,样本中的每个Object归属到且仅归属到一个grid,即便有时Object跨越了几个grid,也仅指定其中一个。具体就是计算出该Object的bounding box的中心位置,这个中心位置落在哪个grid,该grid对应的输出向量中该对象的类别概率是1(该gird负责预测该对象),所有其它grid对该Object的预测概率设为0(不负责预测该对象)。

备注:这里为什么要对每一个grid预测B个box(x,y,w,h,confidence),由于每个单元格grid预测多个边界框box(即文中的B,B取值为2)。但是其对应类别只有一个。那么在训练时,如果该单元格内确实存在目标,那么只选择与ground truth的IOU最大的那个边界框box来负责预测该目标,而其它边界框认为不存在目标。这样设置的一个结果将会使一个单元格grid对应的边界框box更加专业化,其可以分别适用不同大小,不同高宽比的目标,从而提升模型性能.

     3.2 什么是物体的概率probability?

每一个栅格grid还要预测C个 conditional class probability(条件类别概率),即Pr(Classi|Object)。即在一个grid栅格包含一个Object的前提下,它属于某个类的概率。 作者在VOC上实验,所以C取为20(这里为什么不是类似Fast RCNN的21类,因为是否为背景,作者放到了上面的置信度confidence score中)

    3.1.3 每一个grid的输出数据维度的对应关系

通过上面的3.1和3.2的说明,我们对每一个grid的预测会产生下面一些数据

上图展示的是那个“红色的grid”预测的两个不同的“黄色边框”box,每一个边框box所携带的信息是5维,故而产生了10维向量。

再参考一个图片如下:

上图中前面的两个红色圈圈就不说了,表示的每一个grid预测的两个box的信息,共10维,后面的20维是每一个类别的概率,最后一共组成30维的向量。

讨论
① 一张图片最多可以检测出7x7=49个对象
每个30维向量中只有一组(20个)对象分类的概率,也就只能预测出一个对象。所以输出的 7*7=49个 30维向量,最多表示出49个对象。

② 总共有 49*2=98 个候选区(bounding box)
每个30维向量中有2组bounding box,所以总共是98个候选区

4. yolo网络的设计架构

Yolo采用卷积网络来提取特征,然后使用全连接层来得到预测值。网络结构参考GoogLeNet分类网络结构模型(灵感来源于GoogLeNet,但是并没有采取其inception的结构),使用1x1卷积层(此处1x1卷积层的存在是为了跨通道信息整合)+3x3卷积层简单替代,包含24个卷积层和2个全连接层,完整的网络结构如图所示

YOLO的结构非常简单,就是单纯的卷积、池化最后加了两层全连接。单看网络结构的话,和普通的CNN对象分类网络几乎没有本质的区别,最大的差异是最后输出层用线性函数做激活函数,因为需要预测bounding box的位置(数值型),而不仅仅是对象的概率。所以粗略来说,YOLO的整个结构就是输入图片经过神经网络的变换得到一个输出的张量,如下图所示。


 

我们通过开头的tensorflow版本的实现来看下每层如何展现的

def build_networks(self):
		if self.disp_console : print "Building YOLO_small graph..."
		self.x = tf.placeholder('float32',[None,448,448,3])
		self.conv_1 = self.conv_layer(1,self.x,64,7,2) #卷积层1,def conv_layer(self,idx,inputs,filters,size,stride),这里卷积核shape=[7,7,64],stride=2,same方式,卷积后的输出维度=[224,224,64]
		self.pool_2 = self.pooling_layer(2,self.conv_1,2,2)#池化层1,本质同卷积层一样,因为步长=2,输出的维度降低一半=[112,112,2]
		self.conv_3 = self.conv_layer(3,self.pool_2,192,3,1) #卷积层2,卷积核=[3,3,192]卷积后的输出维度=[112,112,192]
		self.pool_4 = self.pooling_layer(4,self.conv_3,256,2,2)#池化层2,卷积核=[2,2,2]因为步长=2,输出的维度降低一半=[56,56,256]
		self.conv_5 = self.conv_layer(5,self.pool_4,128,1,1) #卷积层3,卷积核=[1,1,128]步长=1,卷积后的输出维度=[56,56,256]
		self.conv_6 = self.conv_layer(6,self.conv_5,256,3,1) #卷积层4,卷积核=[3,3,256],步长=1,卷积后的输出维度=[56,56,256]
		self.conv_7 = self.conv_layer(7,self.conv_6,256,1,1) #卷积层5,卷积核=[1,1,256],步长=1,卷积后的输出维度=[56,56,256]
		self.conv_8 = self.conv_layer(8,self.conv_7,512,3,1) #卷积层6,卷积核=[3,3,512],步长=1,卷积后的输出维度=[56,56,512]
		self.pool_9 = self.pooling_layer(9,self.conv_8,2,2)#池化层3,卷积核=[2,2,2]因为步长=2,输出的维度降低一半=[28,28,512]
--------------------------第一块------------------------------------------------
		self.conv_10 = self.conv_layer(10,self.pool_9,256,1,1)  #卷积层7
		self.conv_11 = self.conv_layer(11,self.conv_10,512,3,1)  #卷积层8
-------------------------第二块--------------------------------------------------

		self.conv_12 = self.conv_layer(12,self.conv_11,256,1,1)  #卷积层9
		self.conv_13 = self.conv_layer(13,self.conv_12,512,3,1) #卷积层10
------------------------第三块------------------------------------------------

		self.conv_14 = self.conv_layer(14,self.conv_13,256,1,1) #卷积层11
		self.conv_15 = self.conv_layer(15,self.conv_14,512,3,1) #卷积层12
-----------------------第四块-----------------------------------------------

		self.conv_16 = self.conv_layer(16,self.conv_15,256,1,1) #卷积层13
		self.conv_17 = self.conv_layer(17,self.conv_16,512,3,1) #卷积层14
----------------------------------------------------------------------------

		self.conv_18 = self.conv_layer(18,self.conv_17,512,1,1) #卷积层15,1x1x512
		self.conv_19 = self.conv_layer(19,self.conv_18,1024,3,1) #卷积层16,3x3x1024
		self.pool_20 = self.pooling_layer(20,self.conv_19,2,2)#池化层,输出维度[14,14,1024]
````````````````````````````第一块``````````````````````````````````````````
		self.conv_21 = self.conv_layer(21,self.pool_20,512,1,1) #卷积层17
		self.conv_22 = self.conv_layer(22,self.conv_21,1024,3,1) #卷积层18
````````````````````````````第二块``````````````````````````````````````````````````
		self.conv_23 = self.conv_layer(23,self.conv_22,512,1,1) #卷积层19
		self.conv_24 = self.conv_layer(24,self.conv_23,1024,3,1) #卷积层20
```````````````````````````````````````````````````````````````````````````````````
		self.conv_25 = self.conv_layer(25,self.conv_24,1024,3,1) #卷积层21,3x3x1024
		self.conv_26 = self.conv_layer(26,self.conv_25,1024,3,2) #卷积层22, 3x3x1024,步长=2,维度降低一半,输出维度=[7,7,1024]
		self.conv_27 = self.conv_layer(27,self.conv_26,1024,3,1) #卷积层23,3x3x1024
		self.conv_28 = self.conv_layer(28,self.conv_27,1024,3,1) #卷积层24,3x3x1024
		self.fc_29 = self.fc_layer(29,self.conv_28,512,flat=True,linear=False)#
		self.fc_30 = self.fc_layer(30,self.fc_29,4096,flat=False,linear=False)
		#skip dropout_31
		self.fc_32 = self.fc_layer(32,self.fc_30,1470,flat=False,linear=True)
		self.sess = tf.Session()
		self.sess.run(tf.initialize_all_variables())
		self.saver = tf.train.Saver()
		self.saver.restore(self.sess,self.weights_file)
		if self.disp_console : print "Loading complete!" + '\n'

5. 损失函数

yolov1的损失函数全是均方误差,需要理解的是其含义。

(1) 为了平衡坐标预测误差和分类误差,yolo对坐标误差乘上了一个系数\lambda _{coord}=5,以增加坐标回归误差所占的比重;

(2)为了平衡有目标的box和没有目标的box(正负样本),对负样本box乘了系数\lambda _{noobd}=0.5,即相对含有目标的正样本box来讲,不含目标的负样box对损失函数的贡献较小,这是因为实际上来讲,98个box中,大部分的box都不含目标,即负样本的数量较多,应该抑制这些负样本的影响.

(3)为了在一定程度上区别对待大物体和小物体(相对于大物体,小物体宽高偏移一点会引起更大的iou误差),yolo用开根的方法缓解.

举例来说,大小为10和大小为100的目标,预测大小分别为20和110,损失一样(都是10),但是显然小目标检测的更差一些,开根后,

(\sqrt{20}-\sqrt{10})^2=1.7, (\sqrt{110}-\sqrt{100})^2=0.24,相当于强化了小目标的wh的损失。

4)第3行是存在对象的bounding box的置信度误差。带有意味着只有"负责"(IOU比较大)预测的那个bounding box的置信度才会计入误差。就像前面所说的,分成grid cell包含与不包含object两种情况。这里注意下因为每个grid cell包含两个bounding box,所以只有当ground truth 和该网格中的某个bounding box的IOU值最大的时候,才计算这项。
5)第五行表示预测类别的误差,注意前面的系数只有在grid cell包含object的时候才为1。

所以具体实现的时候是什么样的过程呢?

训练的时候:输入N个图像,每个图像包含M个object,每个object包含4个坐标(x,y,w,h)和1个label。然后通过网络得到7*7*30大小的三维矩阵。每个1*30的向量前5个元素表示第一个bounding box的4个坐标和1个confidence,第6到10元素表示第二个bounding box的4个坐标和1个confidence。最后20个表示这个grid cell所属类别。注意这30个都是预测的结果。然后就可以计算损失函数的第一、二 、五行。至于第二三行,confidence可以根据ground truth和预测的bounding box计算出的IOU和是否有object的0,1值相乘得到。真实的confidence是0或1值,即有object则为1,没有object则为0。 这样就能计算出loss function的值了。

一个grid cell中是否有object怎么界定? 
首先要明白grid cell的含义,以文中7*7为例,这个size其实就是对输入图像(假设是448*448)不断提取特征然后sample得到的(缩小了64倍),然后就是把输入图像划分成7*7个grid cell,这样输入图像中的64个像素点就对应一个grid cell。回归正题,那么我们有每个object的标注信息,也就是知道每个object的中心点坐标在输入图像的哪个位置,那么不就相当于知道了每个object的中心点坐标属于哪个grid cell了吗,而只要object的中心点坐标落在哪个grid cell中,这个object就由哪个grid cell负责预测,也就是该grid cell包含这个object。另外由于一个grid cell会预测两个bounding box,实际上只有一个bounding box是用来预测属于该grid cell的object的,因为这两个bounding box到底哪个来预测呢?答案是:和该object的ground truth的IOU值最大的bounding box。

 

6.训练

 

YOLO的最后一层采用线性激活函数,其它层都是Leaky ReLU。训练中采用了drop out和数据增强(data augmentation)来防止过拟合。

leaky_relu

\phi (x)=\left\{\begin{matrix} x, &if ~~x>0 \\ 0.1x,& otherwise \end{matrix}\right. 

1.训练数据的准备

训练数据是指送入训练的每一个batch,一般都是(imgs_tensor,target_tensor),img部分需要做一些数据增强,比如颜色抖动,随机裁剪,平移变换,水平反转,做完数据增强最好可视化一下输出结果,确保box_label经过变换后是正确的。数据增强完后,根据box_label进行编码(就是按照yolo的思想生成S×S×30S×S×30 的数据块作为target,含有目标的confidence先设置为1,用于区别不含目标的,IOU target在线计算)。

 

7.预测

(inference)
训练好的YOLO网络,输入一张图片,将输出一个 7*7*30 的张量(tensor)来表示图片中所有网格包含的对象(概率)以及该对象可能的2个位置(bounding box)和可信程度(置信度)。测试时候输出的scores等于

Pr(Class_i|Object)*Pr(Object)*IOU_{pred}^{truth}=Pr(Class_i)*IOU_{pred}^{truth}

既可以反映分类的准确率,又可以反映定位的准确率.(这个可以从程序实现中看出来)

    for i in range(2):#因为有2个box,这里对两个box进行遍历
        for j in range(20):#一个grid有20个类别的得分,对每个类别得分进行遍历
            probs[:, :, i, j] = np.multiply(class_probs[:, :, j], scales[:, :, i])#概率得分score=类别概率*box的置信度=Pr(class_i)*iou,此处一个grid对应了40*2=80个概率得分
            #一共有7*7=49个grid,所以共有49*40=1960个得分

为了从中提取出最有可能的那些对象和位置,YOLO采用NMS(Non-maximal suppression,非极大值抑制)算法。

8)NMS(非极大值抑制)
NMS方法并不复杂,其核心思想是:选择得分最高的box作为输出,与该box的iou>阈值的其他box舍弃掉,不断重复这一过程直到所有box处理完。

YOLO的NMS计算方法如下。
网络输出的7*7*30的张量,在每一个网格grid中,对象位于第j个bounding box的得分:代表着某个对象存在于第j个bounding box的可能性。

每个网格grid有:20个对象的概率*2个bounding box的置信度,共40个得分。49个网格共49*40=1960个得分。Andrew Ng建议每种对象分别进行NMS,那么每种对象有 1960/20=98 个得分。

NMS步骤如下:
1)设置一个Score的阈值,低于该阈值的候选对象排除掉(将该Score设为0)
2)遍历每一个对象类别
 2.1)遍历该对象的98个得分
  2.1.1)找到Score最大的那个对象及其bounding box,添加到输出列表
  2.1.2)对每个Score不为0的候选对象,计算其与上面2.1.1输出对象的bounding box的IOU
  2.1.3)根据预先设置的IOU阈值,所有高于该阈值(重叠度较高)的候选对象排除掉(将Score设为0)
  2.1.4)如果所有bounding box要么在输出列表中,要么Score=0,则该对象类别的NMS完成,返回步骤2处理下一种对象
3)输出列表即为预测的对象

代码实现:

import numpy as np
w_img=448
h_img=448
threshold = 0.2

def interpret_output(output):
    probs = np.zeros((7, 7, 2, 20))
    class_probs = np.reshape(output[0:980], (7, 7, 20))#网络输出是7*7*30, 这里取出了7*7*20,就是类别预测值Pr(class_i)
    scales = np.reshape(output[980:1078], (7, 7, 2))#7*7*20--7*7*22=1078为两个box的置信度,本质上是iou,当然了,在预测阶段是没有iou的,但因为我们的回归
    #目标是让这个值回归到iou,所以理想情况下,这个可以认为是iou
    boxes = np.reshape(output[1078:], (7, 7, 2, 4))#后面的就是两个box的(x,y,w,h),
    offset = np.transpose(np.reshape(np.array([np.arange(7)] * 14), (2, 7, 7)), (1, 2, 0))

    boxes[:, :, :, 0] += offset
    boxes[:, :, :, 1] += np.transpose(offset, (1, 0, 2))
    boxes[:, :, :, 0:2] = boxes[:, :, :, 0:2] / 7.0#相当于把预测的box的中心均匀分布到原始图像上,并归一化为0-1之间的值
    boxes[:, :, :, 2] = np.multiply(boxes[:, :, :, 2], boxes[:, :, :, 2])#因为预测的是sqrt(w),sqrt(h),这里再平方回去
    boxes[:, :, :, 3] = np.multiply(boxes[:, :, :, 3], boxes[:, :, :, 3])

    boxes[:, :, :, 0] *=  w_img#还原到原始图片的尺寸*448,(当然原始图片若不是448,比如是720,这里是乘以原始图片的尺寸
    boxes[:, :, :, 1] *=  h_img
    boxes[:, :, :, 2] *=  w_img
    boxes[:, :, :, 3] *=  h_img

    for i in range(2):#因为有2个box,这里对两个box进行遍历
        for j in range(20):#一个grid有20个类别的得分,对每个类别得分进行遍历
            probs[:, :, i, j] = np.multiply(class_probs[:, :, j], scales[:, :, i])#概率得分score=类别概率*box的置信度=Pr(class_i)*iou,此处一个grid对应了40*2=80个概率得分
            #一共有7*7=49个grid,所以共有49*40=1960个得分

    filter_mat_probs = np.array(probs >= threshold, dtype='bool')#找出那些概率得分>0.2的值
    filter_mat_boxes = np.nonzero(filter_mat_probs)
    boxes_filtered = boxes[filter_mat_boxes[0], filter_mat_boxes[1], filter_mat_boxes[2]]
    probs_filtered = probs[filter_mat_probs]
    classes_num_filtered = np.argmax(filter_mat_probs, axis=3)[
        filter_mat_boxes[0], filter_mat_boxes[1], filter_mat_boxes[2]]

    argsort = np.array(np.argsort(probs_filtered))[::-1]
    boxes_filtered = boxes_filtered[argsort]
    probs_filtered = probs_filtered[argsort]
    classes_num_filtered = classes_num_filtered[argsort]

    for i in range(len(boxes_filtered)):
        if probs_filtered[i] == 0: continue
        for j in range(i + 1, len(boxes_filtered)):
            if  iou(boxes_filtered[i], boxes_filtered[j]) >iou_threshold:#本质是执行nms
                probs_filtered[j] = 0.0

    filter_iou = np.array(probs_filtered > 0.0, dtype='bool')
    boxes_filtered = boxes_filtered[filter_iou]
    probs_filtered = probs_filtered[filter_iou]
    classes_num_filtered = classes_num_filtered[filter_iou]

    result = []
    for i in range(len(boxes_filtered)):
        result.append(
            [self.classes[classes_num_filtered[i]], boxes_filtered[i][0], boxes_filtered[i][1], boxes_filtered[i][2],
             boxes_filtered[i][3], probs_filtered[i]])

    return result

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值