BitmapToolkit Scol plugin
ArFaceDetector.cpp
Go to the documentation of this file.
1/*
2-----------------------------------------------------------------------------
3This source file is part of OpenSpace3D
4For the latest info, see http://www.openspace3d.com
5
6Copyright (c) 2012 I-maginer
7
8This program is free software; you can redistribute it and/or modify it under
9the terms of the GNU Lesser General Public License as published by the Free Software
10Foundation; either version 2 of the License, or (at your option) any later
11version.
12
13This program is distributed in the hope that it will be useful, but WITHOUT
14ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
16
17You should have received a copy of the GNU Lesser General Public License along with
18this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19Place - Suite 330, Boston, MA 02111-1307, USA, or go to
20http://www.gnu.org/copyleft/lesser.txt
21
22-----------------------------------------------------------------------------
23*/
24
25#include "ArManager.h"
26#include "ArFaceDetector.h"
27#include <vector>
28#include <boost/filesystem.hpp>
29
31{
32 needUpdate = false;
33
34 try
35 {
36#ifdef ANDROID
37 std::string facesxml = CopyFileFromAssetToSD("btdata/lbpcascade_frontalface.xml", "lbpcascade_frontalface.xml", "btdata");
38 //std::string leyexml = CopyFileFromAssetToSD("btdata/haarcascade_mcs_lefteye.xml", "haarcascade_mcs_lefteye.xml", "btdata");
39 //std::string reyexml = CopyFileFromAssetToSD("btdata/haarcascade_mcs_righteye.xml", "haarcascade_mcs_righteye.xml", "btdata");
40 //std::string nosexml = CopyFileFromAssetToSD("btdata/haarcascade_mcs_nose.xml", "haarcascade_mcs_nose.xml", "btdata");
41 //std::string mouthxml = CopyFileFromAssetToSD("btdata/haarcascade_mcs_mouth.xml", "haarcascade_mcs_mouth.xml", "btdata");
42
43 mFaceClassifier.load(facesxml);
44 //mLeftEyeClassifier.load(leyexml);
45 //mRightEyeClassifier.load(reyexml);
46 //mNoseClassifier.load(nosexml);
47 //mMouthClassifier.load(mouthxml);
48#else
49 mFaceClassifier.load("plugins/btdata/lbpcascade_frontalface.xml");
50 //mLeftEyeClassifier.load("plugins/btdata/haarcascade_mcs_lefteye.xml");
51 //mRightEyeClassifier.load("plugins/btdata/haarcascade_mcs_righteye.xml");
52 //mNoseClassifier.load("plugins/btdata/haarcascade_mcs_nose.xml");
53 //mMouthClassifier.load("plugins/btdata/haarcascade_mcs_mouth.xml");
54#endif
55 }
56 catch(std::exception&)
57 {
58 MMechostr(MSKRUNTIME, "BitmapToolkit Error : Could not load training data for face detector");
59 }
60
61 // Run thread
62 StartThreading(boost::bind(&ArFaceDetector::GoThread, this));
63
64 //cv::namedWindow("debug");
65}
66
71
72std::vector<cv::Rect> ArFaceDetector::DetectFaces(const cv::Mat input, std::vector<ArMarker*> faceMarkers, aruco::CameraParameters camParam, bool reversedBitmap)
73{
74 std::vector<cv::Rect> outVector;
75 std::vector<cv::Rect> objList;
76
77 if (mFaceClassifier.empty())
78 return outVector;
79
80 mFaceClassifier.detectMultiScale(input, objList, 1.2, 4, cv::CASCADE_SCALE_IMAGE/*|CV_HAAR_DO_CANNY_PRUNING*/|cv::CASCADE_FIND_BIGGEST_OBJECT, cv::Size(30, 30));
81
82 for (unsigned int i = 0; i< objList.size(); i++)
83 {
84 cv::Mat objROI = input(objList[i]);
85 cv::Rect objRect = objList[i];
86
87 //search if rect is in the other markers area
88 bool tracked = false;
89 cv::Rect tRect = objRect;
90 tRect.width += 40;
91 tRect.height += 40;
92 tRect.x -= 20;
93 tRect.y -= 20;
94 for (unsigned int j = 0; j < faceMarkers.size() && !tracked; j++)
95 {
96 ArFaceMarker* fmarker = static_cast<ArFaceMarker*> (faceMarkers[j]);
97 if (fmarker->IsInitialized())
98 {
99 std::vector<cv::Point2f> coords = faceMarkers[j]->GetCorners();
100 if (coords.size() == 4)
101 {
102 for (unsigned k = 0; k < coords.size() && !tracked; k++)
103 {
104 if (tRect.contains(coords[k]))
105 tracked = true;
106 }
107
108 // double check
109 if (!tracked)
110 {
111 cv::Rect nRect;
112 nRect.x = (int)coords[0].x;
113 nRect.y = (int)coords[0].y;
114 nRect.width = (int)coords[2].x - (int)coords[0].x;
115 nRect.height = (int)coords[2].y - (int)coords[0].y;
116
117 std::vector<cv::Point2f> lrect;
118 lrect.push_back(cv::Point2f((float)objRect.x, (float)objRect.y));
119 lrect.push_back(cv::Point2f((float)objRect.x + objRect.width, (float)objRect.y));
120 lrect.push_back(cv::Point2f((float)objRect.x + objRect.width, (float)objRect.y + objRect.height));
121 lrect.push_back(cv::Point2f((float)objRect.x, (float)objRect.y + objRect.height));
122
123 for (unsigned k = 0; k < lrect.size() && !tracked; k++)
124 {
125 if (nRect.contains(lrect[k]))
126 tracked = true;
127 }
128 }
129 }
130 }
131 }
132
133 if (!tracked)
134 {
135 /*
136 cv::Mat faceMask = input(objRect);
137 std::vector<cv::Rect> LeyeList;
138 mLeftEyeClassifier.detectMultiScale(faceMask, LeyeList, 1.3, 6, cv::CASCADE_SCALE_IMAGE, cv::Size(20, 20));
139
140 std::vector<cv::Rect> ReyeList;
141 mRightEyeClassifier.detectMultiScale(faceMask, ReyeList, 1.3, 6, cv::CASCADE_SCALE_IMAGE, cv::Size(20, 20));
142
143 // mask the top of the head
144 cv::Mat bMask;
145 faceMask.copyTo(bMask);
146 cv::rectangle(bMask, cv::Rect(0, 0, bMask.cols, (int)((float)bMask.rows / 2.5f)), cv::Scalar(0), cv::FILLED);
147 std::vector<cv::Rect> noseList;
148 mNoseClassifier.detectMultiScale(bMask, noseList, 1.3, 4, cv::CASCADE_SCALE_IMAGE, cv::Size(20, 20));
149
150 std::vector<cv::Rect> mouthList;
151 mMouthClassifier.detectMultiScale(bMask, mouthList, 1.3, 6, cv::CASCADE_SCALE_IMAGE, cv::Size(20, 20));
152
153 if (!LeyeList.empty() && !ReyeList.empty() && !noseList.empty() && !mouthList.empty())
154 {
155 int hdiff = abs(LeyeList[0].y - ReyeList[0].y);
156 int sdiff = abs(LeyeList[0].width - ReyeList[0].width);
157 int eyemid = LeyeList[0].x + (abs((ReyeList[0].x + ReyeList[0].width) - LeyeList[0].x) / 2);
158 int nosepos = noseList[0].x + (noseList[0].width / 2);
159 int mdiff = abs(eyemid - nosepos);
160 float mindiff = (float)objRect.width / 10;
161 if (((float)hdiff < mindiff) && ((float)sdiff < mindiff) && ((float)mdiff < mindiff))
162 {
163 bool found = false;
164 for (unsigned int j = 0; j < faceMarkers.size() && !found; j++)
165 {
166 ArFaceMarker* fmarker = static_cast<ArFaceMarker*> (faceMarkers[j]);
167 if (!fmarker->IsInitialized())
168 {
169 std::vector<cv::Rect> lRects;
170 lRects.push_back(objRect);
171 //lRects.push_back(LeyeList[0]);
172 //lRects.push_back(ReyeList[0]);
173 //lRects.push_back(noseList[0]);
174 //lRects.push_back(mouthList[0]);
175 fmarker->SetImage(input, lRects);
176 found = true;
177 }
178 }
179 }
180 }
181 */
182
183 bool found = false;
184 for (unsigned int j = 0; j < faceMarkers.size() && !found; j++)
185 {
186 ArFaceMarker* fmarker = static_cast<ArFaceMarker*> (faceMarkers[j]);
187 if (!fmarker->IsInitialized())
188 {
189 std::vector<cv::Rect> lRects;
190 lRects.push_back(objRect);
191 fmarker->SetImage(input, lRects);
192 found = true;
193 }
194 }
195 }
196
197 outVector.push_back(objRect);
198 }
199
200 return outVector;
201}
202
204{
205 while (IsRunning())
206 {
207 if (needUpdate)
208 {
210 manager->GetLastData(mLastData);
211 needUpdate = false;
212
213 //small denoising
214 if (mLastData.gray.rows != 480 || mLastData.gray.cols != 640)
215 {
216 cv::Size nsize;
217 if (mLastData.gray.rows >= mLastData.gray.cols)
218 {
219 nsize.width = (int)(((float)mLastData.gray.cols * 480.0f) / (float)mLastData.gray.rows);
220 nsize.height = 480;
221 }
222 else
223 {
224 nsize.width = 640;
225 nsize.height = (int)(((float)mLastData.gray.rows * 640.0f) / (float)mLastData.gray.cols);
226 }
227 cv::resize(mLastData.gray, mLastData.gray, nsize, 0, 0, cv::INTER_LINEAR);
228 }
229
230 cv::equalizeHist(mLastData.gray, mLastData.gray);
231 // increase image contrast
232 //mLastData.gray.convertTo(mLastData.gray, -1, 1.1, 0);
233 //cv::blur(mLastData.gray, mLastData.gray, cv::Size(1, 1));
234
235 // Detect all markers seen on image
236 //read only
237 boost::shared_lock< boost::shared_mutex > lockList(manager->listMutex);
238
239 // hide all markers
240 bool needFaceDetect = false;
241 ArManager::MarkerList::iterator imarker;
242 std::vector<ArMarker*> faceMarkers;
243 for (imarker = manager->markerList.begin(); imarker != manager->markerList.end(); ++imarker)
244 {
245 if ((*imarker)->GetType() == ArMarker::AR_FACE_MARKER)
246 {
247 ArFaceMarker* fmarker = static_cast<ArFaceMarker*> (*imarker);
248 if (!fmarker->IsInitialized())
249 needFaceDetect = true;
250
251 faceMarkers.push_back((*imarker));
252 }
253 }
254
255 //list done
256 manager->listMutex.unlock();
257
258 if (needFaceDetect)
259 DetectFaces(mLastData.gray, faceMarkers, mLastData.arCamParam.camParam, mLastData.reversedBitmap);
260
261 std::vector<ArMarker*> foundMarkers;
262 for (unsigned int j = 0; j < faceMarkers.size(); j++)
263 {
264 ArFaceMarker* fmarker = static_cast<ArFaceMarker*> (faceMarkers[j]);
265 fmarker->Update(mLastData.gray, mLastData.image, mLastData.arCamParam.camParam, mLastData.reversedBitmap);
266 if (faceMarkers[j]->IsVisible())
267 {
268 foundMarkers.push_back(faceMarkers[j]);
269 }
270 }
271 }
272 else
273 {
274 //prevent cpu burn
275 boost::this_thread::sleep_for(boost::chrono::milliseconds(1)); //DO not burn too much CPU
276 }
277 }
278}
aruco::CameraParameters camParam
virtual ~ArFaceDetector()
void SetImage(const cv::Mat image, std::vector< cv::Rect > objRects)
bool IsInitialized()
void Update(cv::Mat frame, cv::Mat color, aruco::CameraParameters &camparam, bool reverse)
void GetLastData(LASTDATA &data)
MarkerList markerList
Definition ArManager.h:51
static ArManager * GetInstance()
Definition ArManager.cpp:70
boost::shared_mutex listMutex
Definition ArManager.h:50
@ AR_FACE_MARKER
Definition ArMarker.h:44
ArCameraParam arCamParam
cv::Mat image
cv::Mat gray
bool reversedBitmap
void StartThreading(std::function< void()> threadFunction)