风机桨叶故障诊断(七)滑动窗与非极大值一直NMS
到目前为止,我已经利用自编码神经网络提取特征后训练得到了BP神经网络(参见:点击打开链接),且在测试样本集上表现不错。下面我们就要应用到实际中来检验算法的好坏了。
在目标检测的问题中,当我们有了一个算法来判断一幅图像是否为目标物体时,在一幅大图像中寻找目标物体,通常且比较简单的方法是使用滑动窗(Sliding windows)。
滑动窗很好理解,首先使用一个指定大小的检测窗口,从图像左上角开始,进行等间距的滑动,每次滑动的间距根据实际情况人为确定,每到一个位置,将窗口中对应的图像(窗口的大小很可能与算法的输入不同,这时我们需要缩放到统一的尺寸)输入我们的算法中,算法会给出这个窗口中的图像是否为我们的目标物体。直至滑动窗沿图像的横竖两个方向遍历了一遍后,我们就完成了某一个尺度窗口的滑动,如下图所示:
然后我们要做的是,变换滑动窗口的尺度,也就是滑动窗的大小。然后使用不同的尺度重复上面的工作。这里尺度大小的范围,变化的比例,滑动窗滑动的间距都是根据实际情况人为设定的。
我们的滑动窗每到一个位置,算法都会给出对于这个窗口是否属于目标物体的一个打分,我们的输出神经元使用的是sigmoid传递函数,所以神经网络给出了我们一个0~1之间的打分。当这个分数高于我们之前设定的某个阈值比如0.7时,我们认为这个图像就是我们寻找的目标物体,我们需要记录下当前窗口的位置信息和对应的分数,以便后面进行非极大值抑制时使用。
所以,当我们的滑动窗算法运行完毕后,我们应该得到的就是关于所有计算机认为包含所寻找的目标物体的窗口的信息,以一个矩阵来记录,那么每行所要包含的信息是:[左上顶点x坐标,左上顶点y坐标,右下x坐标,右下y坐标,得分]
下面是针对我的问题的matlab滑动窗实现代码,大家如果使用需要根据实际情况修改部分代码。函数需要提供一幅待检测的完整图像,和我们预测算法所需的信息,对于我们的神经网络也就是权值矩阵了。
滑动窗算法函数:
function [ location ] = ValidateImage( Theta1, Theta2, Theta3,image ) %验证一幅灰度图像中所有桨叶的可能位置 %返回描述所有桨叶位置的矩阵。每一行记录 横坐标 纵坐标 分数 imageWid=size(image,2); imageHigh=size(image,1); location=[]; for width=250:75:400 for i=1:35:imageHigh-width for j=1:35:imageWid-width tempImage=image(i:i+width-1,j:j+width-1); %灰度矩阵转列向量(逐列扫描) tempImage=Resize(tempImage, 25,25 ); X=reshape(tempImage,625,1); result=PredictPaddle( Theta1, Theta2, Theta3,X' ); if result>=0.6 location=[location;i,j,i+width,j+width,result]; end end end end end
神经网络的预测函数:
function [ p ] = PredictPaddle( Theta1, Theta2, Theta3,X ) %判断一幅图片是否为桨叶(以向量或矩阵的形式输入图像,请归一化到25×25灰度图后展开成行向量输入) m = size(X, 1); p = zeros(size(X, 1), 1); h1 = sigmoid([ones(m, 1) X] * Theta1'); h2 = sigmoid([ones(m, 1) h1] * Theta2'); h3 = sigmoid([ones(m, 1) h2] * Theta3'); p=h3; end
可以看到,我们的算法很好的找到了目标的位置,但是对于同一目标,有很多相似且重叠的窗口覆盖在一起。我们选出所有的窗口进行下一步的识别是没有意义的,显然前辈们也可肯定不是这么做的。在一个研究生学长的指点下,我了解到了一个叫做“非极大值抑制(Non-maximun suppression)”的算法,是专门来解决这类问题的。然后我搜到了下面两篇blog进行学习,后面我给出的实现代码也是直接在原作者给出的代码上进行的修改和注释。
http://blog.csdn.net/h2008066215019910120/article/details/25917609
http://blog.csdn.net/pb09013037/article/details/45477591
我来描述一下我对与“非极大值抑制”的个人理解。首先无论面对什么物体的识别问题,我们现在得到的都是很多个被认为包含目标物体的窗口,并且我们记录了它们的位置信息和分数信息。非极大值抑制中的这个“值”指的就是窗口的得分,因为我们的处于0~1之间的得分也可以理解为我们的算法认为这个窗口内包含目标物体的概率,或者自信程度。得分0.99自然比得分0.7更有可能是更好的包含目标物体的窗口,所以非极大值抑制就是想在窗口相互重叠的局部区域内选出得分的极大值代表的窗口,而抑制不是局部极大值的窗口。这样最后剩下的都是每个局部区域内得分最高的窗口,不会出现过多的重叠情况。
算法需要两个输入参数,一个参数boxs是记录窗口位置和得分信息的,boxs的每一行的格式为[左上顶点x坐标,左上顶点y坐标,右下x坐标,右下y坐标,得分],所以到这里我们明白,上面滑窗时这样格式化输出就是为了与这里进行对应。还有要注意的是,上面的博客中对于这个参数的描述[x,y,width,height ,score ]对应于他给的代码是错误的。
另一个参数overlap是人为选定的覆盖率。这是什么意思?我们上面对于非极大值抑制算法的描述有一个概念一直是模糊的,什么是局部?在这里我们定义:两个窗口重叠
部分的面积除以两个窗口中较小的面积=覆盖率overlap。那么覆盖率其实就可以用来描
述这个局部的概念。我们可以认为覆盖率大于某一个阈值后,两个窗口处于同一个局部
个人感觉这个overlap的选取和实际问题是有关系的,在我们的问题中,不同的桨叶的
窗口是不可能相互挨着的,所以我把overlap取的较小数0.1。
matlab实现代码如下:
function pick = nms(boxes, overlap) % pick = nms(boxes,overlap) % boxs 用来记录所有潜在目标窗口的信息,每一行对应[x,y,x+width,y+height ,score ], % 即左上角,右下角两定点坐标+系统对于当前窗口是目标物体可能性的打分0~1 % overlap是介于0~1的实数 % Non-maximumsuppression. 非极大值抑制 % Greedily selecthigh-scoring detections and skip detections % that are significantlycovered by a previously selected detection. % 贪婪选择:得分检测和跳跃检测(被十分重要的包含于预先选择检测的步骤中) % boxes = boxes'; if isempty(boxes) pick = []; else x1 = boxes(:,1); y1 = boxes(:,2); x2 = boxes(:,3); y2 = boxes(:,4); %得分 s = boxes(:,end); area = (x2-x1+1) .* (y2-y1+1);%结果为一列向量,记录每一个窗口面积 %将分数按从小到大排序 % vals:重拍后的分数 % I:重排后每个位置对应的原位置的索引 [vals, I] = sort(s); pick = []; while ~isempty(I) last = length(I); %分数最高窗口的索引 i = I(last); pick = [pick; i]; suppress = [last]; for pos = 1:last-1 j = I(pos); %求当前窗口与分数最高窗口重叠部分对应矩形的信息 xx1 = max(x1(i), x1(j)); yy1 = max(y1(i), y1(j)); xx2 = min(x2(i), x2(j)); yy2 = min(y2(i), y2(j)); w = xx2-xx1+1; h = yy2-yy1+1; if w > 0 && h > 0 %说明存在重叠部分 % compute overlap 计算重叠比例(重叠面积/当前两个窗口面积较小的那个的面积) o = w * h / min(area(i),area(j)); % o = w/area(j); if o > overlap %覆盖率超过设定值,加入抑制窗口列表 suppress = [suppress; pos]; end end end %将被抑制的窗口清空 I(suppress) = []; end end
我们来看一下使用非极大值抑制后,桨叶识别的效果。
我们可以看到,“非极大值抑制”的效果还是不错的。到目前为止,我们已经基本完
成了对桨叶识别的目标,对于每一幅图,都能保证定位到1-2个桨叶,当然客观的说,
还是存在一些问题,比如我们框出的目标物体不能保证包含整个桨叶,只能说是大部分
区域,如果有可能,后期还会继续完善我们的系统。
今天又学到不少,继续加油