diff --git a/modules/face/include/opencv2/face/facemark.hpp b/modules/face/include/opencv2/face/facemark.hpp index d0f456d677c..86e9384342e 100644 --- a/modules/face/include/opencv2/face/facemark.hpp +++ b/modules/face/include/opencv2/face/facemark.hpp @@ -74,8 +74,8 @@ class CV_EXPORTS_W Facemark : public virtual Algorithm @endcode */ CV_WRAP virtual bool fit( InputArray image, - const std::vector& faces, - CV_OUT std::vector >& landmarks ) = 0; + InputArray faces, + OutputArrayOfArrays landmarks) = 0; }; /* Facemark*/ diff --git a/modules/face/include/opencv2/face/facemarkAAM.hpp b/modules/face/include/opencv2/face/facemarkAAM.hpp index d0a5dca0847..6f96e4a22fd 100644 --- a/modules/face/include/opencv2/face/facemarkAAM.hpp +++ b/modules/face/include/opencv2/face/facemarkAAM.hpp @@ -146,7 +146,7 @@ class CV_EXPORTS_W FacemarkAAM : public FacemarkTrain }; //! overload with additional Config structures - virtual bool fitConfig( InputArray image, const std::vector& roi, std::vector >& _landmarks, const std::vector &runtime_params ) = 0; + virtual bool fitConfig( InputArray image, InputArray roi, OutputArrayOfArrays _landmarks, const std::vector &runtime_params ) = 0; //! initializer diff --git a/modules/face/samples/landmarks_demo.py b/modules/face/samples/landmarks_demo.py new file mode 100644 index 00000000000..4c6c4935975 --- /dev/null +++ b/modules/face/samples/landmarks_demo.py @@ -0,0 +1,29 @@ +import random +import numpy as np +import cv2 as cv + +frame1 = cv.imread(cv.samples.findFile('lena.jpg')) +if frame1 is None: + print("image not found") + exit() +frame = np.vstack((frame1,frame1)) +facemark = cv.face.createFacemarkLBF() +try: + facemark.loadModel(cv.samples.findFile('lbfmodel.yaml')) +except cv.error: + print("Model not found\nlbfmodel.yaml can be download at") + print("https://raw.githubusercontent.com/kurnianggoro/GSOC2017/master/data/lbfmodel.yaml") +cascade = cv.CascadeClassifier(cv.samples.findFile('lbpcascade_frontalface_improved.xml')) +if cascade.empty() : + print("cascade not found") + exit() +faces = cascade.detectMultiScale(frame, 1.05, 3, cv.CASCADE_SCALE_IMAGE, (30, 30)) +ok, landmarks = facemark.fit(frame, faces=faces) +cv.imshow("Image", frame) +for marks in landmarks: + couleur = (random.randint(0,255), + random.randint(0,255), + random.randint(0,255)) + cv.face.drawFacemarks(frame, marks, couleur) +cv.imshow("Image Landmarks", frame) +cv.waitKey() diff --git a/modules/face/src/face_alignmentimpl.hpp b/modules/face/src/face_alignmentimpl.hpp index d6cbe0817a4..352e0f60bbd 100644 --- a/modules/face/src/face_alignmentimpl.hpp +++ b/modules/face/src/face_alignmentimpl.hpp @@ -73,7 +73,7 @@ class FacemarkKazemiImpl : public FacemarkKazemi{ void loadModel(String fs) CV_OVERRIDE; bool setFaceDetector(FN_FaceDetector f, void* userdata) CV_OVERRIDE; bool getFaces(InputArray image, OutputArray faces) CV_OVERRIDE; - bool fit(InputArray image, const std::vector& faces, CV_OUT std::vector >& landmarks ) CV_OVERRIDE; + bool fit(InputArray image, InputArray faces, OutputArrayOfArrays landmarks ) CV_OVERRIDE; void training(String imageList, String groundTruth); bool training(vector& images, vector< vector >& landmarks,string filename,Size scale,string modelFilename) CV_OVERRIDE; // Destructor for the class. diff --git a/modules/face/src/facemarkAAM.cpp b/modules/face/src/facemarkAAM.cpp index c08660226f7..5e1370e3f25 100644 --- a/modules/face/src/facemarkAAM.cpp +++ b/modules/face/src/facemarkAAM.cpp @@ -103,11 +103,11 @@ class FacemarkAAMImpl : public FacemarkAAM { bool getData(void * items) CV_OVERRIDE; - bool fitConfig( InputArray image, const std::vector& roi, std::vector >& _landmarks, const std::vector &runtime_params ) CV_OVERRIDE; + bool fitConfig( InputArray image, InputArray roi, OutputArrayOfArrays _landmarks, const std::vector &runtime_params ) CV_OVERRIDE; protected: - bool fit( InputArray image, const std::vector& faces, CV_OUT std::vector >& landmarks ) CV_OVERRIDE; + bool fit( InputArray image, InputArray faces, OutputArrayOfArrays landmarks ) CV_OVERRIDE; bool fitImpl( const Mat image, std::vector& landmarks,const Mat R,const Point2f T,const float scale, const int sclIdx=0 ); bool addTrainingSample(InputArray image, InputArray landmarks) CV_OVERRIDE; @@ -322,18 +322,54 @@ void FacemarkAAMImpl::training(void* parameters){ if(params.verbose) printf("Training is completed\n"); } -bool FacemarkAAMImpl::fit( InputArray image, const std::vector& roi, CV_OUT std::vector >& _landmarks ) +/** + * @brief Copy the contents of a corners vector to an OutputArray, settings its size. + */ +static void _copyVector2Output(std::vector< std::vector< Point2f > > &vec, OutputArrayOfArrays out) +{ + out.create((int)vec.size(), 1, CV_32FC2); + + if (out.isMatVector()) { + for (unsigned int i = 0; i < vec.size(); i++) { + out.create(68, 1, CV_32FC2, i); + Mat &m = out.getMatRef(i); + Mat(Mat(vec[i]).t()).copyTo(m); + } + } + else if (out.isUMatVector()) { + for (unsigned int i = 0; i < vec.size(); i++) { + out.create(68, 1, CV_32FC2, i); + UMat &m = out.getUMatRef(i); + Mat(Mat(vec[i]).t()).copyTo(m); + } + } + else if (out.kind() == _OutputArray::STD_VECTOR_VECTOR) { + for (unsigned int i = 0; i < vec.size(); i++) { + out.create(68, 1, CV_32FC2, i); + Mat m = out.getMat(i); + Mat(Mat(vec[i]).t()).copyTo(m); + } + } + else { + CV_Error(cv::Error::StsNotImplemented, + "Only Mat vector, UMat vector, and vector OutputArrays are currently supported."); + } +} + + +bool FacemarkAAMImpl::fit( InputArray image, InputArray roi, OutputArrayOfArrays _landmarks ) { std::vector config; // empty return fitConfig(image, roi, _landmarks, config); } -bool FacemarkAAMImpl::fitConfig( InputArray image, const std::vector& roi, std::vector >& _landmarks, const std::vector &configs ) +bool FacemarkAAMImpl::fitConfig( InputArray image, InputArray roi, OutputArrayOfArrays _landmarks, const std::vector &configs ) { - const std::vector & faces = roi; + Mat roimat = roi.getMat(); + std::vector faces = roimat.reshape(4, roimat.rows); if(faces.size()<1) return false; - std::vector > & landmarks = _landmarks; + std::vector > landmarks; landmarks.resize(faces.size()); Mat img = image.getMat(); @@ -354,6 +390,7 @@ bool FacemarkAAMImpl::fitConfig( InputArray image, const std::vector& roi, fitImpl(img, landmarks[i], R,t, scale); } } + _copyVector2Output(landmarks, _landmarks); return true; } diff --git a/modules/face/src/facemarkLBF.cpp b/modules/face/src/facemarkLBF.cpp index 286d2f5866d..642452b2d69 100644 --- a/modules/face/src/facemarkLBF.cpp +++ b/modules/face/src/facemarkLBF.cpp @@ -115,7 +115,7 @@ class FacemarkLBFImpl : public FacemarkLBF { protected: - bool fit( InputArray image, const std::vector & faces, std::vector > & landmarks ) CV_OVERRIDE;//!< from many ROIs + bool fit(InputArray image, InputArray faces, OutputArrayOfArrays landmarks) CV_OVERRIDE; bool fitImpl( const Mat image, std::vector & landmarks );//!< from a face bool addTrainingSample(InputArray image, InputArray landmarks) CV_OVERRIDE; @@ -370,12 +370,47 @@ void FacemarkLBFImpl::training(void* parameters){ isModelTrained = true; } -bool FacemarkLBFImpl::fit( InputArray image, const std::vector & roi, CV_OUT std::vector > & _landmarks ) +/** + * @brief Copy the contents of a corners vector to an OutputArray, settings its size. + */ +static void _copyVector2Output(std::vector< std::vector< Point2f > > &vec, OutputArrayOfArrays out) { - const std::vector & faces = roi; + out.create((int)vec.size(), 1, CV_32FC2); + + if (out.isMatVector()) { + for (unsigned int i = 0; i < vec.size(); i++) { + out.create(68, 1, CV_32FC2, i); + Mat &m = out.getMatRef(i); + Mat(Mat(vec[i]).t()).copyTo(m); + } + } + else if (out.isUMatVector()) { + for (unsigned int i = 0; i < vec.size(); i++) { + out.create(68, 1, CV_32FC2, i); + UMat &m = out.getUMatRef(i); + Mat(Mat(vec[i]).t()).copyTo(m); + } + } + else if (out.kind() == _OutputArray::STD_VECTOR_VECTOR) { + for (unsigned int i = 0; i < vec.size(); i++) { + out.create(68, 1, CV_32FC2, i); + Mat m = out.getMat(i); + Mat(Mat(vec[i]).t()).copyTo(m); + } + } + else { + CV_Error(cv::Error::StsNotImplemented, + "Only Mat vector, UMat vector, and vector OutputArrays are currently supported."); + } +} + +bool FacemarkLBFImpl::fit(InputArray image, InputArray roi, OutputArrayOfArrays _landmarks) +{ + Mat roimat = roi.getMat(); + std::vector faces = roimat.reshape(4, roimat.rows); if (faces.empty()) return false; - std::vector > & landmarks = _landmarks; + std::vector > landmarks; landmarks.resize(faces.size()); @@ -383,7 +418,7 @@ bool FacemarkLBFImpl::fit( InputArray image, const std::vector & roi, CV_O params.detectROI = faces[i]; fitImpl(image.getMat(), landmarks[i]); } - + _copyVector2Output(landmarks, _landmarks); return true; } diff --git a/modules/face/src/getlandmarks.cpp b/modules/face/src/getlandmarks.cpp index 262f4180aa8..9a416197273 100644 --- a/modules/face/src/getlandmarks.cpp +++ b/modules/face/src/getlandmarks.cpp @@ -168,15 +168,53 @@ void FacemarkKazemiImpl :: loadModel(String filename){ f.close(); isModelLoaded = true; } -bool FacemarkKazemiImpl::fit(InputArray img, const std::vector& roi, CV_OUT std::vector >& landmarks){ + +/** + * @brief Copy the contents of a corners vector to an OutputArray, settings its size. + */ +static void _copyVector2Output(std::vector< std::vector< Point2f > > &vec, OutputArrayOfArrays out) +{ + out.create((int)vec.size(), 1, CV_32FC2); + + if (out.isMatVector()) { + for (unsigned int i = 0; i < vec.size(); i++) { + out.create(68, 1, CV_32FC2, i); + Mat &m = out.getMatRef(i); + Mat(Mat(vec[i]).t()).copyTo(m); + } + } + else if (out.isUMatVector()) { + for (unsigned int i = 0; i < vec.size(); i++) { + out.create(68, 1, CV_32FC2, i); + UMat &m = out.getUMatRef(i); + Mat(Mat(vec[i]).t()).copyTo(m); + } + } + else if (out.kind() == _OutputArray::STD_VECTOR_VECTOR) { + for (unsigned int i = 0; i < vec.size(); i++) { + out.create(68, 1, CV_32FC2, i); + Mat m = out.getMat(i); + Mat(Mat(vec[i]).t()).copyTo(m); + } + } + else { + CV_Error(cv::Error::StsNotImplemented, + "Only Mat vector, UMat vector, and vector OutputArrays are currently supported."); + } +} + + +bool FacemarkKazemiImpl::fit(InputArray img, InputArray roi, OutputArrayOfArrays _landmarks) +{ if(!isModelLoaded){ String error_message = "No model loaded. Aborting...."; CV_Error(Error::StsBadArg, error_message); return false; } Mat image = img.getMat(); - const std::vector & faces = roi; - std::vector > & shapes = landmarks; + Mat roimat = roi.getMat(); + std::vector faces = roimat.reshape(4, roimat.rows); + std::vector > shapes; shapes.resize(faces.size()); if(image.empty()){ @@ -243,6 +281,7 @@ bool FacemarkKazemiImpl::fit(InputArray img, const std::vector& roi, CV_OU shapes[e][j].y=float(D.at(1,0)); } } + _copyVector2Output(shapes, _landmarks); return true; } }//cv