├── README.md ├── compile.sh ├── imgs ├── 1.jpg ├── 10.jpg ├── 11.jpg ├── 12.jpg ├── 13.jpg ├── 14.jpg ├── 15.jpg ├── 16.jpg ├── 17.jpg ├── 18.jpg ├── 19.jpg ├── 2.jpg ├── 20.jpg ├── 21.jpg ├── 22.jpg ├── 23.jpg ├── 24.jpg ├── 25.jpg ├── 26.jpg ├── 3.jpg ├── 4.jpg ├── 5.jpg ├── 6.jpg ├── 7.jpg ├── 8.jpg └── 9.jpg ├── main.cpp ├── pcn_model ├── PCN-1.prototxt ├── PCN-2.prototxt ├── PCN-3.prototxt └── PCN.caffemodel └── results ├── out1.jpg ├── out10.jpg ├── out11.jpg ├── out12.jpg ├── out13.jpg ├── out14.jpg ├── out15.jpg ├── out16.jpg ├── out17.jpg ├── out18.jpg ├── out19.jpg ├── out2.jpg ├── out20.jpg ├── out21.jpg ├── out22.jpg ├── out23.jpg ├── out24.jpg ├── out25.jpg ├── out26.jpg ├── out3.jpg ├── out4.jpg ├── out5.jpg ├── out6.jpg ├── out7.jpg ├── out8.jpg └── out9.jpg /README.md: -------------------------------------------------------------------------------- 1 | 2 | ### etos-facedetector 3 | 4 | - Real-Time Rotation-Invariant Face Detection with Progressive Calibration Networks(PCN). 5 | 6 | ### Requirements 7 | 8 | - opencv (3.4.3 is tested) 9 | 10 | ### compile and usage 11 | 12 | ``` 13 | g++ main.cpp -std=c++11 -o etos_facedetector `pkg-config --cflags --libs opencv` 14 | ``` 15 | 16 | ``` 17 | ./etos_facedetector imgs/1.jpg 18 | ``` 19 | 20 | ### Demo Results 21 | 22 | ![](imgs/1.jpg) ![](results/out1.jpg) 23 | ![](imgs/2.jpg) ![](results/out2.jpg) 24 | ![](imgs/3.jpg) ![](results/out3.jpg) 25 | ![](imgs/4.jpg) ![](results/out4.jpg) 26 | ![](imgs/5.jpg) ![](results/out5.jpg) 27 | ![](imgs/6.jpg) ![](results/out6.jpg) 28 | ![](imgs/7.jpg) ![](results/out7.jpg) 29 | ![](imgs/8.jpg) ![](results/out8.jpg) 30 | ![](imgs/9.jpg) ![](results/out9.jpg) 31 | ![](imgs/10.jpg) ![](results/out10.jpg) 32 | ![](imgs/11.jpg) ![](results/out11.jpg) 33 | ![](imgs/12.jpg) ![](results/out12.jpg) 34 | ![](imgs/13.jpg) ![](results/out13.jpg) 35 | ![](imgs/14.jpg) ![](results/out14.jpg) 36 | ![](imgs/15.jpg) ![](results/out15.jpg) 37 | ![](imgs/16.jpg) ![](results/out16.jpg) 38 | ![](imgs/17.jpg) ![](results/out17.jpg) 39 | ![](imgs/18.jpg) ![](results/out18.jpg) 40 | ![](imgs/19.jpg) ![](results/out19.jpg) 41 | ![](imgs/20.jpg) ![](results/out20.jpg) 42 | ![](imgs/21.jpg) ![](results/out21.jpg) 43 | ![](imgs/22.jpg) ![](results/out22.jpg) 44 | ![](imgs/23.jpg) ![](results/out23.jpg) 45 | ![](imgs/24.jpg) ![](results/out24.jpg) 46 | ![](imgs/25.jpg) ![](results/out25.jpg) 47 | ![](imgs/26.jpg) ![](results/out26.jpg) 48 | 49 | ### Thanks 50 | 51 | [PCN-FaceDetection-FaceAlignment](https://github.com/Jack-CV/PCN-FaceDetection-FaceAlignment) 52 | -------------------------------------------------------------------------------- /compile.sh: -------------------------------------------------------------------------------- 1 | g++ main.cpp -std=c++11 -o etos_facedetector `pkg-config --cflags --libs opencv` 2 | -------------------------------------------------------------------------------- /imgs/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/1.jpg -------------------------------------------------------------------------------- /imgs/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/10.jpg -------------------------------------------------------------------------------- /imgs/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/11.jpg -------------------------------------------------------------------------------- /imgs/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/12.jpg -------------------------------------------------------------------------------- /imgs/13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/13.jpg -------------------------------------------------------------------------------- /imgs/14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/14.jpg -------------------------------------------------------------------------------- /imgs/15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/15.jpg -------------------------------------------------------------------------------- /imgs/16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/16.jpg -------------------------------------------------------------------------------- /imgs/17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/17.jpg -------------------------------------------------------------------------------- /imgs/18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/18.jpg -------------------------------------------------------------------------------- /imgs/19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/19.jpg -------------------------------------------------------------------------------- /imgs/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/2.jpg -------------------------------------------------------------------------------- /imgs/20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/20.jpg -------------------------------------------------------------------------------- /imgs/21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/21.jpg -------------------------------------------------------------------------------- /imgs/22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/22.jpg -------------------------------------------------------------------------------- /imgs/23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/23.jpg -------------------------------------------------------------------------------- /imgs/24.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/24.jpg -------------------------------------------------------------------------------- /imgs/25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/25.jpg -------------------------------------------------------------------------------- /imgs/26.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/26.jpg -------------------------------------------------------------------------------- /imgs/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/3.jpg -------------------------------------------------------------------------------- /imgs/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/4.jpg -------------------------------------------------------------------------------- /imgs/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/5.jpg -------------------------------------------------------------------------------- /imgs/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/6.jpg -------------------------------------------------------------------------------- /imgs/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/7.jpg -------------------------------------------------------------------------------- /imgs/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/8.jpg -------------------------------------------------------------------------------- /imgs/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/imgs/9.jpg -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | using namespace cv; 7 | using namespace cv::dnn; 8 | 9 | 10 | #define EPS 1e-5 11 | 12 | 13 | struct FaceBox 14 | { 15 | int x, y, w, h; 16 | float angle, scale, score; 17 | 18 | Rect getBox() { return Rect(x,y,w,h); } 19 | 20 | FaceBox(int x_, int y_, int w_, int h_, float a_, float s_, float c_) 21 | : x(x_), y(y_), w(w_), h(h_), angle(a_), scale(s_), score(c_) 22 | {} 23 | }; 24 | 25 | 26 | bool xyValid(int _x, int _y, cv::Mat _img) 27 | { 28 | if (_x >= 0 && _x < _img.cols && _y >= 0 && _y < _img.rows) 29 | return true; 30 | else 31 | return false; 32 | } 33 | 34 | 35 | cv::Mat preprocessImg(cv::Mat _img) 36 | { 37 | cv::Mat mean(_img.size(), CV_32FC3, cv::Scalar(104, 117, 123)); 38 | cv::Mat imgF; 39 | _img.convertTo(imgF, CV_32FC3); 40 | return imgF - mean; 41 | } 42 | 43 | cv::Mat preprocessImg(cv::Mat _img, int _dim) 44 | { 45 | cv::Mat imgNew; 46 | cv::resize(_img, imgNew, cv::Size(_dim, _dim)); 47 | cv::Mat mean(imgNew.size(), CV_32FC3, cv::Scalar(104, 117, 123)); 48 | cv::Mat imgF; 49 | imgNew.convertTo(imgF, CV_32FC3); 50 | return imgF - mean; 51 | } 52 | 53 | 54 | cv::Mat resizeImg(cv::Mat _img, float _scale) 55 | { 56 | cv::Mat aux; 57 | cv::resize(_img, aux, cv::Size(int(_img.cols / _scale), int(_img.rows / _scale))); 58 | return aux; 59 | } 60 | 61 | cv::Mat padImg(cv::Mat _img) 62 | { 63 | int row = std::min(int(_img.rows * 0.2), 100); 64 | int col = std::min(int(_img.cols * 0.2), 100); 65 | cv::Mat aux; 66 | cv::copyMakeBorder(_img, aux, row, row, col, col, cv::BORDER_CONSTANT); 67 | return aux; 68 | } 69 | 70 | 71 | bool compareFaceByScore(const FaceBox &box1, const FaceBox &box2) 72 | { 73 | return box1.score > box2.score; 74 | } 75 | 76 | 77 | float IoU(FaceBox &box1, FaceBox &box2) 78 | { 79 | int xOverlap = std::max(0, std::min(box1.x + box1.w - 1, box2.x + box2.w - 1) - std::max(box1.x, box2.x) + 1); 80 | int yOverlap = std::max(0, std::min(box1.y + box1.h - 1, box2.y + box2.h - 1) - std::max(box1.y, box2.y) + 1); 81 | int intersection = xOverlap * yOverlap; 82 | int unio = box1.w * box1.h + box2.w * box2.h - intersection; 83 | 84 | return float(intersection) / unio; 85 | } 86 | 87 | 88 | std::vector NMS(std::vector &_faces, bool _local, float _threshold) 89 | { 90 | if (_faces.size() == 0) 91 | return _faces; 92 | 93 | std::sort(_faces.begin(), _faces.end(), compareFaceByScore); 94 | bool flag[_faces.size()]; 95 | 96 | memset(flag, 0, _faces.size()); 97 | for (int i = 0; i < _faces.size(); i++) 98 | { 99 | if (flag[i]) 100 | continue; 101 | for (int j = i + 1; j < _faces.size(); j++) 102 | { 103 | if (_local && abs(_faces[i].scale - _faces[j].scale) > EPS) 104 | continue; 105 | 106 | if (IoU(_faces[i], _faces[j]) > _threshold) 107 | flag[j] = 1; 108 | } 109 | } 110 | 111 | std::vector faces_nms; 112 | for (int i = 0; i < _faces.size(); i++) 113 | { 114 | if (!flag[i]) 115 | faces_nms.push_back(_faces[i]); 116 | } 117 | return faces_nms; 118 | } 119 | 120 | 121 | std::vector TransformBoxes(cv::Mat _img, cv::Mat _imgPad, std::vector &_faces) 122 | { 123 | int row = (_imgPad.rows - _img.rows) / 2; 124 | int col = (_imgPad.cols - _img.cols) / 2; 125 | 126 | std::vector newFaceBoxes; 127 | for(int i = 0; i < _faces.size(); i++) 128 | { 129 | if (_faces[i].w > 0 && _faces[i].h > 0) 130 | newFaceBoxes.push_back(FaceBox(_faces[i].x - col, _faces[i].y - row, _faces[i].w, _faces[i].w, _faces[i].angle, _faces[i].scale, _faces[i].score)); 131 | } 132 | return newFaceBoxes; 133 | } 134 | 135 | 136 | 137 | std::vector PCN_1(cv::Mat _img, cv::Mat _paddedImg, cv::dnn::Net _net, float _thresh, int _minFaceSize) 138 | { 139 | std::vector faceBoxes_1; 140 | int row = (_paddedImg.rows - _img.rows) / 2; 141 | int col = (_paddedImg.cols - _img.cols) / 2; 142 | int netSize = 24; 143 | int minFace = _minFaceSize * 1.4; // - size 20 + 40% 144 | float currentScale = minFace / float(netSize); 145 | int stride = 8; 146 | 147 | cv::Mat resizedImg = resizeImg(_img, currentScale); 148 | 149 | while (std::min(resizedImg.rows, resizedImg.cols) >= netSize) 150 | { 151 | // - Set input for net 152 | Mat inputMat = preprocessImg(resizedImg); 153 | Mat inputBlob = blobFromImage(inputMat, 1.0, Size(), Scalar(), false, false); 154 | _net.setInput(inputBlob); 155 | 156 | std::vector outputBlobNames = {"cls_prob", "rotate_cls_prob", "bbox_reg_1" }; 157 | std::vector outputBlobs; 158 | 159 | _net.forward(outputBlobs, outputBlobNames); 160 | cv::Mat scoresData = outputBlobs[0]; 161 | cv::Mat rotateProbsData = outputBlobs[1]; 162 | cv::Mat regressionData = outputBlobs[2]; 163 | 164 | // scoresData.ptr(0, 1) ----> image 0, channel 1 165 | Mat scoresMat(scoresData.size[2], scoresData.size[3], CV_32F, scoresData.ptr(0, 1)); 166 | Mat reg_1_Mat(regressionData.size[2], regressionData.size[3], CV_32F, regressionData.ptr(0, 0)); 167 | Mat reg_2_Mat(regressionData.size[2], regressionData.size[3], CV_32F, regressionData.ptr(0, 1)); 168 | Mat reg_3_Mat(regressionData.size[2], regressionData.size[3], CV_32F, regressionData.ptr(0, 2)); 169 | Mat rotateProbsMat(rotateProbsData.size[2], rotateProbsData.size[3], CV_32F, rotateProbsData.ptr(0, 1)); 170 | 171 | float w = netSize * currentScale; 172 | 173 | for (int i = 0; i < scoresData.size[2]; i++) 174 | { 175 | for (int j = 0; j < scoresData.size[3]; j++) 176 | { 177 | if (scoresMat.at(i, j) < _thresh) 178 | {continue;} 179 | 180 | float score = scoresMat.at(i, j); 181 | float sn = reg_1_Mat.at(i, j); 182 | float xn = reg_2_Mat.at(i, j); 183 | float yn = reg_3_Mat.at(i, j); 184 | 185 | int x = int(j * currentScale * stride - 0.5 * sn * w + sn * xn * w + 0.5 * w) + col; 186 | int y = int(i * currentScale * stride - 0.5 * sn * w + sn * yn * w + 0.5 * w) + row; 187 | int wh = int(w * sn); 188 | 189 | if(xyValid(x, y, _paddedImg) && xyValid(x+wh-1, y+wh-1, _paddedImg)) 190 | { 191 | if (rotateProbsMat.at(i, j) > 0.5) 192 | { 193 | faceBoxes_1.push_back(FaceBox(x,y,wh,wh,0, currentScale, score)); 194 | //rectangle(_paddedImg, Rect(x, y, wh, wh), Scalar(0, 255, 0), 2); 195 | } 196 | else 197 | { 198 | faceBoxes_1.push_back(FaceBox(x,y,wh,wh,180, currentScale, score)); 199 | //rectangle(_paddedImg, Rect(x, y, wh, wh), Scalar(255, 0, 0), 2); 200 | } 201 | } 202 | 203 | 204 | } 205 | } 206 | 207 | resizedImg = resizeImg(resizedImg, 1.414); 208 | currentScale = float(_img.rows) / resizedImg.rows; 209 | } 210 | 211 | return faceBoxes_1; 212 | } 213 | 214 | 215 | std::vector PCN_2(cv::Mat _img, cv::Mat _img180, cv::dnn::Net _net, float _threshold, int _dim, std::vector _faces) 216 | { 217 | //_dim = 24 ---> network size 218 | if (_faces.size() == 0) 219 | return _faces; 220 | 221 | std::vector dataList; 222 | int height = _img.rows; 223 | for (int i = 0; i < _faces.size(); i++) 224 | { 225 | if (abs(_faces[i].angle) < EPS) 226 | dataList.push_back(preprocessImg(_img(_faces[i].getBox()), _dim)); 227 | else 228 | { 229 | int y2 = _faces[i].y + _faces[i].h - 1; 230 | dataList.push_back(preprocessImg(_img180(cv::Rect(_faces[i].x, height - 1 - y2, _faces[i].w, _faces[i].h)), _dim)); 231 | } 232 | } 233 | 234 | std::vector faceBoxes_2; 235 | 236 | // - Process the dataList vector 237 | for(int dataNr=0; dataNr outputBlobNames = {"cls_prob", "rotate_cls_prob", "bbox_reg_2" }; 242 | std::vector outputBlobs; 243 | 244 | _net.forward(outputBlobs, outputBlobNames); 245 | cv::Mat scoresData = outputBlobs[0]; 246 | cv::Mat rotateProbsData = outputBlobs[1]; 247 | cv::Mat regressionData = outputBlobs[2]; 248 | 249 | Mat scoresMat(scoresData.size[1], scoresData.size[0], CV_32F, scoresData.ptr(0, 0)); // - using channel 0 250 | Mat regMat(regressionData.size[1], regressionData.size[0], CV_32F, regressionData.ptr(0, 0)); 251 | Mat rotateProbsMat(rotateProbsData.size[1], rotateProbsData.size[0], CV_32F, rotateProbsData.ptr(0, 0)); 252 | 253 | float score = scoresMat.at(1,0); 254 | if(score < _threshold) 255 | continue; 256 | 257 | float sn = regMat.at(0,0);// reg->data_at(i, 0, 0, 0); 258 | float xn = regMat.at(1,0);// reg->data_at(i, 1, 0, 0); 259 | float yn = regMat.at(2,0);//reg->data_at(i, 2, 0, 0); 260 | 261 | sn = regMat.at(0,0);// reg->data_at(i, 0, 0, 0); 262 | xn = regMat.at(0,1);// reg->data_at(i, 1, 0, 0); 263 | yn = regMat.at(0,2);//reg->data_at(i, 2, 0, 0); 264 | 265 | int cropX = _faces[dataNr].x; 266 | int cropY = _faces[dataNr].y; 267 | int cropW = _faces[dataNr].w; 268 | if (abs(_faces[dataNr].angle) > EPS) 269 | cropY = height - 1 - (cropY + cropW - 1); 270 | int w = int(sn * cropW); 271 | int x = int(cropX - 0.5 * sn * cropW + cropW * sn * xn + 0.5 * cropW); 272 | int y = int(cropY - 0.5 * sn * cropW + cropW * sn * yn + 0.5 * cropW); 273 | float maxRotateScore = 0; 274 | int maxRotateIndex = 0; 275 | for (int j = 0; j < 3; j++) 276 | { 277 | float rotateProb = rotateProbsMat.at(j,0); 278 | if (rotateProb > maxRotateScore) 279 | { 280 | maxRotateScore = rotateProb;//rotateProb->data_at(i, j, 0, 0); 281 | maxRotateIndex = j; 282 | } 283 | } 284 | 285 | if(xyValid(x, y, _img) && xyValid(x+w-1, y+w-1, _img)) //(Legal(x, y, img) && Legal(x + w - 1, y + w - 1, img)) 286 | { 287 | float angle = 0; 288 | if (abs(_faces[dataNr].angle) < EPS) 289 | { 290 | if (maxRotateIndex == 0) 291 | angle = 90; 292 | else if (maxRotateIndex == 1) 293 | angle = 0; 294 | else 295 | angle = -90; 296 | faceBoxes_2.push_back(FaceBox(x,y,w,w,angle, _faces[dataNr].scale, score)); 297 | //ret.push_back(Window2(x, y, w, w, angle, _faces[i].scale, prob->data_at(i, 1, 0, 0))); 298 | } 299 | else 300 | { 301 | if (maxRotateIndex == 0) 302 | angle = 90; 303 | else if (maxRotateIndex == 1) 304 | angle = 180; 305 | else 306 | angle = -90; 307 | faceBoxes_2.push_back(FaceBox(x, height - 1 - (y + w - 1), w, w, angle, _faces[dataNr].scale, score)); 308 | } 309 | } 310 | } 311 | return faceBoxes_2; 312 | } 313 | 314 | 315 | 316 | std::vector PCN_3(cv::Mat _img, cv::Mat _img180, cv::Mat _img90, cv::Mat _imgNeg90, cv::dnn::Net _net, float _threshold, int _dim, std::vector _faces) 317 | { 318 | if (_faces.size() == 0) 319 | return _faces; 320 | 321 | std::vector dataList; 322 | int height = _img.rows; 323 | int width = _img.cols; 324 | for (int i = 0; i < _faces.size(); i++) 325 | { 326 | if (abs(_faces[i].angle) < EPS) 327 | dataList.push_back(preprocessImg(_img(cv::Rect(_faces[i].x, _faces[i].y, _faces[i].w, _faces[i].h)), _dim)); 328 | else if (abs(_faces[i].angle - 90) < EPS) 329 | { 330 | dataList.push_back(preprocessImg(_img90(cv::Rect(_faces[i].y, _faces[i].x, _faces[i].h, _faces[i].w)), _dim)); 331 | } 332 | else if (abs(_faces[i].angle + 90) < EPS) 333 | { 334 | int x = _faces[i].y; 335 | int y = width - 1 - (_faces[i].x + _faces[i].w - 1); 336 | dataList.push_back(preprocessImg(_imgNeg90(cv::Rect(x, y, _faces[i].w, _faces[i].h)), _dim)); 337 | } 338 | else 339 | { 340 | int y2 = _faces[i].y + _faces[i].h - 1; 341 | dataList.push_back(preprocessImg(_img180(cv::Rect(_faces[i].x, height - 1 - y2, _faces[i].w, _faces[i].h)), _dim)); 342 | } 343 | } 344 | 345 | std::vector faceBoxes_3; 346 | 347 | for(int dataNr=0; dataNr outputBlobNames = {"cls_prob", "rotate_reg_3", "bbox_reg_3" }; 352 | std::vector outputBlobs; 353 | 354 | _net.forward(outputBlobs, outputBlobNames); 355 | cv::Mat scoresData = outputBlobs[0]; 356 | cv::Mat rotateProbsData = outputBlobs[1]; 357 | cv::Mat regressionData = outputBlobs[2]; 358 | 359 | Mat scoresMat(scoresData.size[1], scoresData.size[0], CV_32F, scoresData.ptr(0, 0)); // - using channel 0 360 | Mat regMat(regressionData.size[1], regressionData.size[0], CV_32F, regressionData.ptr(0, 0)); 361 | Mat rotateProbsMat(rotateProbsData.size[1], rotateProbsData.size[0], CV_32F, rotateProbsData.ptr(0, 0)); 362 | 363 | // - score = prob->data_at(i, 1, 0, 0) 364 | float score = scoresMat.at(1,0); 365 | if(score < _threshold) 366 | continue; 367 | 368 | float sn = regMat.at(0,0);// reg->data_at(i, 0, 0, 0); 369 | float xn = regMat.at(1,0);// reg->data_at(i, 1, 0, 0); 370 | float yn = regMat.at(2,0);//reg->data_at(i, 2, 0, 0); 371 | 372 | int cropX = _faces[dataNr].x; 373 | int cropY = _faces[dataNr].y; 374 | int cropW = _faces[dataNr].w; 375 | cv::Mat imgTmp = _img; 376 | if (abs(_faces[dataNr].angle - 180) < EPS) 377 | { 378 | cropY = height - 1 - (cropY + cropW - 1); 379 | imgTmp = _img180; 380 | } 381 | else if (abs(_faces[dataNr].angle - 90) < EPS) 382 | { 383 | std::swap(cropX, cropY); 384 | imgTmp = _img90; 385 | } 386 | else if (abs(_faces[dataNr].angle + 90) < EPS) 387 | { 388 | cropX = _faces[dataNr].y; 389 | cropY = width - 1 - (_faces[dataNr].x + _faces[dataNr].w - 1); 390 | imgTmp = _imgNeg90; 391 | } 392 | 393 | int w = int(sn * cropW); 394 | int x = int(cropX - 0.5 * sn * cropW + cropW * sn * xn + 0.5 * cropW); 395 | int y = int(cropY - 0.5 * sn * cropW + cropW * sn * yn + 0.5 * cropW); 396 | float angleRange_ = 45; 397 | float angle = angleRange_ * rotateProbsMat.at(0,0); 398 | if(xyValid(x, y, imgTmp) && xyValid(x + w - 1, y + w - 1, imgTmp)) 399 | { 400 | if (abs(_faces[dataNr].angle) < EPS) 401 | faceBoxes_3.push_back(FaceBox(x, y, w, w, angle, _faces[dataNr].scale, score)); 402 | else if (abs(_faces[dataNr].angle - 180) < EPS) 403 | { 404 | faceBoxes_3.push_back(FaceBox(x, height - 1 - (y + w - 1), w, w, 180 - angle, _faces[dataNr].scale, score)); 405 | } 406 | else if (abs(_faces[dataNr].angle - 90) < EPS) 407 | { 408 | faceBoxes_3.push_back(FaceBox(y, x, w, w, 90 - angle, _faces[dataNr].scale, score)); 409 | } 410 | else 411 | { 412 | faceBoxes_3.push_back(FaceBox(width - y - w, x, w, w, -90 + angle, _faces[dataNr].scale, score)); 413 | } 414 | } 415 | } 416 | 417 | return faceBoxes_3; 418 | } 419 | 420 | 421 | 422 | cv::Point RotatePoint(int x, int y, float centerX, float centerY, float angle) 423 | { 424 | x -= centerX; 425 | y -= centerY; 426 | float theta = -angle * M_PI / 180; 427 | int rx = int(centerX + x * std::cos(theta) - y * std::sin(theta)); 428 | int ry = int(centerY + x * std::sin(theta) + y * std::cos(theta)); 429 | return cv::Point(rx, ry); 430 | } 431 | 432 | void DrawLine(cv::Mat img, std::vector pointList) 433 | { 434 | int thick = 2; 435 | cv::Scalar cyan = CV_RGB(0, 0, 255); 436 | cv::Scalar blue = CV_RGB(0, 255, 0); 437 | cv::line(img, pointList[0], pointList[1], cyan, thick); 438 | cv::line(img, pointList[1], pointList[2], cyan, thick); 439 | cv::line(img, pointList[2], pointList[3], cyan, thick); 440 | cv::line(img, pointList[3], pointList[0], blue, thick); 441 | } 442 | 443 | void DrawFace(cv::Mat img, FaceBox face) 444 | { 445 | int x1 = face.x; 446 | int y1 = face.y; 447 | int x2 = face.w + face.x - 1; 448 | int y2 = face.w + face.y - 1; 449 | int centerX = (x1 + x2) / 2; 450 | int centerY = (y1 + y2) / 2; 451 | std::vector pointList; 452 | pointList.push_back(RotatePoint(x1, y1, centerX, centerY, face.angle)); 453 | pointList.push_back(RotatePoint(x1, y2, centerX, centerY, face.angle)); 454 | pointList.push_back(RotatePoint(x2, y2, centerX, centerY, face.angle)); 455 | pointList.push_back(RotatePoint(x2, y1, centerX, centerY, face.angle)); 456 | DrawLine(img, pointList); 457 | } 458 | 459 | 460 | // ------------------------------------------------------------------------------------------------------------ 461 | 462 | int main(int argc, char **argv) 463 | { 464 | Net net_1 = readNet("pcn_model/PCN-1.prototxt", "pcn_model/PCN.caffemodel"); 465 | Net net_2 = readNet("pcn_model/PCN-2.prototxt", "pcn_model/PCN.caffemodel"); 466 | Net net_3 = readNet("pcn_model/PCN-3.prototxt", "pcn_model/PCN.caffemodel"); 467 | 468 | 469 | Mat img = imread(argv[1]); 470 | Mat paddedImg = padImg(img); 471 | 472 | cv::Mat img180, img90, imgNeg90; 473 | cv::flip(paddedImg, img180, 0); 474 | cv::transpose(paddedImg, img90); 475 | cv::flip(img90, imgNeg90, 0); 476 | 477 | float thresholds[] = {0.37, 0.43, 0.95}; 478 | 479 | cv::TickMeter tm; 480 | tm.reset(); 481 | tm.start(); 482 | 483 | int minFaceSize = 40; 484 | std::vector faces = PCN_1(img, paddedImg, net_1, thresholds[0], minFaceSize); 485 | faces = NMS(faces, true, 0.8); 486 | faces = PCN_2(paddedImg, img180, net_2, thresholds[1], 24, faces); 487 | faces = NMS(faces, true, 0.8); 488 | faces = PCN_3(paddedImg, img180, img90, imgNeg90, net_3, thresholds[2], 48, faces); 489 | faces = NMS(faces, false, 0.3); 490 | 491 | tm.stop(); 492 | std::cout << "Time Cost: "<< tm.getTimeMilli() << " ms" << std::endl; 493 | 494 | std::vector preList = TransformBoxes(img, paddedImg, faces); 495 | 496 | for (int i = 0; i < preList.size(); i++) 497 | { 498 | DrawFace(img, preList[i]); 499 | } 500 | 501 | //imshow("IMG", img); 502 | //waitKey(); 503 | imwrite("out.jpg",img); 504 | 505 | 506 | return 1; 507 | 508 | } 509 | 510 | -------------------------------------------------------------------------------- /pcn_model/PCN-1.prototxt: -------------------------------------------------------------------------------- 1 | name: "PCN-1" 2 | 3 | input: "data" 4 | input_dim: 1 5 | input_dim: 3 6 | input_dim: 24 7 | input_dim: 24 8 | 9 | layer { 10 | name: "conv1_1" 11 | type: "Convolution" 12 | bottom: "data" 13 | top: "conv1_1" 14 | convolution_param { 15 | num_output: 16 16 | kernel_size: 3 17 | stride: 2 18 | } 19 | } 20 | layer { 21 | name: "relu1_1" 22 | type: "ReLU" 23 | bottom: "conv1_1" 24 | top: "conv1_1" 25 | } 26 | layer { 27 | name: "conv2_1" 28 | type: "Convolution" 29 | bottom: "conv1_1" 30 | top: "conv2_1" 31 | convolution_param { 32 | num_output: 32 33 | kernel_size: 3 34 | stride: 2 35 | } 36 | } 37 | layer { 38 | name: "relu2_1" 39 | type: "ReLU" 40 | bottom: "conv2_1" 41 | top: "conv2_1" 42 | } 43 | layer { 44 | name: "conv3_1" 45 | type: "Convolution" 46 | bottom: "conv2_1" 47 | top: "conv3_1" 48 | convolution_param { 49 | num_output: 64 50 | kernel_size: 3 51 | stride: 2 52 | } 53 | } 54 | layer { 55 | name: "relu3_1" 56 | type: "ReLU" 57 | bottom: "conv3_1" 58 | top: "conv3_1" 59 | } 60 | layer { 61 | name: "fc4_1" 62 | type: "Convolution" 63 | bottom: "conv3_1" 64 | top: "fc4_1" 65 | convolution_param { 66 | num_output: 128 67 | kernel_size: 2 68 | stride: 1 69 | } 70 | } 71 | layer { 72 | name: "relu4_1" 73 | type: "ReLU" 74 | bottom: "fc4_1" 75 | top: "fc4_1" 76 | } 77 | layer { 78 | name: "fc5_1" 79 | type: "Convolution" 80 | bottom: "fc4_1" 81 | top: "fc5_1" 82 | convolution_param { 83 | num_output: 2 84 | kernel_size: 1 85 | } 86 | } 87 | layer { 88 | name: "cls_prob" 89 | type: "Softmax" 90 | bottom: "fc5_1" 91 | top: "cls_prob" 92 | } 93 | layer { 94 | name: "fc6_1" 95 | type: "Convolution" 96 | bottom: "fc4_1" 97 | top: "fc6_1" 98 | convolution_param { 99 | num_output: 2 100 | kernel_size: 1 101 | } 102 | } 103 | layer { 104 | name: "rotate_cls_prob" 105 | type: "Softmax" 106 | bottom: "fc6_1" 107 | top: "rotate_cls_prob" 108 | } 109 | layer { 110 | name: "bbox_reg_1" 111 | type: "Convolution" 112 | bottom: "fc4_1" 113 | top: "bbox_reg_1" 114 | convolution_param { 115 | num_output: 3 116 | kernel_size: 1 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /pcn_model/PCN-2.prototxt: -------------------------------------------------------------------------------- 1 | name: "PCN-2" 2 | 3 | input: "data" 4 | input_dim: 1 5 | input_dim: 3 6 | input_dim: 24 7 | input_dim: 24 8 | 9 | layer { 10 | name: "conv1_2" 11 | type: "Convolution" 12 | bottom: "data" 13 | top: "conv1_2" 14 | convolution_param { 15 | num_output: 20 16 | kernel_size: 3 17 | } 18 | } 19 | layer { 20 | name: "pool1_2" 21 | type: "Pooling" 22 | bottom: "conv1_2" 23 | top: "pool1_2" 24 | pooling_param { 25 | pool: MAX 26 | kernel_size: 3 27 | stride: 2 28 | } 29 | } 30 | layer { 31 | name: "relu1_2" 32 | type: "ReLU" 33 | bottom: "pool1_2" 34 | top: "pool1_2" 35 | } 36 | layer { 37 | name: "conv2_2" 38 | type: "Convolution" 39 | bottom: "pool1_2" 40 | top: "conv2_2" 41 | convolution_param { 42 | num_output: 40 43 | kernel_size: 3 44 | } 45 | } 46 | layer { 47 | name: "pool2_2" 48 | type: "Pooling" 49 | bottom: "conv2_2" 50 | top: "pool2_2" 51 | pooling_param { 52 | pool: MAX 53 | kernel_size: 3 54 | stride: 2 55 | } 56 | } 57 | layer { 58 | name: "relu2_2" 59 | type: "ReLU" 60 | bottom: "pool2_2" 61 | top: "pool2_2" 62 | } 63 | layer { 64 | name: "conv3_2" 65 | type: "Convolution" 66 | bottom: "pool2_2" 67 | top: "conv3_2" 68 | convolution_param { 69 | num_output: 70 70 | kernel_size: 2 71 | } 72 | } 73 | layer { 74 | name: "relu3_2" 75 | type: "ReLU" 76 | bottom: "conv3_2" 77 | top: "conv3_2" 78 | } 79 | layer { 80 | name: "fc4_2" 81 | type: "InnerProduct" 82 | bottom: "conv3_2" 83 | top: "fc4_2" 84 | inner_product_param { 85 | num_output: 140 86 | } 87 | } 88 | layer { 89 | name: "relu4_2" 90 | type: "ReLU" 91 | bottom: "fc4_2" 92 | top: "fc4_2" 93 | } 94 | layer { 95 | name: "fc5_2" 96 | type: "InnerProduct" 97 | bottom: "fc4_2" 98 | top: "fc5_2" 99 | inner_product_param { 100 | num_output: 2 101 | } 102 | } 103 | layer { 104 | name: "cls_prob" 105 | type: "Softmax" 106 | bottom: "fc5_2" 107 | top: "cls_prob" 108 | } 109 | layer { 110 | name: "fc6_2" 111 | type: "InnerProduct" 112 | bottom: "fc4_2" 113 | top: "fc6_2" 114 | inner_product_param { 115 | num_output: 3 116 | } 117 | } 118 | layer { 119 | name: "rotate_cls_prob" 120 | type: "Softmax" 121 | bottom: "fc6_2" 122 | top: "rotate_cls_prob" 123 | } 124 | layer { 125 | name: "bbox_reg_2" 126 | type: "InnerProduct" 127 | bottom: "fc4_2" 128 | top: "bbox_reg_2" 129 | inner_product_param { 130 | num_output: 3 131 | } 132 | } 133 | 134 | -------------------------------------------------------------------------------- /pcn_model/PCN-3.prototxt: -------------------------------------------------------------------------------- 1 | name: "PCN-3" 2 | 3 | input: "data" 4 | input_dim: 1 5 | input_dim: 3 6 | input_dim: 48 7 | input_dim: 48 8 | 9 | layer { 10 | name: "conv1_3" 11 | type: "Convolution" 12 | bottom: "data" 13 | top: "conv1_3" 14 | convolution_param { 15 | num_output: 24 16 | kernel_size: 3 17 | } 18 | } 19 | layer { 20 | name: "pool1_3" 21 | type: "Pooling" 22 | bottom: "conv1_3" 23 | top: "pool1_3" 24 | pooling_param { 25 | pool: MAX 26 | kernel_size: 3 27 | stride: 2 28 | } 29 | } 30 | layer { 31 | name: "relu1_3" 32 | type: "ReLU" 33 | bottom: "pool1_3" 34 | top: "pool1_3" 35 | } 36 | layer { 37 | name: "conv2_3" 38 | type: "Convolution" 39 | bottom: "pool1_3" 40 | top: "conv2_3" 41 | convolution_param { 42 | num_output: 48 43 | kernel_size: 3 44 | } 45 | } 46 | layer { 47 | name: "pool2_3" 48 | type: "Pooling" 49 | bottom: "conv2_3" 50 | top: "pool2_3" 51 | pooling_param { 52 | pool: MAX 53 | kernel_size: 3 54 | stride: 2 55 | } 56 | } 57 | layer { 58 | name: "relu2_3" 59 | type: "ReLU" 60 | bottom: "pool2_3" 61 | top: "pool2_3" 62 | } 63 | layer { 64 | name: "conv3_3" 65 | type: "Convolution" 66 | bottom: "pool2_3" 67 | top: "conv3_3" 68 | convolution_param { 69 | num_output: 96 70 | kernel_size: 3 71 | } 72 | } 73 | layer { 74 | name: "pool3_3" 75 | type: "Pooling" 76 | bottom: "conv3_3" 77 | top: "pool3_3" 78 | pooling_param { 79 | pool: MAX 80 | kernel_size: 2 81 | stride: 2 82 | } 83 | } 84 | layer { 85 | name: "relu3_3" 86 | type: "ReLU" 87 | bottom: "pool3_3" 88 | top: "pool3_3" 89 | } 90 | layer { 91 | name: "conv4_3" 92 | type: "Convolution" 93 | bottom: "pool3_3" 94 | top: "conv4_3" 95 | convolution_param { 96 | num_output: 144 97 | kernel_size: 2 98 | } 99 | } 100 | layer { 101 | name: "relu4_3" 102 | type: "ReLU" 103 | bottom: "conv4_3" 104 | top: "conv4_3" 105 | } 106 | layer { 107 | name: "fc5_3" 108 | type: "InnerProduct" 109 | bottom: "conv4_3" 110 | top: "fc5_3" 111 | inner_product_param { 112 | num_output: 192 113 | } 114 | } 115 | layer { 116 | name: "relu5_3" 117 | type: "ReLU" 118 | bottom: "fc5_3" 119 | top: "fc5_3" 120 | } 121 | layer { 122 | name: "fc6_3" 123 | type: "InnerProduct" 124 | bottom: "fc5_3" 125 | top: "fc6_3" 126 | inner_product_param { 127 | num_output: 2 128 | } 129 | } 130 | layer { 131 | name: "cls_prob" 132 | type: "Softmax" 133 | bottom: "fc6_3" 134 | top: "cls_prob" 135 | } 136 | layer { 137 | name: "bbox_reg_3" 138 | type: "InnerProduct" 139 | bottom: "fc5_3" 140 | top: "bbox_reg_3" 141 | inner_product_param { 142 | num_output: 3 143 | } 144 | } 145 | layer { 146 | name: "rotate_reg_3" 147 | type: "InnerProduct" 148 | bottom: "fc5_3" 149 | top: "rotate_reg_3" 150 | inner_product_param { 151 | num_output: 1 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /pcn_model/PCN.caffemodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/pcn_model/PCN.caffemodel -------------------------------------------------------------------------------- /results/out1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out1.jpg -------------------------------------------------------------------------------- /results/out10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out10.jpg -------------------------------------------------------------------------------- /results/out11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out11.jpg -------------------------------------------------------------------------------- /results/out12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out12.jpg -------------------------------------------------------------------------------- /results/out13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out13.jpg -------------------------------------------------------------------------------- /results/out14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out14.jpg -------------------------------------------------------------------------------- /results/out15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out15.jpg -------------------------------------------------------------------------------- /results/out16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out16.jpg -------------------------------------------------------------------------------- /results/out17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out17.jpg -------------------------------------------------------------------------------- /results/out18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out18.jpg -------------------------------------------------------------------------------- /results/out19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out19.jpg -------------------------------------------------------------------------------- /results/out2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out2.jpg -------------------------------------------------------------------------------- /results/out20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out20.jpg -------------------------------------------------------------------------------- /results/out21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out21.jpg -------------------------------------------------------------------------------- /results/out22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out22.jpg -------------------------------------------------------------------------------- /results/out23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out23.jpg -------------------------------------------------------------------------------- /results/out24.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out24.jpg -------------------------------------------------------------------------------- /results/out25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out25.jpg -------------------------------------------------------------------------------- /results/out26.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out26.jpg -------------------------------------------------------------------------------- /results/out3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out3.jpg -------------------------------------------------------------------------------- /results/out4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out4.jpg -------------------------------------------------------------------------------- /results/out5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out5.jpg -------------------------------------------------------------------------------- /results/out6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out6.jpg -------------------------------------------------------------------------------- /results/out7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out7.jpg -------------------------------------------------------------------------------- /results/out8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out8.jpg -------------------------------------------------------------------------------- /results/out9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etosworld/etos-facedetector/184f654731d947d8ad4eff246bbddf9e3184c428/results/out9.jpg --------------------------------------------------------------------------------