diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index cc49818..6c150ab 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -7,3 +7,6 @@ include(mini-caffe/mini-caffe.cmake) add_executable(jfda main.cpp jfda.hpp jfda.cpp) target_link_libraries(jfda caffe ${OpenCV_LIBS}) + +add_executable(video video.cpp jfda.hpp jfda.cpp) +target_link_libraries(video caffe ${OpenCV_LIBS}) diff --git a/cpp/README.md b/cpp/README.md new file mode 100644 index 0000000..e39c643 --- /dev/null +++ b/cpp/README.md @@ -0,0 +1,4 @@ +CPP code for JFDA +================= + +View video demo on YouTube [here](https://www.youtube.com/watch?v=Ee9UT9Zf98s). diff --git a/cpp/convert.sh b/cpp/convert.sh new file mode 100644 index 0000000..d54e65f --- /dev/null +++ b/cpp/convert.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +ffmpeg -i ./build/test.mp4 -f mp3 -vn ./build/test.mp3 + +ffmpeg -i ./build/test.mp3 -i ./build/result.avi -f mp4 -y ./build/result.mp4 diff --git a/cpp/video.cpp b/cpp/video.cpp new file mode 100644 index 0000000..dbb903a --- /dev/null +++ b/cpp/video.cpp @@ -0,0 +1,53 @@ +#include +#include "./jfda.hpp" +#include "caffe/caffe.hpp" +#include +#include + +using namespace cv; +using namespace std; +using namespace jfda; + +int main(int argc, char* argv[]) { + VideoCapture cap("./test.mp4"); + if (!cap.isOpened()) { + cout << "Can\'t open test.mp4" << endl; + return 0; + } + double rate = cap.get(CV_CAP_PROP_FPS); + double width = cap.get(CV_CAP_PROP_FRAME_WIDTH); + double height = cap.get(CV_CAP_PROP_FRAME_HEIGHT); + int fourcc = CV_FOURCC('X', 'V', 'I', 'D'); + VideoWriter writer("./result.avi", fourcc, rate, Size(width, height), true); + string proto_dir = "../../proto/"; + string model_dir = "../../model/"; + JfdaDetector detector(proto_dir + "p.prototxt", model_dir + "p.caffemodel", + proto_dir + "r.prototxt", model_dir + "r.caffemodel", + proto_dir + "o.prototxt", model_dir + "o.caffemodel", + proto_dir + "l.prototxt", model_dir + "l.caffemodel", 0); + detector.SetMinSize(24); + Mat frame; + caffe::Profiler* profiler = caffe::Profiler::Get(); + while (cap.read(frame)) { + int64_t t1 = profiler->Now(); + vector faces = detector.Detect(frame); + int64_t t2 = profiler->Now(); + double fps = 1000000 / (t2 - t1); + for (int i = 0; i < faces.size(); i++) { + FaceInfo& face = faces[i]; + cv::rectangle(frame, face.bbox, Scalar(0, 0, 255), 2); + for (int j = 0; j < 5; j++) { + cv::circle(frame, face.landmark5[j], 2, Scalar(0, 255, 0), -1); + } + } + char buff[30]; + sprintf(buff, "%.2f FPS", fps); + cv::putText(frame, buff, Point2f(0, 10), FONT_HERSHEY_PLAIN, 1, Scalar(0, 255, 0)); + writer.write(frame); + cv::imshow("frame", frame); + if (waitKey(1) >= 0) { + break; + } + } + return 0; +} diff --git a/demo.py b/demo.py index 8f435b3..71a2f57 100644 --- a/demo.py +++ b/demo.py @@ -1,4 +1,5 @@ #!/usr/bin/env python2.7 +# pylint: disable=bad-indentation, no-member, invalid-name, line-too-long import os import argparse @@ -12,7 +13,11 @@ def main(args): 'proto/r.prototxt', 'model/r.caffemodel', 'proto/o.prototxt', 'model/o.caffemodel', 'proto/l.prototxt', 'model/l.caffemodel',] - detector = JfdaDetector(net) + if args.minicaffe: + from jfda.mdetector import MiniCaffeDetector + detector = MiniCaffeDetector(net) + else: + detector = JfdaDetector(net) if args.pnet_single: detector.set_pnet_single_forward(True) param = { @@ -63,6 +68,7 @@ def gen(img, bboxes, outname): parser = argparse.ArgumentParser() parser.add_argument('--gpu', type=int, default=-1, help='gpu id to use, -1 for cpu') parser.add_argument('--pnet-single', action='store_true', help='pnet use single forward') + parser.add_argument('--minicaffe', action='store_true', help='use minicaffe') args = parser.parse_args() if args.gpu >= 0: diff --git a/fddb.py b/fddb.py index c76d78f..d3ea6c5 100644 --- a/fddb.py +++ b/fddb.py @@ -1,4 +1,5 @@ #!/usr/bin/env python2.7 +# pylint: disable=bad-indentation, no-member, invalid-name, line-too-long import argparse import cv2 diff --git a/jfda/detector.py b/jfda/detector.py index 1a8e754..f48c4e4 100644 --- a/jfda/detector.py +++ b/jfda/detector.py @@ -95,6 +95,9 @@ def detect(self, img, ths, min_size, factor, debug=False): data[i] = cv2.resize(face, (24, 24)).transpose((2, 0, 1)) data = (data - 128) / 128 prob, bbox_pred, landmark_pred = self._forward(self.rnet, data, ['prob', 'bbox_pred', 'landmark_pred']) + prob = prob.reshape(n, 2) + bbox_pred = bbox_pred.reshape(n, 4) + landmark_pred = landmark_pred.reshape(n, 10) keep = prob[:, 1] > ths[1] bboxes = bboxes[keep] bboxes[:, 4] = prob[keep, 1] @@ -122,6 +125,9 @@ def detect(self, img, ths, min_size, factor, debug=False): data[i] = cv2.resize(face, (48, 48)).transpose((2, 0, 1)) data = (data - 128) / 128 prob, bbox_pred, landmark_pred = self._forward(self.onet, data, ['prob', 'bbox_pred', 'landmark_pred']) + prob = prob.reshape(n, 2) + bbox_pred = bbox_pred.reshape(n, 4) + landmark_pred = landmark_pred.reshape(n, 10) keep = prob[:, 1] > ths[2] bboxes = bboxes[keep] bboxes[:, 4] = prob[keep, 1] @@ -158,6 +164,7 @@ def detect(self, img, ths, min_size, factor, debug=False): data[i, (3*j):(3*j+3)] = patch data = (data - 128) / 128 offset = self._forward(self.lnet, data, ['landmark_offset'])[0] + offset = offset.reshape(n, 10) offset *= l.reshape((-1, 1)) bboxes[:, 9:] += offset timer.toc() diff --git a/jfda/mdetector.py b/jfda/mdetector.py new file mode 100644 index 0000000..7d61e1c --- /dev/null +++ b/jfda/mdetector.py @@ -0,0 +1,19 @@ +# pylint: disable=bad-indentation, no-member, invalid-name, line-too-long +from .detector import JfdaDetector +import minicaffe as caffe + + +class MiniCaffeDetector(JfdaDetector): + + def __init__(self, nets): + assert len(nets) in [2, 4, 6, 8], 'wrong number of nets' + self.pnet, self.rnet, self.onet, self.lnet = None, None, None, None + if len(nets) >= 2: + self.pnet = caffe.Net(nets[0], nets[1]) + if len(nets) >= 4: + self.rnet = caffe.Net(nets[2], nets[3]) + if len(nets) >= 6: + self.onet = caffe.Net(nets[4], nets[5]) + if len(nets) >= 8: + self.lnet = caffe.Net(nets[6], nets[7]) + self.pnet_single_forward = False