diff --git a/modules/gpu/include/opencv2/gpu/gpu.hpp b/modules/gpu/include/opencv2/gpu/gpu.hpp index f487ac607..6b324f852 100644 --- a/modules/gpu/include/opencv2/gpu/gpu.hpp +++ b/modules/gpu/include/opencv2/gpu/gpu.hpp @@ -1520,7 +1520,7 @@ namespace cv // The cascade classifier class for object detection. class CV_EXPORTS CascadeClassifier_GPU { - public: + public: CascadeClassifier_GPU(); CascadeClassifier_GPU(const string& filename); ~CascadeClassifier_GPU(); @@ -1528,20 +1528,20 @@ namespace cv bool empty() const; bool load(const string& filename); void release(); - + /* returns number of detected objects */ int detectMultiScale( const GpuMat& image, GpuMat& objectsBuf, double scaleFactor=1.2, int minNeighbors=4, Size minSize=Size()); - + bool findLargestObject; bool visualizeInPlace; Size getClassifierSize() const; private: - - struct CascadeClassifierImpl; - CascadeClassifierImpl* impl; + + struct CascadeClassifierImpl; + CascadeClassifierImpl* impl; }; - + ////////////////////////////////// SURF ////////////////////////////////////////// class CV_EXPORTS SURF_GPU : public CvSURFParams diff --git a/modules/gpu/src/cascadeclassifier.cpp b/modules/gpu/src/cascadeclassifier.cpp index 35653a0ff..0ffd10c71 100644 --- a/modules/gpu/src/cascadeclassifier.cpp +++ b/modules/gpu/src/cascadeclassifier.cpp @@ -62,16 +62,22 @@ int cv::gpu::CascadeClassifier_GPU::detectMultiScale( const GpuMat& , GpuMat& , #else struct cv::gpu::CascadeClassifier_GPU::CascadeClassifierImpl -{ +{ CascadeClassifierImpl(const string& filename) : lastAllocatedFrameSize(-1, -1) { - ncvSetDebugOutputHandler(NCVDebugOutputHandler); + ncvSetDebugOutputHandler(NCVDebugOutputHandler); if (ncvStat != load(filename)) + { CV_Error(CV_GpuApiCallError, "Error in GPU cacade load"); - } - NCVStatus process(const GpuMat& src, GpuMat& objects, float scaleStep, int minNeighbors, bool findLargestObject, bool visualizeInPlace, NcvSize32u ncvMinSize, /*out*/unsigned int& numDetections) - { - calculateMemReqsAndAllocate(src.size()); + } + } + + + NCVStatus process(const GpuMat& src, GpuMat& objects, float scaleStep, int minNeighbors, + bool findLargestObject, bool visualizeInPlace, NcvSize32u ncvMinSize, + /*out*/unsigned int& numDetections) + { + calculateMemReqsAndAllocate(src.size()); NCVMemPtr src_beg; src_beg.ptr = (void*)src.ptr(); @@ -81,14 +87,8 @@ struct cv::gpu::CascadeClassifier_GPU::CascadeClassifierImpl src_seg.begin = src_beg; src_seg.size = src.step * src.rows; - NCVMatrixReuse d_src(src_seg, devProp.textureAlignment, src.cols, src.rows, src.step, true); - ncvAssertReturn(d_src.isMemReused(), NCV_ALLOCATOR_BAD_REUSE); - - //NCVMatrixAlloc d_src(*gpuAllocator, src.cols, src.rows); - //ncvAssertReturn(d_src.isMemAllocated(), NCV_ALLOCATOR_BAD_ALLOC); - - //NCVMatrixAlloc h_src(*cpuAllocator, src.cols, src.rows); - //ncvAssertReturn(h_src.isMemAllocated(), NCV_ALLOCATOR_BAD_ALLOC); + NCVMatrixReuse d_src(src_seg, devProp.textureAlignment, src.cols, src.rows, src.step, true); + ncvAssertReturn(d_src.isMemReused(), NCV_ALLOCATOR_BAD_REUSE); CV_Assert(objects.rows == 1); @@ -100,10 +100,8 @@ struct cv::gpu::CascadeClassifier_GPU::CascadeClassifierImpl objects_seg.begin = objects_beg; objects_seg.size = objects.step * objects.rows; NCVVectorReuse d_rects(objects_seg, objects.cols); - ncvAssertReturn(d_rects.isMemReused(), NCV_ALLOCATOR_BAD_REUSE); - //NCVVectorAlloc d_rects(*gpuAllocator, 100); - //ncvAssertReturn(d_rects.isMemAllocated(), NCV_ALLOCATOR_BAD_ALLOC); - + ncvAssertReturn(d_rects.isMemReused(), NCV_ALLOCATOR_BAD_REUSE); + NcvSize32u roi; roi.width = d_src.width(); roi.height = d_src.height(); @@ -111,7 +109,7 @@ struct cv::gpu::CascadeClassifier_GPU::CascadeClassifierImpl Ncv32u flags = 0; flags |= findLargestObject? NCVPipeObjDet_FindLargestObject : 0; flags |= visualizeInPlace ? NCVPipeObjDet_VisualizeInPlace : 0; - + ncvStat = ncvDetectObjectsMultiScale_device( d_src, roi, d_rects, numDetections, haar, *h_haarStages, *d_haarStages, *d_haarNodes, *d_haarFeatures, @@ -122,24 +120,28 @@ struct cv::gpu::CascadeClassifier_GPU::CascadeClassifierImpl *gpuAllocator, *cpuAllocator, devProp, 0); ncvAssertReturnNcvStat(ncvStat); ncvAssertCUDAReturn(cudaStreamSynchronize(0), NCV_CUDA_ERROR); - + return NCV_SUCCESS; } - //// - + + NcvSize32u getClassifierSize() const { return haar.ClassifierSize; } cv::Size getClassifierCvSize() const { return cv::Size(haar.ClassifierSize.width, haar.ClassifierSize.height); } + + private: + static void NCVDebugOutputHandler(const char* msg) { CV_Error(CV_GpuApiCallError, msg); } + NCVStatus load(const string& classifierFile) - { - int devId = cv::gpu::getDevice(); + { + int devId = cv::gpu::getDevice(); ncvAssertCUDAReturn(cudaGetDeviceProperties(&devProp, devId), NCV_CUDA_ERROR); // Load the classifier from file (assuming its size is about 1 mb) using a simple allocator - gpuCascadeAllocator = new NCVMemNativeAllocator(NCVMemoryTypeDevice, devProp.textureAlignment); + gpuCascadeAllocator = new NCVMemNativeAllocator(NCVMemoryTypeDevice, devProp.textureAlignment); cpuCascadeAllocator = new NCVMemNativeAllocator(NCVMemoryTypeHostPinned, devProp.textureAlignment); ncvAssertPrintReturn(gpuCascadeAllocator->isInitialized(), "Error creating cascade GPU allocator", NCV_CUDA_ERROR); @@ -149,12 +151,12 @@ private: ncvStat = ncvHaarGetClassifierSize(classifierFile, haarNumStages, haarNumNodes, haarNumFeatures); ncvAssertPrintReturn(ncvStat == NCV_SUCCESS, "Error reading classifier size (check the file)", NCV_FILE_ERROR); - h_haarStages = new NCVVectorAlloc(*cpuCascadeAllocator, haarNumStages); + h_haarStages = new NCVVectorAlloc(*cpuCascadeAllocator, haarNumStages); h_haarNodes = new NCVVectorAlloc(*cpuCascadeAllocator, haarNumNodes); h_haarFeatures = new NCVVectorAlloc(*cpuCascadeAllocator, haarNumFeatures); ncvAssertPrintReturn(h_haarStages->isMemAllocated(), "Error in cascade CPU allocator", NCV_CUDA_ERROR); - ncvAssertPrintReturn(h_haarNodes->isMemAllocated(), "Error in cascade CPU allocator", NCV_CUDA_ERROR); + ncvAssertPrintReturn(h_haarNodes->isMemAllocated(), "Error in cascade CPU allocator", NCV_CUDA_ERROR); ncvAssertPrintReturn(h_haarFeatures->isMemAllocated(), "Error in cascade CPU allocator", NCV_CUDA_ERROR); ncvStat = ncvHaarLoadFromFile_host(classifierFile, haar, *h_haarStages, *h_haarNodes, *h_haarFeatures); @@ -165,7 +167,7 @@ private: d_haarFeatures = new NCVVectorAlloc(*gpuCascadeAllocator, haarNumFeatures); ncvAssertPrintReturn(d_haarStages->isMemAllocated(), "Error in cascade GPU allocator", NCV_CUDA_ERROR); - ncvAssertPrintReturn(d_haarNodes->isMemAllocated(), "Error in cascade GPU allocator", NCV_CUDA_ERROR); + ncvAssertPrintReturn(d_haarNodes->isMemAllocated(), "Error in cascade GPU allocator", NCV_CUDA_ERROR); ncvAssertPrintReturn(d_haarFeatures->isMemAllocated(), "Error in cascade GPU allocator", NCV_CUDA_ERROR); ncvStat = h_haarStages->copySolid(*d_haarStages, 0); @@ -173,31 +175,33 @@ private: ncvStat = h_haarNodes->copySolid(*d_haarNodes, 0); ncvAssertPrintReturn(ncvStat == NCV_SUCCESS, "Error copying cascade to GPU", NCV_CUDA_ERROR); ncvStat = h_haarFeatures->copySolid(*d_haarFeatures, 0); - ncvAssertPrintReturn(ncvStat == NCV_SUCCESS, "Error copying cascade to GPU", NCV_CUDA_ERROR); + ncvAssertPrintReturn(ncvStat == NCV_SUCCESS, "Error copying cascade to GPU", NCV_CUDA_ERROR); return NCV_SUCCESS; } - //// + NCVStatus calculateMemReqsAndAllocate(const Size& frameSize) - { + { if (lastAllocatedFrameSize == frameSize) + { return NCV_SUCCESS; + } // Calculate memory requirements and create real allocators NCVMemStackAllocator gpuCounter(devProp.textureAlignment); NCVMemStackAllocator cpuCounter(devProp.textureAlignment); - ncvAssertPrintReturn(gpuCounter.isInitialized(), "Error creating GPU memory counter", NCV_CUDA_ERROR); + ncvAssertPrintReturn(gpuCounter.isInitialized(), "Error creating GPU memory counter", NCV_CUDA_ERROR); ncvAssertPrintReturn(cpuCounter.isInitialized(), "Error creating CPU memory counter", NCV_CUDA_ERROR); - + NCVMatrixAlloc d_src(gpuCounter, frameSize.width, frameSize.height); NCVMatrixAlloc h_src(cpuCounter, frameSize.width, frameSize.height); - ncvAssertReturn(d_src.isMemAllocated(), NCV_ALLOCATOR_BAD_ALLOC); + ncvAssertReturn(d_src.isMemAllocated(), NCV_ALLOCATOR_BAD_ALLOC); ncvAssertReturn(h_src.isMemAllocated(), NCV_ALLOCATOR_BAD_ALLOC); - NCVVectorAlloc d_rects(gpuCounter, 100); + NCVVectorAlloc d_rects(gpuCounter, 100); ncvAssertReturn(d_rects.isMemAllocated(), NCV_ALLOCATOR_BAD_ALLOC); NcvSize32u roi; @@ -209,23 +213,23 @@ private: ncvAssertReturnNcvStat(ncvStat); ncvAssertCUDAReturn(cudaStreamSynchronize(0), NCV_CUDA_ERROR); - - gpuAllocator = new NCVMemStackAllocator(NCVMemoryTypeDevice, gpuCounter.maxSize(), devProp.textureAlignment); + + gpuAllocator = new NCVMemStackAllocator(NCVMemoryTypeDevice, gpuCounter.maxSize(), devProp.textureAlignment); cpuAllocator = new NCVMemStackAllocator(NCVMemoryTypeHostPinned, cpuCounter.maxSize(), devProp.textureAlignment); ncvAssertPrintReturn(gpuAllocator->isInitialized(), "Error creating GPU memory allocator", NCV_CUDA_ERROR); - ncvAssertPrintReturn(cpuAllocator->isInitialized(), "Error creating CPU memory allocator", NCV_CUDA_ERROR); + ncvAssertPrintReturn(cpuAllocator->isInitialized(), "Error creating CPU memory allocator", NCV_CUDA_ERROR); return NCV_SUCCESS; } - //// + cudaDeviceProp devProp; NCVStatus ncvStat; - Ptr gpuCascadeAllocator; + Ptr gpuCascadeAllocator; Ptr cpuCascadeAllocator; - Ptr > h_haarStages; + Ptr > h_haarStages; Ptr > h_haarNodes; Ptr > h_haarFeatures; @@ -237,96 +241,103 @@ private: Size lastAllocatedFrameSize; - Ptr gpuAllocator; + Ptr gpuAllocator; Ptr cpuAllocator; }; - cv::gpu::CascadeClassifier_GPU::CascadeClassifier_GPU() : findLargestObject(false), visualizeInPlace(false), impl(0) {} cv::gpu::CascadeClassifier_GPU::CascadeClassifier_GPU(const string& filename) : findLargestObject(false), visualizeInPlace(false), impl(0) { load(filename); } cv::gpu::CascadeClassifier_GPU::~CascadeClassifier_GPU() { release(); } bool cv::gpu::CascadeClassifier_GPU::empty() const { return impl == 0; } - void cv::gpu::CascadeClassifier_GPU::release() { if (impl) { delete impl; impl = 0; } } + bool cv::gpu::CascadeClassifier_GPU::load(const string& filename) -{ +{ release(); impl = new CascadeClassifierImpl(filename); - return !this->empty(); + return !this->empty(); } + Size cv::gpu::CascadeClassifier_GPU::getClassifierSize() const { return this->empty() ? Size() : impl->getClassifierCvSize(); } - + + int cv::gpu::CascadeClassifier_GPU::detectMultiScale( const GpuMat& image, GpuMat& objectsBuf, double scaleFactor, int minNeighbors, Size minSize) -{ +{ CV_Assert( scaleFactor > 1 && image.depth() == CV_8U); CV_Assert( !this->empty()); - + const int defaultObjSearchNum = 100; if (objectsBuf.empty()) + { objectsBuf.create(1, defaultObjSearchNum, DataType::type); - + } + NcvSize32u ncvMinSize = impl->getClassifierSize(); if (ncvMinSize.width < (unsigned)minSize.width && ncvMinSize.height < (unsigned)minSize.height) { ncvMinSize.width = minSize.width; ncvMinSize.height = minSize.height; - } - + } + unsigned int numDetections; - NCVStatus ncvStat = impl->process(image, objectsBuf, (float)scaleFactor, minNeighbors, findLargestObject, visualizeInPlace, ncvMinSize, numDetections); + NCVStatus ncvStat = impl->process(image, objectsBuf, (float)scaleFactor, minNeighbors, findLargestObject, visualizeInPlace, ncvMinSize, numDetections); if (ncvStat != NCV_SUCCESS) + { CV_Error(CV_GpuApiCallError, "Error in face detectioln"); + } return numDetections; } + struct RectConvert { - Rect operator()(const NcvRect32u& nr) const { return Rect(nr.x, nr.y, nr.width, nr.height); } - NcvRect32u operator()(const Rect& nr) const - { - NcvRect32u rect; - rect.x = nr.x; - rect.y = nr.y; - rect.width = nr.width; - rect.height = nr.height; - return rect; - } + Rect operator()(const NcvRect32u& nr) const { return Rect(nr.x, nr.y, nr.width, nr.height); } + NcvRect32u operator()(const Rect& nr) const + { + NcvRect32u rect; + rect.x = nr.x; + rect.y = nr.y; + rect.width = nr.width; + rect.height = nr.height; + return rect; + } }; + void groupRectangles(std::vector &hypotheses, int groupThreshold, double eps, std::vector *weights) { - vector rects(hypotheses.size()); - std::transform(hypotheses.begin(), hypotheses.end(), rects.begin(), RectConvert()); - - if (weights) - { - vector weights_int; - weights_int.assign(weights->begin(), weights->end()); - cv::groupRectangles(rects, weights_int, groupThreshold, eps); - } - else - { - cv::groupRectangles(rects, groupThreshold, eps); - } - std::transform(rects.begin(), rects.end(), hypotheses.begin(), RectConvert()); - hypotheses.resize(rects.size()); + vector rects(hypotheses.size()); + std::transform(hypotheses.begin(), hypotheses.end(), rects.begin(), RectConvert()); + + if (weights) + { + vector weights_int; + weights_int.assign(weights->begin(), weights->end()); + cv::groupRectangles(rects, weights_int, groupThreshold, eps); + } + else + { + cv::groupRectangles(rects, groupThreshold, eps); + } + std::transform(rects.begin(), rects.end(), hypotheses.begin(), RectConvert()); + hypotheses.resize(rects.size()); } #if 1 /* loadFromXML implementation switch */ -NCVStatus loadFromXML(const std::string &filename, - HaarClassifierCascadeDescriptor &haar, - std::vector &haarStages, - std::vector &haarClassifierNodes, +NCVStatus loadFromXML(const std::string &filename, + HaarClassifierCascadeDescriptor &haar, + std::vector &haarStages, + std::vector &haarClassifierNodes, std::vector &haarFeatures) { NCVStatus ncvStat; @@ -347,12 +358,12 @@ NCVStatus loadFromXML(const std::string &filename, haarStages.resize(0); haarClassifierNodes.resize(0); haarFeatures.resize(0); - + Ptr oldCascade = (CvHaarClassifierCascade*)cvLoad(filename.c_str(), 0, 0, 0); if (oldCascade.empty()) + { return NCV_HAAR_XML_LOADING_EXCEPTION; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + } haar.ClassifierSize.width = oldCascade->orig_window_size.width; haar.ClassifierSize.height = oldCascade->orig_window_size.height; @@ -384,14 +395,14 @@ NCVStatus loadFromXML(const std::string &filename, HaarClassifierNodeDescriptor32 nodeLeft; if ( tree->left[n] <= 0 ) - { + { Ncv32f leftVal = tree->alpha[-tree->left[n]]; ncvStat = nodeLeft.create(leftVal); ncvAssertReturn(ncvStat == NCV_SUCCESS, ncvStat); bIsLeftNodeLeaf = true; } else - { + { Ncv32u leftNodeOffset = tree->left[n]; nodeLeft.create((Ncv32u)(h_TmpClassifierNotRootNodes.size() + leftNodeOffset - 1)); haar.bHasStumpsOnly = false; @@ -419,8 +430,8 @@ NCVStatus loadFromXML(const std::string &filename, Ncv32u featureId = 0; for(int l = 0; l < CV_HAAR_FEATURE_MAX; ++l) //by rects - { - Ncv32u rectX = feature->rect[l].r.x; + { + Ncv32u rectX = feature->rect[l].r.x; Ncv32u rectY = feature->rect[l].r.y; Ncv32u rectWidth = feature->rect[l].r.width; Ncv32u rectHeight = feature->rect[l].r.height; @@ -441,7 +452,7 @@ NCVStatus loadFromXML(const std::string &filename, HaarFeatureDescriptor32 tmpFeatureDesc; ncvStat = tmpFeatureDesc.create(haar.bNeedsTiltedII, bIsLeftNodeLeaf, bIsRightNodeLeaf, - featureId, haarFeatures.size() - featureId); + featureId, haarFeatures.size() - featureId); ncvAssertReturn(NCV_SUCCESS == ncvStat, ncvStat); curNode.setFeatureDesc(tmpFeatureDesc); @@ -466,8 +477,6 @@ NCVStatus loadFromXML(const std::string &filename, haarStages.push_back(curStage); } -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - //fill in cascade stats haar.NumStages = haarStages.size(); haar.NumClassifierRootNodes = haarClassifierNodes.size(); @@ -496,6 +505,7 @@ NCVStatus loadFromXML(const std::string &filename, } haarClassifierNodes[i].setRightNodeDesc(nodeRight); } + for (Ncv32u i=0; i +#include #include #include #include #include #include -#include -#include - using namespace std; using namespace cv; using namespace cv::gpu; +#if !defined(HAVE_CUDA) +int main(int argc, const char **argv) +{ + cout << "Please compile the library with CUDA support" << endl; + return -1; +} +#else + + void help() { cout << "Usage: ./cascadeclassifier \n" @@ -21,14 +31,8 @@ void help() } -void DetectAndDraw(Mat& img, CascadeClassifier_GPU& cascade); - - -String cascadeName = "../../data/haarcascades/haarcascade_frontalface_alt.xml"; -String nestedCascadeName = "../../data/haarcascades/haarcascade_eye_tree_eyeglasses.xml"; - - -template void convertAndResize(const T& src, T& gray, T& resized, double scale) +template +void convertAndResize(const T& src, T& gray, T& resized, double scale) { if (src.channels() == 3) { @@ -54,15 +58,16 @@ template void convertAndResize(const T& src, T& gray, T& resized, doubl void matPrint(Mat &img, int lineOffsY, Scalar fontColor, const ostringstream &ss) { - int fontFace = FONT_HERSHEY_PLAIN; - double fontScale = 1.5; + int fontFace = FONT_HERSHEY_DUPLEX; + double fontScale = 0.8; int fontThickness = 2; Size fontSize = cv::getTextSize("T[]", fontFace, fontScale, fontThickness, 0); Point org; org.x = 1; org.y = 3 * fontSize.height * (lineOffsY + 1) / 2; - putText(img, ss.str(), org, fontFace, fontScale, fontColor, fontThickness); + putText(img, ss.str(), org, fontFace, fontScale, CV_RGB(0,0,0), 5*fontThickness/2, 16); + putText(img, ss.str(), org, fontFace, fontScale, fontColor, fontThickness, 16); } @@ -72,25 +77,26 @@ void displayState(Mat &canvas, bool bHelp, bool bGpu, bool bLargestFace, bool bF Scalar fontColorNV = CV_RGB(118,185,0); ostringstream ss; + ss << "FPS = " << setprecision(1) << fixed << fps; + matPrint(canvas, 0, fontColorRed, ss); + ss.str(""); ss << "[" << canvas.cols << "x" << canvas.rows << "], " << (bGpu ? "GPU, " : "CPU, ") << (bLargestFace ? "OneFace, " : "MultiFace, ") << - (bFilter ? "Filter:ON, " : "Filter:OFF, ") << - "FPS = " << setprecision(1) << fixed << fps; - - matPrint(canvas, 0, fontColorRed, ss); + (bFilter ? "Filter:ON" : "Filter:OFF"); + matPrint(canvas, 1, fontColorRed, ss); if (bHelp) { - matPrint(canvas, 1, fontColorNV, ostringstream("Space - switch GPU / CPU")); - matPrint(canvas, 2, fontColorNV, ostringstream("M - switch OneFace / MultiFace")); - matPrint(canvas, 3, fontColorNV, ostringstream("F - toggle rectangles Filter (only in MultiFace)")); - matPrint(canvas, 4, fontColorNV, ostringstream("H - toggle hotkeys help")); - matPrint(canvas, 5, fontColorNV, ostringstream("1/Q - increase/decrease scale")); + matPrint(canvas, 2, fontColorNV, ostringstream("Space - switch GPU / CPU")); + matPrint(canvas, 3, fontColorNV, ostringstream("M - switch OneFace / MultiFace")); + matPrint(canvas, 4, fontColorNV, ostringstream("F - toggle rectangles Filter")); + matPrint(canvas, 5, fontColorNV, ostringstream("H - toggle hotkeys help")); + matPrint(canvas, 6, fontColorNV, ostringstream("1/Q - increase/decrease scale")); } else { - matPrint(canvas, 1, fontColorNV, ostringstream("H - toggle hotkeys help")); + matPrint(canvas, 2, fontColorNV, ostringstream("H - toggle hotkeys help")); } } @@ -130,8 +136,10 @@ int main(int argc, const char *argv[]) { if (!capture.open(inputName)) { - int camid = 0; - sscanf(inputName.c_str(), "%d", &camid); + int camid = -1; + istringstream iss(inputName); + iss >> camid; + if (!capture.open(camid)) { cout << "Can't open source" << endl; @@ -180,24 +188,26 @@ int main(int argc, const char *argv[]) cascade_gpu.visualizeInPlace = true; cascade_gpu.findLargestObject = findLargestObject; - detections_num = cascade_gpu.detectMultiScale(resized_gpu, facesBuf_gpu, 1.2, filterRects ? 4 : 0); + detections_num = cascade_gpu.detectMultiScale(resized_gpu, facesBuf_gpu, 1.2, + (filterRects || findLargestObject) ? 4 : 0); facesBuf_gpu.colRange(0, detections_num).download(faces_downloaded); } else { Size minSize = cascade_gpu.getClassifierSize(); - cascade_cpu.detectMultiScale(resized_cpu, facesBuf_cpu, 1.2, filterRects ? 4 : 0, (findLargestObject ? CV_HAAR_FIND_BIGGEST_OBJECT : 0) | CV_HAAR_SCALE_IMAGE, minSize); + cascade_cpu.detectMultiScale(resized_cpu, facesBuf_cpu, 1.2, + (filterRects || findLargestObject) ? 4 : 0, + (findLargestObject ? CV_HAAR_FIND_BIGGEST_OBJECT : 0) + | CV_HAAR_SCALE_IMAGE, + minSize); detections_num = (int)facesBuf_cpu.size(); } - if (!useGPU) + if (!useGPU && detections_num) { - if (detections_num) + for (int i = 0; i < detections_num; ++i) { - for (int i = 0; i < detections_num; ++i) - { - rectangle(resized_cpu, facesBuf_cpu[i], Scalar(255)); - } + rectangle(resized_cpu, facesBuf_cpu[i], Scalar(255)); } } @@ -265,3 +275,5 @@ int main(int argc, const char *argv[]) return 0; } + +#endif //!defined(HAVE_CUDA) diff --git a/samples/gpu/cascadeclassifier_nvidia_api.cpp b/samples/gpu/cascadeclassifier_nvidia_api.cpp index ebdac1c14..2dd3945b2 100644 --- a/samples/gpu/cascadeclassifier_nvidia_api.cpp +++ b/samples/gpu/cascadeclassifier_nvidia_api.cpp @@ -1,50 +1,76 @@ #pragma warning( disable : 4201 4408 4127 4100) -#include #include "cvconfig.h" -#if !defined(HAVE_CUDA) - int main( int argc, const char** argv ) { return printf("Please compile the library with CUDA support."), -1; } -#else - -#include -#include "opencv2/opencv.hpp" +#include +#include +#include +#include #include "NCVHaarObjectDetection.hpp" +using namespace std; using namespace cv; -const Size2i preferredVideoFrameSize(640, 480); -std::string preferredClassifier = "haarcascade_frontalface_alt.xml"; -std::string wndTitle = "NVIDIA Computer Vision SDK :: Face Detection in Video Feed"; - - -void printSyntax(void) +#if !defined(HAVE_CUDA) +int main( int argc, const char** argv ) { - printf("Syntax: FaceDetectionFeed.exe [-c cameranum | -v filename] classifier.xml\n"); + cout << "Please compile the library with CUDA support" << endl; + return -1; +} +#else + + +const Size2i preferredVideoFrameSize(640, 480); +const string wndTitle = "NVIDIA Computer Vision :: Haar Classifiers Cascade"; + + +void matPrint(Mat &img, int lineOffsY, Scalar fontColor, const ostringstream &ss) +{ + int fontFace = FONT_HERSHEY_DUPLEX; + double fontScale = 0.8; + int fontThickness = 2; + Size fontSize = cv::getTextSize("T[]", fontFace, fontScale, fontThickness, 0); + + Point org; + org.x = 1; + org.y = 3 * fontSize.height * (lineOffsY + 1) / 2; + putText(img, ss.str(), org, fontFace, fontScale, CV_RGB(0,0,0), 5*fontThickness/2, 16); + putText(img, ss.str(), org, fontFace, fontScale, fontColor, fontThickness, 16); } -void imagePrintf(Mat& img, int lineOffsY, Scalar color, const char *format, ...) -{ - int fontFace = CV_FONT_HERSHEY_PLAIN; - double fontScale = 1; - - int baseline; - Size textSize = cv::getTextSize("T", fontFace, fontScale, 1, &baseline); - va_list arg_ptr; - va_start(arg_ptr, format); +void displayState(Mat &canvas, bool bHelp, bool bGpu, bool bLargestFace, bool bFilter, double fps) +{ + Scalar fontColorRed = CV_RGB(255,0,0); + Scalar fontColorNV = CV_RGB(118,185,0); - char strBuf[4096]; - vsprintf(&strBuf[0], format, arg_ptr); + ostringstream ss; + ss << "FPS = " << setprecision(1) << fixed << fps; + matPrint(canvas, 0, fontColorRed, ss); + ss.str(""); + ss << "[" << canvas.cols << "x" << canvas.rows << "], " << + (bGpu ? "GPU, " : "CPU, ") << + (bLargestFace ? "OneFace, " : "MultiFace, ") << + (bFilter ? "Filter:ON" : "Filter:OFF"); + matPrint(canvas, 1, fontColorRed, ss); - Point org(1, 3 * textSize.height * (lineOffsY + 1) / 2); - putText(img, &strBuf[0], org, fontFace, fontScale, color); - va_end(arg_ptr); + if (bHelp) + { + matPrint(canvas, 2, fontColorNV, ostringstream("Space - switch GPU / CPU")); + matPrint(canvas, 3, fontColorNV, ostringstream("M - switch OneFace / MultiFace")); + matPrint(canvas, 4, fontColorNV, ostringstream("F - toggle rectangles Filter")); + matPrint(canvas, 5, fontColorNV, ostringstream("H - toggle hotkeys help")); + } + else + { + matPrint(canvas, 2, fontColorNV, ostringstream("H - toggle hotkeys help")); + } } + NCVStatus process(Mat *srcdst, Ncv32u width, Ncv32u height, - NcvBool bShowAllHypotheses, NcvBool bLargestFace, + NcvBool bFilterRects, NcvBool bLargestFace, HaarClassifierCascadeDescriptor &haar, NCVVector &d_haarStages, NCVVector &d_haarNodes, NCVVector &d_haarFeatures, NCVVector &h_haarStages, @@ -87,7 +113,7 @@ NCVStatus process(Mat *srcdst, d_src, roi, d_rects, numDetections, haar, h_haarStages, d_haarStages, d_haarNodes, d_haarFeatures, haar.ClassifierSize, - bShowAllHypotheses ? 0 : 4, + (bFilterRects || bLargestFace) ? 4 : 0, 1.2f, 1, (bLargestFace ? NCVPipeObjDet_FindLargestObject : 0) | NCVPipeObjDet_VisualizeInPlace, @@ -111,80 +137,67 @@ NCVStatus process(Mat *srcdst, return NCV_SUCCESS; } -int main( int argc, const char** argv ) + +int main(int argc, const char** argv) { + cout << "OpenCV / NVIDIA Computer Vision" << endl; + cout << "Face Detection in video and live feed" << endl; + cout << "Syntax: exename " << endl; + cout << "=========================================" << endl; + + ncvAssertPrintReturn(cv::gpu::getCudaEnabledDeviceCount() != 0, "No GPU found or the library is compiled without GPU support", -1); + ncvAssertPrintReturn(argc == 3, "Invalid number of arguments", -1); + + string cascadeName = argv[1]; + string inputName = argv[2]; + NCVStatus ncvStat; - - printf("NVIDIA Computer Vision SDK\n"); - printf("Face Detection in video and live feed\n"); - printf("=========================================\n"); - printf(" Esc - Quit\n"); - printf(" Space - Switch between NCV and OpenCV\n"); - printf(" L - Switch between FullSearch and LargestFace modes\n"); - printf(" U - Toggle unfiltered hypotheses visualization in FullSearch\n"); - - VideoCapture capture; - bool bQuit = false; - + NcvBool bQuit = false; + VideoCapture capture; Size2i frameSize; - if (argc != 4 && argc != 1) + //open content source + Mat image = imread(inputName); + Mat frame; + if (!image.empty()) { - printSyntax(); - return -1; - } - - if (argc == 1 || strcmp(argv[1], "-c") == 0) - { - // Camera input is specified - int camIdx = (argc == 3) ? atoi(argv[2]) : 0; - if(!capture.open(camIdx)) - return printf("Error opening camera\n"), -1; - - capture.set(CV_CAP_PROP_FRAME_WIDTH, preferredVideoFrameSize.width); - capture.set(CV_CAP_PROP_FRAME_HEIGHT, preferredVideoFrameSize.height); - capture.set(CV_CAP_PROP_FPS, 25); - frameSize = preferredVideoFrameSize; - } - else if (strcmp(argv[1], "-v") == 0) - { - // Video file input (avi) - if(!capture.open(argv[2])) - return printf("Error opening video file\n"), -1; - - frameSize.width = (int)capture.get(CV_CAP_PROP_FRAME_WIDTH); - frameSize.height = (int)capture.get(CV_CAP_PROP_FRAME_HEIGHT); + frameSize.width = image.cols; + frameSize.height = image.rows; } else - return printSyntax(), -1; + { + if (!capture.open(inputName)) + { + int camid = -1; - NcvBool bUseOpenCV = true; - NcvBool bLargestFace = false; //LargestFace=true is used usually during training - NcvBool bShowAllHypotheses = false; + istringstream ss(inputName); + int x = 0; + ss >> x; + + ncvAssertPrintReturn(capture.open(camid) != 0, "Can't open source", -1); + } + + capture >> frame; + ncvAssertPrintReturn(!frame.empty(), "Empty video source", -1); + + frameSize.width = frame.cols; + frameSize.height = frame.rows; + } + + NcvBool bUseGPU = true; + NcvBool bLargestObject = false; + NcvBool bFilterRects = true; + NcvBool bHelpScreen = false; CascadeClassifier classifierOpenCV; - std::string classifierFile; - if (argc == 1) - { - classifierFile = preferredClassifier; - } - else - { - classifierFile.assign(argv[3]); - } - - if (!classifierOpenCV.load(classifierFile)) - { - printf("Error (in OpenCV) opening classifier\n"); - printSyntax(); - return -1; - } + ncvAssertPrintReturn(classifierOpenCV.load(cascadeName) != 0, "Error (in OpenCV) opening classifier", -1); int devId; ncvAssertCUDAReturn(cudaGetDevice(&devId), -1); cudaDeviceProp devProp; ncvAssertCUDAReturn(cudaGetDeviceProperties(&devProp, devId), -1); - printf("Using GPU %d %s, arch=%d.%d\n", devId, devProp.name, devProp.major, devProp.minor); + cout << "Using GPU: " << devId << "(" << devProp.name << + "), arch=" << devProp.major << "." << devProp.minor << endl; //============================================================================== // @@ -199,7 +212,7 @@ int main( int argc, const char** argv ) ncvAssertPrintReturn(cpuCascadeAllocator.isInitialized(), "Error creating cascade CPU allocator", -1); Ncv32u haarNumStages, haarNumNodes, haarNumFeatures; - ncvStat = ncvHaarGetClassifierSize(classifierFile, haarNumStages, haarNumNodes, haarNumFeatures); + ncvStat = ncvHaarGetClassifierSize(cascadeName, haarNumStages, haarNumNodes, haarNumFeatures); ncvAssertPrintReturn(ncvStat == NCV_SUCCESS, "Error reading classifier size (check the file)", -1); NCVVectorAlloc h_haarStages(cpuCascadeAllocator, haarNumStages); @@ -210,7 +223,7 @@ int main( int argc, const char** argv ) ncvAssertPrintReturn(h_haarFeatures.isMemAllocated(), "Error in cascade CPU allocator", -1); HaarClassifierCascadeDescriptor haar; - ncvStat = ncvHaarLoadFromFile_host(classifierFile, haar, h_haarStages, h_haarNodes, h_haarFeatures); + ncvStat = ncvHaarLoadFromFile_host(cascadeName, haar, h_haarStages, h_haarNodes, h_haarFeatures); ncvAssertPrintReturn(ncvStat == NCV_SUCCESS, "Error loading classifier", -1); NCVVectorAlloc d_haarStages(gpuCascadeAllocator, haarNumStages); @@ -258,30 +271,25 @@ int main( int argc, const char** argv ) // //============================================================================== - namedWindow(wndTitle, 1); - Mat frame, gray, frameDisp; + namedWindow(wndTitle, 1); + Mat gray, frameDisp; do { - // For camera and video file, capture the next image - capture >> frame; - if (frame.empty()) - break; - Mat gray; - cvtColor(frame, gray, CV_BGR2GRAY); + cvtColor((image.empty() ? frame : image), gray, CV_BGR2GRAY); // // process // NcvSize32u minSize = haar.ClassifierSize; - if (bLargestFace) + if (bLargestObject) { Ncv32u ratioX = preferredVideoFrameSize.width / minSize.width; Ncv32u ratioY = preferredVideoFrameSize.height / minSize.height; - Ncv32u ratioSmallest = std::min(ratioX, ratioY); - ratioSmallest = std::max((Ncv32u)(ratioSmallest / 2.5f), (Ncv32u)1); + Ncv32u ratioSmallest = min(ratioX, ratioY); + ratioSmallest = max((Ncv32u)(ratioSmallest / 2.5f), (Ncv32u)1); minSize.width *= ratioSmallest; minSize.height *= ratioSmallest; } @@ -289,10 +297,10 @@ int main( int argc, const char** argv ) Ncv32f avgTime; NcvTimer timer = ncvStartTimer(); - if (!bUseOpenCV) + if (bUseGPU) { ncvStat = process(&gray, frameSize.width, frameSize.height, - bShowAllHypotheses, bLargestFace, haar, + bFilterRects, bLargestObject, haar, d_haarStages, d_haarNodes, d_haarFeatures, h_haarStages, gpuAllocator, cpuAllocator, devProp); @@ -306,8 +314,8 @@ int main( int argc, const char** argv ) gray, rectsOpenCV, 1.2f, - bShowAllHypotheses && !bLargestFace ? 0 : 4, - (bLargestFace ? CV_HAAR_FIND_BIGGEST_OBJECT : 0) + bFilterRects ? 4 : 0, + (bLargestObject ? CV_HAAR_FIND_BIGGEST_OBJECT : 0) | CV_HAAR_SCALE_IMAGE, Size(minSize.width, minSize.height)); @@ -318,32 +326,41 @@ int main( int argc, const char** argv ) avgTime = (Ncv32f)ncvEndQueryTimerMs(timer); cvtColor(gray, frameDisp, CV_GRAY2BGR); + displayState(frameDisp, bHelpScreen, bUseGPU, bLargestObject, bFilterRects, 1000.0f / avgTime); + imshow(wndTitle, frameDisp); - imagePrintf(frameDisp, 0, CV_RGB(255, 0,0), "Space - Switch NCV%s / OpenCV%s", bUseOpenCV?"":" (ON)", bUseOpenCV?" (ON)":""); - imagePrintf(frameDisp, 1, CV_RGB(255, 0,0), "L - Switch FullSearch%s / LargestFace%s modes", bLargestFace?"":" (ON)", bLargestFace?" (ON)":""); - imagePrintf(frameDisp, 2, CV_RGB(255, 0,0), "U - Toggle unfiltered hypotheses visualization in FullSearch %s", bShowAllHypotheses?"(ON)":"(OFF)"); - imagePrintf(frameDisp, 3, CV_RGB(118,185,0), " Running at %f FPS on %s", 1000.0f / avgTime, bUseOpenCV?"CPU":"GPU"); - - cv::imshow(wndTitle, frameDisp); - + //handle input switch (cvWaitKey(3)) { case ' ': - bUseOpenCV = !bUseOpenCV; + bUseGPU = !bUseGPU; break; - case 'L': - case 'l': - bLargestFace = !bLargestFace; + case 'm': + case 'M': + bLargestObject = !bLargestObject; break; - case 'U': - case 'u': - bShowAllHypotheses = !bShowAllHypotheses; + case 'f': + case 'F': + bFilterRects = !bFilterRects; + break; + case 'h': + case 'H': + bHelpScreen = !bHelpScreen; break; case 27: bQuit = true; break; } + // For camera and video file, capture the next image + if (capture.isOpened()) + { + capture >> frame; + if (frame.empty()) + { + break; + } + } } while (!bQuit); cvDestroyWindow(wndTitle.c_str()); @@ -351,5 +368,4 @@ int main( int argc, const char** argv ) return 0; } - -#endif +#endif //!defined(HAVE_CUDA)