1 #if defined _MSC_VER && _MSC_VER >= 1400
2 #pragma warning( disable : 4201 4408 4127 4100)
3 #endif
4
5 #include "cvconfig.h"
6 #include <iostream>
7 #include <iomanip>
8 #include <cstdio>
9 #include "opencv2/core/cuda.hpp"
10 #include "opencv2/cudalegacy.hpp"
11 #include "opencv2/highgui.hpp"
12 #include "opencv2/imgproc.hpp"
13 #include "opencv2/objdetect.hpp"
14 #include "opencv2/objdetect/objdetect_c.h"
15
16 using namespace std;
17 using namespace cv;
18
19
20 #if !defined(HAVE_CUDA) || defined(__arm__)
21
main(int,const char **)22 int main( int, const char** )
23 {
24 #if !defined(HAVE_CUDA)
25 std::cout << "CUDA support is required (CMake key 'WITH_CUDA' must be true)." << std::endl;
26 #endif
27
28 #if defined(__arm__)
29 std::cout << "Unsupported for ARM CUDA library." << std::endl;
30 #endif
31
32 return 0;
33 }
34
35 #else
36
37
38 const Size2i preferredVideoFrameSize(640, 480);
39 const cv::String wndTitle = "NVIDIA Computer Vision :: Haar Classifiers Cascade";
40
41
matPrint(Mat & img,int lineOffsY,Scalar fontColor,const string & ss)42 static void matPrint(Mat &img, int lineOffsY, Scalar fontColor, const string &ss)
43 {
44 int fontFace = FONT_HERSHEY_DUPLEX;
45 double fontScale = 0.8;
46 int fontThickness = 2;
47 Size fontSize = cv::getTextSize("T[]", fontFace, fontScale, fontThickness, 0);
48
49 Point org;
50 org.x = 1;
51 org.y = 3 * fontSize.height * (lineOffsY + 1) / 2;
52 putText(img, ss, org, fontFace, fontScale, Scalar(0,0,0), 5*fontThickness/2, 16);
53 putText(img, ss, org, fontFace, fontScale, fontColor, fontThickness, 16);
54 }
55
56
displayState(Mat & canvas,bool bHelp,bool bGpu,bool bLargestFace,bool bFilter,double fps)57 static void displayState(Mat &canvas, bool bHelp, bool bGpu, bool bLargestFace, bool bFilter, double fps)
58 {
59 Scalar fontColorRed(0,0,255);
60 Scalar fontColorNV(0,185,118);
61
62 ostringstream ss;
63 ss << "FPS = " << setprecision(1) << fixed << fps;
64 matPrint(canvas, 0, fontColorRed, ss.str());
65 ss.str("");
66 ss << "[" << canvas.cols << "x" << canvas.rows << "], " <<
67 (bGpu ? "GPU, " : "CPU, ") <<
68 (bLargestFace ? "OneFace, " : "MultiFace, ") <<
69 (bFilter ? "Filter:ON" : "Filter:OFF");
70 matPrint(canvas, 1, fontColorRed, ss.str());
71
72 if (bHelp)
73 {
74 matPrint(canvas, 2, fontColorNV, "Space - switch GPU / CPU");
75 matPrint(canvas, 3, fontColorNV, "M - switch OneFace / MultiFace");
76 matPrint(canvas, 4, fontColorNV, "F - toggle rectangles Filter");
77 matPrint(canvas, 5, fontColorNV, "H - toggle hotkeys help");
78 }
79 else
80 {
81 matPrint(canvas, 2, fontColorNV, "H - toggle hotkeys help");
82 }
83 }
84
85
process(Mat * srcdst,Ncv32u width,Ncv32u height,NcvBool bFilterRects,NcvBool bLargestFace,HaarClassifierCascadeDescriptor & haar,NCVVector<HaarStage64> & d_haarStages,NCVVector<HaarClassifierNode128> & d_haarNodes,NCVVector<HaarFeature64> & d_haarFeatures,NCVVector<HaarStage64> & h_haarStages,INCVMemAllocator & gpuAllocator,INCVMemAllocator & cpuAllocator,cudaDeviceProp & devProp)86 static NCVStatus process(Mat *srcdst,
87 Ncv32u width, Ncv32u height,
88 NcvBool bFilterRects, NcvBool bLargestFace,
89 HaarClassifierCascadeDescriptor &haar,
90 NCVVector<HaarStage64> &d_haarStages, NCVVector<HaarClassifierNode128> &d_haarNodes,
91 NCVVector<HaarFeature64> &d_haarFeatures, NCVVector<HaarStage64> &h_haarStages,
92 INCVMemAllocator &gpuAllocator,
93 INCVMemAllocator &cpuAllocator,
94 cudaDeviceProp &devProp)
95 {
96 ncvAssertReturn(!((srcdst == NULL) ^ gpuAllocator.isCounting()), NCV_NULL_PTR);
97
98 NCVStatus ncvStat;
99
100 NCV_SET_SKIP_COND(gpuAllocator.isCounting());
101
102 NCVMatrixAlloc<Ncv8u> d_src(gpuAllocator, width, height);
103 ncvAssertReturn(d_src.isMemAllocated(), NCV_ALLOCATOR_BAD_ALLOC);
104 NCVMatrixAlloc<Ncv8u> h_src(cpuAllocator, width, height);
105 ncvAssertReturn(h_src.isMemAllocated(), NCV_ALLOCATOR_BAD_ALLOC);
106 NCVVectorAlloc<NcvRect32u> d_rects(gpuAllocator, 100);
107 ncvAssertReturn(d_rects.isMemAllocated(), NCV_ALLOCATOR_BAD_ALLOC);
108
109 NCV_SKIP_COND_BEGIN
110
111 for (Ncv32u i=0; i<(Ncv32u)srcdst->rows; i++)
112 {
113 memcpy(h_src.ptr() + i * h_src.stride(), srcdst->ptr(i), srcdst->cols);
114 }
115
116 ncvStat = h_src.copySolid(d_src, 0);
117 ncvAssertReturnNcvStat(ncvStat);
118 ncvAssertCUDAReturn(cudaStreamSynchronize(0), NCV_CUDA_ERROR);
119
120 NCV_SKIP_COND_END
121
122 NcvSize32u roi;
123 roi.width = d_src.width();
124 roi.height = d_src.height();
125
126 Ncv32u numDetections;
127 ncvStat = ncvDetectObjectsMultiScale_device(
128 d_src, roi, d_rects, numDetections, haar, h_haarStages,
129 d_haarStages, d_haarNodes, d_haarFeatures,
130 haar.ClassifierSize,
131 (bFilterRects || bLargestFace) ? 4 : 0,
132 1.2f, 1,
133 (bLargestFace ? NCVPipeObjDet_FindLargestObject : 0)
134 | NCVPipeObjDet_VisualizeInPlace,
135 gpuAllocator, cpuAllocator, devProp, 0);
136 ncvAssertReturnNcvStat(ncvStat);
137 ncvAssertCUDAReturn(cudaStreamSynchronize(0), NCV_CUDA_ERROR);
138
139 NCV_SKIP_COND_BEGIN
140
141 ncvStat = d_src.copySolid(h_src, 0);
142 ncvAssertReturnNcvStat(ncvStat);
143 ncvAssertCUDAReturn(cudaStreamSynchronize(0), NCV_CUDA_ERROR);
144
145 for (Ncv32u i=0; i<(Ncv32u)srcdst->rows; i++)
146 {
147 memcpy(srcdst->ptr(i), h_src.ptr() + i * h_src.stride(), srcdst->cols);
148 }
149
150 NCV_SKIP_COND_END
151
152 return NCV_SUCCESS;
153 }
154
155
main(int argc,const char ** argv)156 int main(int argc, const char** argv)
157 {
158 cout << "OpenCV / NVIDIA Computer Vision" << endl;
159 cout << "Face Detection in video and live feed" << endl;
160 cout << "Syntax: exename <cascade_file> <image_or_video_or_cameraid>" << endl;
161 cout << "=========================================" << endl;
162
163 ncvAssertPrintReturn(cv::cuda::getCudaEnabledDeviceCount() != 0, "No GPU found or the library is compiled without CUDA support", -1);
164 ncvAssertPrintReturn(argc == 3, "Invalid number of arguments", -1);
165
166 cv::cuda::printShortCudaDeviceInfo(cv::cuda::getDevice());
167
168 string cascadeName = argv[1];
169 string inputName = argv[2];
170
171 NCVStatus ncvStat;
172 NcvBool bQuit = false;
173 VideoCapture capture;
174 Size2i frameSize;
175
176 //open content source
177 Mat image = imread(inputName);
178 Mat frame;
179 if (!image.empty())
180 {
181 frameSize.width = image.cols;
182 frameSize.height = image.rows;
183 }
184 else
185 {
186 if (!capture.open(inputName))
187 {
188 int camid = -1;
189
190 istringstream ss(inputName);
191 int x = 0;
192 ss >> x;
193
194 ncvAssertPrintReturn(capture.open(camid) != 0, "Can't open source", -1);
195 }
196
197 capture >> frame;
198 ncvAssertPrintReturn(!frame.empty(), "Empty video source", -1);
199
200 frameSize.width = frame.cols;
201 frameSize.height = frame.rows;
202 }
203
204 NcvBool bUseGPU = true;
205 NcvBool bLargestObject = false;
206 NcvBool bFilterRects = true;
207 NcvBool bHelpScreen = false;
208
209 CascadeClassifier classifierOpenCV;
210 ncvAssertPrintReturn(classifierOpenCV.load(cascadeName) != 0, "Error (in OpenCV) opening classifier", -1);
211
212 int devId;
213 ncvAssertCUDAReturn(cudaGetDevice(&devId), -1);
214 cudaDeviceProp devProp;
215 ncvAssertCUDAReturn(cudaGetDeviceProperties(&devProp, devId), -1);
216 cout << "Using GPU: " << devId << "(" << devProp.name <<
217 "), arch=" << devProp.major << "." << devProp.minor << endl;
218
219 //==============================================================================
220 //
221 // Load the classifier from file (assuming its size is about 1 mb)
222 // using a simple allocator
223 //
224 //==============================================================================
225
226 NCVMemNativeAllocator gpuCascadeAllocator(NCVMemoryTypeDevice, static_cast<Ncv32u>(devProp.textureAlignment));
227 ncvAssertPrintReturn(gpuCascadeAllocator.isInitialized(), "Error creating cascade GPU allocator", -1);
228 NCVMemNativeAllocator cpuCascadeAllocator(NCVMemoryTypeHostPinned, static_cast<Ncv32u>(devProp.textureAlignment));
229 ncvAssertPrintReturn(cpuCascadeAllocator.isInitialized(), "Error creating cascade CPU allocator", -1);
230
231 Ncv32u haarNumStages, haarNumNodes, haarNumFeatures;
232 ncvStat = ncvHaarGetClassifierSize(cascadeName, haarNumStages, haarNumNodes, haarNumFeatures);
233 ncvAssertPrintReturn(ncvStat == NCV_SUCCESS, "Error reading classifier size (check the file)", -1);
234
235 NCVVectorAlloc<HaarStage64> h_haarStages(cpuCascadeAllocator, haarNumStages);
236 ncvAssertPrintReturn(h_haarStages.isMemAllocated(), "Error in cascade CPU allocator", -1);
237 NCVVectorAlloc<HaarClassifierNode128> h_haarNodes(cpuCascadeAllocator, haarNumNodes);
238 ncvAssertPrintReturn(h_haarNodes.isMemAllocated(), "Error in cascade CPU allocator", -1);
239 NCVVectorAlloc<HaarFeature64> h_haarFeatures(cpuCascadeAllocator, haarNumFeatures);
240
241 ncvAssertPrintReturn(h_haarFeatures.isMemAllocated(), "Error in cascade CPU allocator", -1);
242
243 HaarClassifierCascadeDescriptor haar;
244 ncvStat = ncvHaarLoadFromFile_host(cascadeName, haar, h_haarStages, h_haarNodes, h_haarFeatures);
245 ncvAssertPrintReturn(ncvStat == NCV_SUCCESS, "Error loading classifier", -1);
246
247 NCVVectorAlloc<HaarStage64> d_haarStages(gpuCascadeAllocator, haarNumStages);
248 ncvAssertPrintReturn(d_haarStages.isMemAllocated(), "Error in cascade GPU allocator", -1);
249 NCVVectorAlloc<HaarClassifierNode128> d_haarNodes(gpuCascadeAllocator, haarNumNodes);
250 ncvAssertPrintReturn(d_haarNodes.isMemAllocated(), "Error in cascade GPU allocator", -1);
251 NCVVectorAlloc<HaarFeature64> d_haarFeatures(gpuCascadeAllocator, haarNumFeatures);
252 ncvAssertPrintReturn(d_haarFeatures.isMemAllocated(), "Error in cascade GPU allocator", -1);
253
254 ncvStat = h_haarStages.copySolid(d_haarStages, 0);
255 ncvAssertPrintReturn(ncvStat == NCV_SUCCESS, "Error copying cascade to GPU", -1);
256 ncvStat = h_haarNodes.copySolid(d_haarNodes, 0);
257 ncvAssertPrintReturn(ncvStat == NCV_SUCCESS, "Error copying cascade to GPU", -1);
258 ncvStat = h_haarFeatures.copySolid(d_haarFeatures, 0);
259 ncvAssertPrintReturn(ncvStat == NCV_SUCCESS, "Error copying cascade to GPU", -1);
260
261 //==============================================================================
262 //
263 // Calculate memory requirements and create real allocators
264 //
265 //==============================================================================
266
267 NCVMemStackAllocator gpuCounter(static_cast<Ncv32u>(devProp.textureAlignment));
268 ncvAssertPrintReturn(gpuCounter.isInitialized(), "Error creating GPU memory counter", -1);
269 NCVMemStackAllocator cpuCounter(static_cast<Ncv32u>(devProp.textureAlignment));
270 ncvAssertPrintReturn(cpuCounter.isInitialized(), "Error creating CPU memory counter", -1);
271
272 ncvStat = process(NULL, frameSize.width, frameSize.height,
273 false, false, haar,
274 d_haarStages, d_haarNodes,
275 d_haarFeatures, h_haarStages,
276 gpuCounter, cpuCounter, devProp);
277 ncvAssertPrintReturn(ncvStat == NCV_SUCCESS, "Error in memory counting pass", -1);
278
279 NCVMemStackAllocator gpuAllocator(NCVMemoryTypeDevice, gpuCounter.maxSize(), static_cast<Ncv32u>(devProp.textureAlignment));
280 ncvAssertPrintReturn(gpuAllocator.isInitialized(), "Error creating GPU memory allocator", -1);
281 NCVMemStackAllocator cpuAllocator(NCVMemoryTypeHostPinned, cpuCounter.maxSize(), static_cast<Ncv32u>(devProp.textureAlignment));
282 ncvAssertPrintReturn(cpuAllocator.isInitialized(), "Error creating CPU memory allocator", -1);
283
284 printf("Initialized for frame size [%dx%d]\n", frameSize.width, frameSize.height);
285
286 //==============================================================================
287 //
288 // Main processing loop
289 //
290 //==============================================================================
291
292 namedWindow(wndTitle, 1);
293 Mat frameDisp;
294
295 do
296 {
297 Mat gray;
298 cvtColor((image.empty() ? frame : image), gray, cv::COLOR_BGR2GRAY);
299
300 //
301 // process
302 //
303
304 NcvSize32u minSize = haar.ClassifierSize;
305 if (bLargestObject)
306 {
307 Ncv32u ratioX = preferredVideoFrameSize.width / minSize.width;
308 Ncv32u ratioY = preferredVideoFrameSize.height / minSize.height;
309 Ncv32u ratioSmallest = min(ratioX, ratioY);
310 ratioSmallest = max((Ncv32u)(ratioSmallest / 2.5f), (Ncv32u)1);
311 minSize.width *= ratioSmallest;
312 minSize.height *= ratioSmallest;
313 }
314
315 Ncv32f avgTime;
316 NcvTimer timer = ncvStartTimer();
317
318 if (bUseGPU)
319 {
320 ncvStat = process(&gray, frameSize.width, frameSize.height,
321 bFilterRects, bLargestObject, haar,
322 d_haarStages, d_haarNodes,
323 d_haarFeatures, h_haarStages,
324 gpuAllocator, cpuAllocator, devProp);
325 ncvAssertPrintReturn(ncvStat == NCV_SUCCESS, "Error in memory counting pass", -1);
326 }
327 else
328 {
329 vector<Rect> rectsOpenCV;
330
331 classifierOpenCV.detectMultiScale(
332 gray,
333 rectsOpenCV,
334 1.2f,
335 bFilterRects ? 4 : 0,
336 (bLargestObject ? CV_HAAR_FIND_BIGGEST_OBJECT : 0)
337 | CV_HAAR_SCALE_IMAGE,
338 Size(minSize.width, minSize.height));
339
340 for (size_t rt = 0; rt < rectsOpenCV.size(); ++rt)
341 rectangle(gray, rectsOpenCV[rt], Scalar(255));
342 }
343
344 avgTime = (Ncv32f)ncvEndQueryTimerMs(timer);
345
346 cvtColor(gray, frameDisp, cv::COLOR_GRAY2BGR);
347 displayState(frameDisp, bHelpScreen, bUseGPU, bLargestObject, bFilterRects, 1000.0f / avgTime);
348 imshow(wndTitle, frameDisp);
349
350 //handle input
351 switch (cv::waitKey(3))
352 {
353 case ' ':
354 bUseGPU = !bUseGPU;
355 break;
356 case 'm':
357 case 'M':
358 bLargestObject = !bLargestObject;
359 break;
360 case 'f':
361 case 'F':
362 bFilterRects = !bFilterRects;
363 break;
364 case 'h':
365 case 'H':
366 bHelpScreen = !bHelpScreen;
367 break;
368 case 27:
369 bQuit = true;
370 break;
371 }
372
373 // For camera and video file, capture the next image
374 if (capture.isOpened())
375 {
376 capture >> frame;
377 if (frame.empty())
378 {
379 break;
380 }
381 }
382 } while (!bQuit);
383
384 cv::destroyWindow(wndTitle);
385
386 return 0;
387 }
388
389 #endif //!defined(HAVE_CUDA)
390