FaceNet

宏观理解 我们之前说过的算法都是人脸检测,比如Faster R-CNN和MTCNN。可以在一张图片中找出人脸的位置,但是我们常见的人脸识别系统并不能只依赖于人脸检测,我们还需要一个步骤就是人脸识别。 人脸识别大致有两种,第一种是给出一张人脸来对比是否和另一张一样,第二种是给出一张人脸来判断是否在一个数据库里。这样我们把人脸检测和识别的算法结合在一起就是一个完整的人脸识别系统了,这个流程类似这样: 现在我们就介绍一种可以识别的算法FaceNet。FaceNet的作用在于给出两张照片,它用欧式距离来判断这两张照片的人是不是同一个人。比如下面这个图中,我们规定两张照片的欧式距离小于1.05的判断为同一个人,所以左右都为同一个人但是上下都大于1.05判断为不同的两个人。 其实是一个很简单的过程,先把人脸embedding然后计算两个编码的欧式距离就好,那么这篇论文贡献的就是一个新的loss function来使相同的人脸距离越来越近,不同的越来越远。 (注:那么为什么要用欧式距离而不是用cos距离来做呢?因为如果用cos的话进行N:N的比对需要O^2的复杂度,但是如果用欧式距离的KD-TREE之类的数据结构可以很大的提高效率。)   微观分析 这种新颖的loss叫triplet loss。我们先随机拿出一张照片叫anchor。然后我们再随机从标注好的同一个人的照片中再拿出一个叫positive(可能翻译成正样本?),最后从不是同一个人脸的数据中随机拿一个叫做negative(就叫负样本吧)。然后这三张照片通过embedding之后,交给triplet loss计算出一个损失。整个过程就是这样的。 这个损失在优化完我们当然希望anchor和positive的距离越来越近,anchor和negative的距离越来越远。所以我们的目标就像这张图: 为了更好的应用,我们还可以在这个loss上加上一个超参数α当作margin,就像SVM一样,不仅要找出一个答案,我们还要这个答案变得尽量最好。我们定义一下anchor为A,negative为N, positive为P。所以我们写成 ||F(A) – F(P)|| + α < || F(A) – F(N) || 。F我们可以理解为embedding的函数。这样在训练完可以让我们的算法更加有鲁棒性。 现在我们稍微移项一下就有了我们的loss = ||F(A) – F(P)|| – || F(A) – F(N) ||+ α 在训练过程中,因为每个样本都是由一个三元组组成的,如果我们把数据集中的所有三元组都先找出来再全部来做loss,这样的复杂度异常的高。所以我们选择在每次的一个batch中,寻找有哪些组合是满足||F(A) – F(P)|| + α < || F(A) – F(N) ||的,然后仅仅求出这些的loss进行反向传播。但是还有一个问题就是,为了更加的优化这个过程,即使在一个batch中也能选出很多种三元组,那么优化就落在了怎么选择这些三元组的问题上。我们可以把每个batch的数据平均一下,来保证每个batch中有P个人,每个人有K张图。接下来有两种方法来选择: 全生成:单纯的选择所有满足的三元组,也就是PK(K – 1)(PK – …

MTCNN

宏观理解 参考论文《Joint Face Detection and Alignment using Multi-task Cascaded Convolutional Networks》 我们已经了解了Faster R-CNN的过程和目的,其主要成就在于加速了人脸检测的速度并且提升了bbox的准确率。2016年诞生的MTCNN做的事情也是类似。下面一张流程图展示了MTCNN的训练过程,最后会得到带有人脸的bounding box以用于人脸检测和人脸上的5个landmarks以用于人脸识别。 从上图我们可以看出整个算法有3个stage,这三个stage是以级联的方式运作的。stage 1,浅层的CNN快速产生候选框;stage 2,通过更复杂的CNN精炼候选框,丢弃大量的重叠框;stage 3,使用更加强大的CNN,实现候选框去留,同时显示五个面部关键点定位。 在MTCNN之前的一个很广泛应用的级联是Viola和Jones提出的级联人脸检测器,利用Harr特征结合AdaBoost去实现高性能的实时训练。但是这种方法有一个很大的缺陷就是因为图片中人脸的姿势、光照或遮挡等原因致使能力直线下降,但是来解决这些问题的一个很好的工具就是深度学习,所以接下来我们来看看MTCNN的原理。 微观分析 我们已经看到了整个网络是由三个stage级联而生的,这是整个MTCNN的三个级联网络的过程图: 我们把这三个级联的网络叫做P-Net, R-Net和O-Net,所代表的含义就是Proposal Net, Re-fine Net和Output Net。 我们首先把原图拿来做图像金字塔resize成不同尺度的图片,然后用一个12*12的sliding window在所有的不同尺度的图片中滑动来截取初始候选框,接下来把这些12*12的候选框送到级联网络的第一个网络P-Net。 P-Net 首先传进去的是一张12*12的图片,可以看出来很小,也就是144个像素。接着做了10个3*3*3的卷积操作,stride可以看出来是1 ,最后得到一个10*10*10的中间量feature map。然后做一个2*2的max pooling操作,stride为2,所以最后得出来5*5*10的feature map。 随后也接着经过另外两个3*3的卷积操作把图像整合成1*1*32的特征向量。 最后在输出层中,我们可以得到3个结果。第一个是分类结果,通过2个1*1*32的卷积得到两个数字,然后把这两个数字送给softmax来做分类得到是人脸的概率和不是人脸的概率。第二个是人脸的位置回归,用4个1*1*32的卷积得到的4个位置信息。第三个是特征点信息,用10个1*1*32的卷积得到5个特征点位置(x,y)所以是10个值。这5个特征点分别是两个眼睛,鼻子和两个嘴角。 我们可以发现之前学过的网络中的全连接层在P-Net中没有用到,其实这种结构叫做全卷积网络,也就是全部都是用的卷积层来实现的。它是有好处的。想想我们之前讲神经网络的时候说过,因为后面有了全连接,所以参数量固定了,所以输入的图片的size也是要固定的。这也是为什么我们之前都需要在神经网络的输入前resize一下原图。但是全卷积网络就可以接受不同大小的图片,没有了后面参数量的局限。 R-Net R-Net就不再是全卷积网络了。它的输入是从P-Net中输出的bbox在原图中裁剪出来的24*24的RGB image。然后经过3个卷积和一个全连接变成一个128维的特征向量。然后照样我们有3个输出,都是用全连接生成2,4,10个对应的值。 O-Net O-Net同理,它的输入是从R-Net中输出的bbox在原图中裁剪出来的48*48的RGB image。然后经过4个卷积和一个全连接变成一个256维的特征向量。然后照样我们有3个输出,都是用全连接生成2,4,10个对应的值。 我们发现在stage的传递中,前面的landmark并没有被后面的网络应用,这是为什么?这是因为在multi-task学习中,我们希望landmark即使在前面没有应用到,也要作为一个约束力告诉前面的网络我们还有一个landmark用来考量,这样在计算损失的时候不至于让bbox回归和分类把landmark带的很偏。 现在我们了解完所有的结构之后,就要看一下优化的损失函数了。 loss functions 我们看到每个net都有3个输出,所以我们对应的也需要3个loss来计算。 对于face classification任务       我们用的就是交叉熵函数。p指的就是对应的yi的概率了。 对于bounding …

Faster R-CNN

宏观理解 我们学完R-CNN后,想想它有什么地方可以改进的?有什么地方是拖累了整个算法的速度?在2015年同一个作者提出了Fast R-CNN算法,它是基于R-CNN在选取候选框时大量的重叠导致同一图片的特征被多次提取而改进的算法。它使用一个叫RoI(regions of interest) pooling层来将抠出来的候选框resize到统一的大小。 在提出Fast R-CNN后,又是同样的作者,在2015年提出来又一个新的改进叫Faster R-CNN 。我们在R-CNN选取候选框的时候用的是selective search,但是它无法在GPU上进行加速,所以为了实现算法的速度更快,作者终于把整个算法都用一个CNN来实现,借助CNN来生成候选框。 论文参考 《Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks》 微观分析 我们先看看faster R-CNN的整个流水线: 对于每一个图片,我们先经过conv layers来提取图片特征,生成一张feature map。然后通过一个叫做Region Proposal Network的网络结构来代替selective search生成候选框。然后把生成的proposal和feature map给到RoI pooling层来得到统一大小的候选框提取特征。最后给一个分类器来分类并且精修bounding box的位置。 再来看一个更加细节的图示: 大家可以对照上面的两个图对应一下每个网络的位置。 我们分成3个部分来学习整个pipeline。 对于蓝色框 对于我们的每个卷积层,作者设置kernel size = 3,padding = 1,stride = 1 。这意味着什么?意味着一个M*N的原图经过卷积后的feature map还是M*N,卷积层并没有改变图片的大小。 对于我们的每个max池化层,作者设置kernel size = 2,padding = 0,stride = …

R-CNN

宏观理解 相关论文见《Rich Feature Hierarchies for Accurate Object Detection and Semantic Segmentation》by Ross Girshick, Jeff Donahue, Trevor Darrell, Jitendra Malik 学习R-CNN算法之前我们要知道他是干什么的。计算机视觉领域里有一个很重要很基础的分支是物体检测,它做的就是把一张图片的前景和背景区分开,然后给所有的前景进行分类。比如下图中的前景包括car,dog,horse和person。物体检测也就是得到一张如图一样的结果。这也是监督学习的一种,但是在数据集中他们经常是包含这张图片、人工标注的前景bounding box和对应的分类。在传统的机器学习算法中要做到这样的结果分为两步,也就是对应着我们之前说过的HOG + SVM类似,首先是要找到一个函数(HOG)可以输入一张图片输出一些特征,然后再有一个函数(SVM)输入这些特征再输出分类类别label。那么R-CNN则是基于深度学习来完成这个任务的方法。它的全名是Region CNN,我们知道CNN可以帮助我们分类图片,那么Region也就可以帮助我们找到图片中的特征。当然除了R-CNN还有很多比如YOLO, SSD,还有人尽皆知的Fast R-CNN, Faster R-CNN等等。这些以后都会依次介绍。 微观分析 下图是论文中给出的步骤,第二步中的region proposals代表我们要在图片中找到的前景,然后把这些候选框(大概1~2千个)抠出来,交给CNN来特征提取,最后交给分类器SVM分类。 总结一下,我们可以把整个R-CNN算法分成4个步骤: 生成候选框(使用selective search) 对每个候选框用CNN进行特征提取 把提取出来的特征送到分类器(默认SVM)中进行分类 使用回归器对候选框的位置进行修正 我们对以上4个步骤进行逐一讨论。   生成候选框(使用selective search) selective search方法做的事情是把一张图分割成很多很多很细节的小区域,举个例子,假如图片中有五星红旗🇨🇳,那么使用selective search后,不仅仅整个五星红旗会被框出来,连里面的5颗星星也会被框出来。这种手段叫做过分割。我们不妨把分出来的所有小区域存进一个集合R中。下一步则是在集合R中循环所有的区域,把他们两个的相似度存进另外一个集合S中。然后我们在集合S中排序,找到相似度最高的两个区域,我们把这两个区域合并(集合S中也就对应删除掉相应信息),然后放到集合R,形成新的候选框。随后因为有了新的候选框,我们要计算新的候选框和其余子集的相似度,然后重复以上过程每次两两合并最终直到整张图像合并成一个区域为止,这时集合S也就空了。伪代码如下: 那么我们怎么直到两个小区域的相似度呢?我们可以按照以下的顺序进行合并,如果颜色全部类似,那么就往下一步来按纹理合并,以此类推: 通过颜色直方图中颜色相近的 通过梯度直方图中梯度相近的(梯度可以理解为图片的条纹走势类似) 合并之后总面积小 合并之后总面积在bounding boxes中所占比例大 整个这个过程完成之后,我们把所有在集合R中的候选框全部拿来用,这就完成了第一步通过selective search选取候选框。   2. 对每个候选框用CNN进行特征提取 我们拿到了集合R中的候选框后,发现什么玩意都有,而且乱的一逼。这种东西怎么能直接送给CNN呢,我们在CNN中提到过,它的输入都是一定的不能变化。所以在送之前,我们需要对所有的候选框进行resize,那么怎么resize也是有讲究的,论文中提到了两种方式: 各向异性缩放:如图(D)所示,也就是平民玩家的愣resize,啥都不用管,大小弄成一样就好。但是这种方法对CNN的特征提取肯定不好。 各向同性缩放: 先扩充再裁剪:如图(B)所示,vip玩家,先把原图扩展成正方形,然后再裁剪 …

LBP feature

宏观理解 我们处理图片多了,就会发现其实计算机看图片就是通过当前像素点和旁边像素点的差异,就比如之前讲的HOG特征,就是找出像素点之前的梯度变化。万变不离其宗,LBP特征也是根据一个像素点和其他像素点的比较来锁定物体位置。 微观分析 这个理解起来比HOG要简单很多。我们把一个block定义在一个3*3的像素网格内,然后计算中心的像素点与其周围8个像素点的差异,如果大于中心点,标记为1,小于则标记为0。所以大概结果是这样的: 然后我们就得到来LBP编码:11010011 。 除了这样的3*3网格为一个LBP算子,还有圆形的算子,并且大小任意: 但是细心的同学会发现一个问题,我们的LBP编码,不是唯一的啊,这要看你读的起点在哪里,11010011可以,11110100就不可以吗?既然可以的话那不就会产生很大变化? 对的。这确实是LBP特征存在的问题,为了解决这个问题,Maenpaa等人将其进行了拓展,保证了一个像素点仅有唯一的编码。方法实在简单,就是在旋转的过程中我们可以得到8组变换,那么我们就规定好,我们只要这8组二进制编码中最小的那个。。(就是这么随性) 所以像上图一样,无论怎么旋转,最终输出的都是最小的二进制编码。 好了,现在我们把所有的像素点的LBP编码都求出来之后他们又可以组成一个图片,叫做LBP图谱: 原图   —->   LBP图谱 但是我们并不能用图谱来进行分类,我们还是需要转化成直方图的形式。但是问题是,如果我们按照上面的程序做下来我们只能得到一个直方图。这个对于描述一个图片的信息显然是不够的。 那么我们可以把一张图片分成n等份,每一份去做一个直方图出来,最后我们就有n个直方图。 最后我们把直方图归一化并且把所有的直方图进行拼接,就得到了这一张照片的LBP纹理特征向量,最后用它去做分类就好。

HOG features

宏观理解 在计算机视觉里有很多方法去处理图片搞分类和物体检测,比如现如今的CNN, RNN,R-CNN一类,当然在他们被证实更有效果之前,还有很多处理特征的方法,其中一个鼻祖就是HOG。HOG特征的全名叫histogram of oriented gradient。可以从名字看出来他是从图片的梯度入手来做图片物体检测的,在行人检测中获得了极大的成功。 这些传统的物体检测方法一般都有相似的流程: 选取候选窗口 提取候选窗口的图像特征 对特征进行分类 HOG特征的主要思想就是既然我们人类观察一个物体进行物体检测和分类是从物体的轮廓开始的,那么它也可以。那么怎么来通过物体的轮廓来锁定物体呢,那就是物体边缘的颜色呗,一片绿色的草原上插着一朵红色的花,是不是很容易发现?那么怎么看边缘颜色呢?梯度呗。所以HOG在做的正是利用图片的梯度来检测物体的位置,是不是有点像夜间的红外线探测仪,你看到一个颜色深的轮廓你能直觉出可能那是个物体。 背景知识介绍 之前提到了物体检测的第一步就是选取候选窗口,那么这一步怎么做呢?首先能想起来的就是暴力的方法,把截取的窗口的坐标循环一遍。但是显而易见这种方法时间复杂度太大,更别说做成实时的模型了。所以前人提出了一种叫做图像金字塔的方法,如下图。我们把每张图片缩放到不同的几种大小,然后仅仅用一种固定大小的框,像CNN中的kernel一样去扫一遍这个图片,然后把每次扫到的片段进行特征提取和分类。特征提取就是HOG的事情了。 微观分析 用梯度来检测物体就很简单,听起来就不是什么复杂的算法,无非就是提取图片所有像素点梯度,然后把有规则的归在一起,再用分类器进行分类呗。总结一下HOG处理过程: 论文Histograms of Oriented Gradients for Human Detection中的流程1. 对输入的图片进行预处理,比如裁剪或者resize。 2. 对预处理完的图片进行灰度处理 因为HOG特征注意的是边缘特征而不是颜色,可以用opencv库来转化:sample_image = cv2.cvtColor(sample_image,cv2.COLOR_RGB2GRAY) 3. 归一化图片 用Gamma校正法对输入图像进行颜色空间归一化,意义在于去除噪声,降低图片光度造成的影响。所谓的gamma校正就是在原图中开根号即可。 4. 计算出梯度图像 我们怎么得到梯度呢,需要用两个卷积核对原图进行卷积。 通过卷积之后我们可以得到两个梯度,分别是GX和GY: GX = translate(sample_image,1,0) – translate(sample_image,-1,0) GY = translate(sample_image,0,1) – translate(sample_image,0,-1) 然后通过GX,GY计算出幅值delta_G和方向angle得到梯度图: delta_G = np.sqrt(GX ** 2 + GY ** 2) …