| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- % MIT License
- %
- % Copyright (c) 2016 Kaipeng Zhang
- %
- % Permission is hereby granted, free of charge, to any person obtaining a copy
- % of this software and associated documentation files (the "Software"), to deal
- % in the Software without restriction, including without limitation the rights
- % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- % copies of the Software, and to permit persons to whom the Software is
- % furnished to do so, subject to the following conditions:
- %
- % The above copyright notice and this permission notice shall be included in all
- % copies or substantial portions of the Software.
- %
- % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- % SOFTWARE.
- function [total_boxes, points] = detect_face_v2(img,minsize,PNet,RNet,ONet,LNet,threshold,fastresize,factor)
- %im: input image
- %minsize: minimum of faces' size
- %pnet, rnet, onet: caffemodel
- %threshold: threshold=[th1 th2 th3], th1-3 are three steps's threshold
- %fastresize: resize img from last scale (using in high-resolution images) if fastresize==true
- factor_count=0;
- total_boxes=[];
- points=[];
- h=size(img,1);
- w=size(img,2);
- minl=min([w h]);
- img=single(img);
- if fastresize
- im_data=(single(img)-127.5)*0.0078125;
- end
- m=12/minsize;
- minl=minl*m;
- %creat scale pyramid
- scales=[];
- while (minl>=12)
- scales=[scales m*factor^(factor_count)];
- minl=minl*factor;
- factor_count=factor_count+1;
- end
- %first stage
- for j = 1:size(scales,2)
- scale=scales(j);
- hs=ceil(h*scale);
- ws=ceil(w*scale);
- if fastresize
- im_data=imResample(im_data,[hs ws],'bilinear');
- else
- im_data=(imResample(img,[hs ws],'bilinear')-127.5)*0.0078125;
- end
- PNet.blobs('data').reshape([hs ws 3 1]);
- out=PNet.forward({im_data});
- boxes=generateBoundingBox(out{2}(:,:,2),out{1},scale,threshold(1));
- %inter-scale nms
- pick=nms(boxes,0.5,'Union');
- boxes=boxes(pick,:);
- if ~isempty(boxes)
- total_boxes=[total_boxes;boxes];
- end
- end
- numbox=size(total_boxes,1);
- if ~isempty(total_boxes)
- pick=nms(total_boxes,0.7,'Union');
- total_boxes=total_boxes(pick,:);
- bbw=total_boxes(:,3)-total_boxes(:,1);
- bbh=total_boxes(:,4)-total_boxes(:,2);
- total_boxes=[total_boxes(:,1)+total_boxes(:,6).*bbw total_boxes(:,2)+total_boxes(:,7).*bbh total_boxes(:,3)+total_boxes(:,8).*bbw total_boxes(:,4)+total_boxes(:,9).*bbh total_boxes(:,5)];
- total_boxes=rerec(total_boxes);
- total_boxes(:,1:4)=fix(total_boxes(:,1:4));
- [dy edy dx edx y ey x ex tmpw tmph]=pad(total_boxes,w,h);
- end
- numbox=size(total_boxes,1);
- if numbox>0
- %second stage
- tempimg=zeros(24,24,3,numbox);
- for k=1:numbox
- tmp=zeros(tmph(k),tmpw(k),3);
- tmp(dy(k):edy(k),dx(k):edx(k),:)=img(y(k):ey(k),x(k):ex(k),:);
- tempimg(:,:,:,k)=imResample(tmp,[24 24],'bilinear');
- end
- tempimg=(tempimg-127.5)*0.0078125;
- RNet.blobs('data').reshape([24 24 3 numbox]);
- out=RNet.forward({tempimg});
- score=squeeze(out{2}(2,:));
- pass=find(score>threshold(2));
- total_boxes=[total_boxes(pass,1:4) score(pass)'];
- mv=out{1}(:,pass);
- if size(total_boxes,1)>0
- pick=nms(total_boxes,0.7,'Union');
- total_boxes=total_boxes(pick,:);
- total_boxes=bbreg(total_boxes,mv(:,pick)');
- total_boxes=rerec(total_boxes);
- end
- numbox=size(total_boxes,1);
- if numbox>0
- %third stage
- total_boxes=fix(total_boxes);
- [dy edy dx edx y ey x ex tmpw tmph]=pad(total_boxes,w,h);
- tempimg=zeros(48,48,3,numbox);
- for k=1:numbox
- tmp=zeros(tmph(k),tmpw(k),3);
- tmp(dy(k):edy(k),dx(k):edx(k),:)=img(y(k):ey(k),x(k):ex(k),:);
- tempimg(:,:,:,k)=imResample(tmp,[48 48],'bilinear');
- end
- tempimg=(tempimg-127.5)*0.0078125;
- ONet.blobs('data').reshape([48 48 3 numbox]);
- out=ONet.forward({tempimg});
- score=squeeze(out{3}(2,:));
- points=out{2};
- pass=find(score>threshold(3));
- points=points(:,pass);
- total_boxes=[total_boxes(pass,1:4) score(pass)'];
- mv=out{1}(:,pass);
- bbw=total_boxes(:,3)-total_boxes(:,1)+1;
- bbh=total_boxes(:,4)-total_boxes(:,2)+1;
- points(1:5,:)=repmat(bbw',[5 1]).*points(1:5,:)+repmat(total_boxes(:,1)',[5 1])-1;
- points(6:10,:)=repmat(bbh',[5 1]).*points(6:10,:)+repmat(total_boxes(:,2)',[5 1])-1;
- if size(total_boxes,1)>0
- total_boxes=bbreg(total_boxes,mv(:,:)');
- pick=nms(total_boxes,0.7,'Min');
- total_boxes=total_boxes(pick,:);
- points=points(:,pick);
- end
- end
- numbox=size(total_boxes,1);
- %extended stage
- if numbox>0
- tempimg=zeros(24,24,15,numbox);
- patchw=max([total_boxes(:,3)-total_boxes(:,1)+1 total_boxes(:,4)-total_boxes(:,2)+1]');
- patchw=fix(0.25*patchw);
- tmp=find(mod(patchw,2)==1);
- patchw(tmp)=patchw(tmp)+1;
- pointx=ones(numbox,5);
- pointy=ones(numbox,5);
- for k=1:5
- tmp=[points(k,:);points(k+5,:)];
- x=fix(tmp(1,:)-0.5*patchw);
- y=fix(tmp(2,:)-0.5*patchw);
- [dy edy dx edx y ey x ex tmpw tmph]=pad([x' y' x'+patchw' y'+patchw'],w,h);
- for j=1:numbox
- tmpim=zeros(tmpw(j),tmpw(j),3);
- tmpim(dy(j):edy(j),dx(j):edx(j),:)=img(y(j):ey(j),x(j):ex(j),:);
- tempimg(:,:,(k-1)*3+1:(k-1)*3+3,j)=imResample(tmpim,[24 24],'bilinear');
- end
- end
- LNet.blobs('data').reshape([24 24 15 numbox]);
- tempimg=(tempimg-127.5)*0.0078125;
- out=LNet.forward({tempimg});
- score=squeeze(out{3}(2,:));
- for k=1:5
- tmp=[points(k,:);points(k+5,:)];
- %do not make a large movement
- temp=find(abs(out{k}(1,:)-0.5)>0.35);
- if ~isempty(temp)
- l=length(temp);
- out{k}(:,temp)=ones(2,l)*0.5;
- end
- temp=find(abs(out{k}(2,:)-0.5)>0.35);
- if ~isempty(temp)
- l=length(temp);
- out{k}(:,temp)=ones(2,l)*0.5;
- end
- pointx(:,k)=(tmp(1,:)-0.5*patchw+out{k}(1,:).*patchw)';
- pointy(:,k)=(tmp(2,:)-0.5*patchw+out{k}(2,:).*patchw)';
- end
- for j=1:numbox
- points(:,j)=[pointx(j,:)';pointy(j,:)'];
- end
- end
- end
- end
- function [boundingbox] = bbreg(boundingbox,reg)
- %calibrate bouding boxes
- if size(reg,2)==1
- reg=reshape(reg,[size(reg,3) size(reg,4)])';
- end
- w=[boundingbox(:,3)-boundingbox(:,1)]+1;
- h=[boundingbox(:,4)-boundingbox(:,2)]+1;
- boundingbox(:,1:4)=[boundingbox(:,1)+reg(:,1).*w boundingbox(:,2)+reg(:,2).*h boundingbox(:,3)+reg(:,3).*w boundingbox(:,4)+reg(:,4).*h];
- end
- function [boundingbox reg] = generateBoundingBox(map,reg,scale,t)
- %use heatmap to generate bounding boxes
- stride=2;
- cellsize=12;
- boundingbox=[];
- map=map';
- dx1=reg(:,:,1)';
- dy1=reg(:,:,2)';
- dx2=reg(:,:,3)';
- dy2=reg(:,:,4)';
- [y x]=find(map>=t);
- a=find(map>=t);
- if size(y,1)==1
- y=y';x=x';score=map(a)';dx1=dx1';dy1=dy1';dx2=dx2';dy2=dy2';
- else
- score=map(a);
- end
- reg=[dx1(a) dy1(a) dx2(a) dy2(a)];
- if isempty(reg)
- reg=reshape([],[0 3]);
- end
- boundingbox=[y x];
- boundingbox=[fix((stride*(boundingbox-1)+1)/scale) fix((stride*(boundingbox-1)+cellsize-1+1)/scale) score reg];
- end
- function pick = nms(boxes,threshold,type)
- %NMS
- if isempty(boxes)
- pick = [];
- return;
- end
- x1 = boxes(:,1);
- y1 = boxes(:,2);
- x2 = boxes(:,3);
- y2 = boxes(:,4);
- s = boxes(:,5);
- area = (x2-x1+1) .* (y2-y1+1);
- [vals, I] = sort(s);
- pick = s*0;
- counter = 1;
- while ~isempty(I)
- last = length(I);
- i = I(last);
- pick(counter) = i;
- counter = counter + 1;
- xx1 = max(x1(i), x1(I(1:last-1)));
- yy1 = max(y1(i), y1(I(1:last-1)));
- xx2 = min(x2(i), x2(I(1:last-1)));
- yy2 = min(y2(i), y2(I(1:last-1)));
- w = max(0.0, xx2-xx1+1);
- h = max(0.0, yy2-yy1+1);
- inter = w.*h;
- if strcmp(type,'Min')
- o = inter ./ min(area(i),area(I(1:last-1)));
- else
- o = inter ./ (area(i) + area(I(1:last-1)) - inter);
- end
- I = I(find(o<=threshold));
- end
- pick = pick(1:(counter-1));
- end
- function [dy edy dx edx y ey x ex tmpw tmph] = pad(total_boxes,w,h)
- %compute the padding coordinates (pad the bounding boxes to square)
- tmpw=total_boxes(:,3)-total_boxes(:,1)+1;
- tmph=total_boxes(:,4)-total_boxes(:,2)+1;
- numbox=size(total_boxes,1);
-
- dx=ones(numbox,1);dy=ones(numbox,1);
- edx=tmpw;edy=tmph;
-
- x=total_boxes(:,1);y=total_boxes(:,2);
- ex=total_boxes(:,3);ey=total_boxes(:,4);
-
- tmp=find(ex>w);
- edx(tmp)=-ex(tmp)+w+tmpw(tmp);ex(tmp)=w;
-
- tmp=find(ey>h);
- edy(tmp)=-ey(tmp)+h+tmph(tmp);ey(tmp)=h;
-
- tmp=find(x<1);
- dx(tmp)=2-x(tmp);x(tmp)=1;
-
- tmp=find(y<1);
- dy(tmp)=2-y(tmp);y(tmp)=1;
- end
- function [bboxA] = rerec(bboxA)
- %convert bboxA to square
- bboxB=bboxA(:,1:4);
- h=bboxA(:,4)-bboxA(:,2);
- w=bboxA(:,3)-bboxA(:,1);
- l=max([w h]')';
- bboxA(:,1)=bboxA(:,1)+w.*0.5-l.*0.5;
- bboxA(:,2)=bboxA(:,2)+h.*0.5-l.*0.5;
- bboxA(:,3:4)=bboxA(:,1:2)+repmat(l,[1 2]);
- end
|