Commit 734212a4 authored by Vladislav Vinogradov's avatar Vladislav Vinogradov

refactor CUDA CascadeClassifier

parent 8257dc3c
...@@ -75,7 +75,7 @@ namespace cv { namespace cuda { ...@@ -75,7 +75,7 @@ namespace cv { namespace cuda {
- (Python) An example applying the HOG descriptor for people detection can be found at - (Python) An example applying the HOG descriptor for people detection can be found at
opencv_source_code/samples/python2/peopledetect.py opencv_source_code/samples/python2/peopledetect.py
*/ */
class CV_EXPORTS HOG : public cv::Algorithm class CV_EXPORTS HOG : public Algorithm
{ {
public: public:
enum enum
...@@ -204,87 +204,84 @@ public: ...@@ -204,87 +204,84 @@ public:
- A Nvidea API specific cascade classifier example can be found at - A Nvidea API specific cascade classifier example can be found at
opencv_source_code/samples/gpu/cascadeclassifier_nvidia_api.cpp opencv_source_code/samples/gpu/cascadeclassifier_nvidia_api.cpp
*/ */
class CV_EXPORTS CascadeClassifier_CUDA class CV_EXPORTS CascadeClassifier : public Algorithm
{ {
public: public:
CascadeClassifier_CUDA();
/** @brief Loads the classifier from a file. Cascade type is detected automatically by constructor parameter. /** @brief Loads the classifier from a file. Cascade type is detected automatically by constructor parameter.
@param filename Name of the file from which the classifier is loaded. Only the old haar classifier @param filename Name of the file from which the classifier is loaded. Only the old haar classifier
(trained by the haar training application) and NVIDIA's nvbin are supported for HAAR and only new (trained by the haar training application) and NVIDIA's nvbin are supported for HAAR and only new
type of OpenCV XML cascade supported for LBP. type of OpenCV XML cascade supported for LBP.
*/ */
CascadeClassifier_CUDA(const String& filename); static Ptr<CascadeClassifier> create(const String& filename);
~CascadeClassifier_CUDA(); /** @overload
*/
static Ptr<CascadeClassifier> create(const FileStorage& file);
/** @brief Checks whether the classifier is loaded or not. //! Maximum possible object size. Objects larger than that are ignored. Used for
*/ //! second signature and supported only for LBP cascades.
bool empty() const; virtual void setMaxObjectSize(Size maxObjectSize) = 0;
/** @brief Loads the classifier from a file. The previous content is destroyed. virtual Size getMaxObjectSize() const = 0;
@param filename Name of the file from which the classifier is loaded. Only the old haar classifier //! Minimum possible object size. Objects smaller than that are ignored.
(trained by the haar training application) and NVIDIA's nvbin are supported for HAAR and only new virtual void setMinObjectSize(Size minSize) = 0;
type of OpenCV XML cascade supported for LBP. virtual Size getMinObjectSize() const = 0;
*/
bool load(const String& filename); //! Parameter specifying how much the image size is reduced at each image scale.
/** @brief Destroys the loaded classifier. virtual void setScaleFactor(double scaleFactor) = 0;
*/ virtual double getScaleFactor() const = 0;
void release();
//! Parameter specifying how many neighbors each candidate rectangle should have
//! to retain it.
virtual void setMinNeighbors(int minNeighbors) = 0;
virtual int getMinNeighbors() const = 0;
virtual void setFindLargestObject(bool findLargestObject) = 0;
virtual bool getFindLargestObject() = 0;
virtual void setMaxNumObjects(int maxNumObjects) = 0;
virtual int getMaxNumObjects() const = 0;
virtual Size getClassifierSize() const = 0;
/** @overload */
int detectMultiScale(const GpuMat& image, GpuMat& objectsBuf, double scaleFactor = 1.2, int minNeighbors = 4, Size minSize = Size());
/** @brief Detects objects of different sizes in the input image. /** @brief Detects objects of different sizes in the input image.
@param image Matrix of type CV_8U containing an image where objects should be detected. @param image Matrix of type CV_8U containing an image where objects should be detected.
@param objectsBuf Buffer to store detected objects (rectangles). If it is empty, it is allocated @param objects Buffer to store detected objects (rectangles).
with the default size. If not empty, the function searches not more than N objects, where
N = sizeof(objectsBufer's data)/sizeof(cv::Rect). To get final array of detected objects use CascadeClassifier::convert method.
@param maxObjectSize Maximum possible object size. Objects larger than that are ignored. Used for
second signature and supported only for LBP cascades.
@param scaleFactor Parameter specifying how much the image size is reduced at each image scale.
@param minNeighbors Parameter specifying how many neighbors each candidate rectangle should have
to retain it.
@param minSize Minimum possible object size. Objects smaller than that are ignored.
The detected objects are returned as a list of rectangles.
The function returns the number of detected objects, so you can retrieve them as in the following
example:
@code @code
cuda::CascadeClassifier_CUDA cascade_gpu(...); Ptr<cuda::CascadeClassifier> cascade_gpu = cuda::CascadeClassifier::create(...);
Mat image_cpu = imread(...) Mat image_cpu = imread(...)
GpuMat image_gpu(image_cpu); GpuMat image_gpu(image_cpu);
GpuMat objbuf; GpuMat objbuf;
int detections_number = cascade_gpu.detectMultiScale( image_gpu, cascade_gpu->detectMultiScale(image_gpu, objbuf);
objbuf, 1.2, minNeighbors);
Mat obj_host; std::vector<Rect> faces;
// download only detected number of rectangles cascade_gpu->convert(objbuf, faces);
objbuf.colRange(0, detections_number).download(obj_host);
Rect* faces = obj_host.ptr<Rect>();
for(int i = 0; i < detections_num; ++i) for(int i = 0; i < detections_num; ++i)
cv::rectangle(image_cpu, faces[i], Scalar(255)); cv::rectangle(image_cpu, faces[i], Scalar(255));
imshow("Faces", image_cpu); imshow("Faces", image_cpu);
@endcode @endcode
@sa CascadeClassifier::detectMultiScale @sa CascadeClassifier::detectMultiScale
*/ */
int detectMultiScale(const GpuMat& image, GpuMat& objectsBuf, Size maxObjectSize, Size minSize = Size(), double scaleFactor = 1.1, int minNeighbors = 4); virtual void detectMultiScale(InputArray image,
OutputArray objects,
Stream& stream = Stream::Null()) = 0;
bool findLargestObject; /** @brief Converts objects array from internal representation to standard vector.
bool visualizeInPlace;
Size getClassifierSize() const; @param gpu_objects Objects array in internal representation.
@param objects Resulting array.
private: */
struct CascadeClassifierImpl; virtual void convert(OutputArray gpu_objects,
CascadeClassifierImpl* impl; std::vector<Rect>& objects) = 0;
struct HaarCascade;
struct LbpCascade;
friend class CascadeClassifier_CUDA_LBP;
}; };
//! @} //! @}
......
...@@ -107,18 +107,17 @@ PERF_TEST_P(ImageAndCascade, ObjDetect_HaarClassifier, ...@@ -107,18 +107,17 @@ PERF_TEST_P(ImageAndCascade, ObjDetect_HaarClassifier,
if (PERF_RUN_CUDA()) if (PERF_RUN_CUDA())
{ {
cv::cuda::CascadeClassifier_CUDA d_cascade; cv::Ptr<cv::cuda::CascadeClassifier> d_cascade =
ASSERT_TRUE(d_cascade.load(perf::TestBase::getDataPath(GetParam().second))); cv::cuda::CascadeClassifier::create(perf::TestBase::getDataPath(GetParam().second));
const cv::cuda::GpuMat d_img(img); const cv::cuda::GpuMat d_img(img);
cv::cuda::GpuMat objects_buffer; cv::cuda::GpuMat objects_buffer;
int detections_num = 0;
TEST_CYCLE() detections_num = d_cascade.detectMultiScale(d_img, objects_buffer); TEST_CYCLE() d_cascade->detectMultiScale(d_img, objects_buffer);
std::vector<cv::Rect> gpu_rects;
d_cascade->convert(objects_buffer, gpu_rects);
std::vector<cv::Rect> gpu_rects(detections_num);
cv::Mat gpu_rects_mat(1, detections_num, cv::DataType<cv::Rect>::type, &gpu_rects[0]);
objects_buffer.colRange(0, detections_num).download(gpu_rects_mat);
cv::groupRectangles(gpu_rects, 3, 0.2); cv::groupRectangles(gpu_rects, 3, 0.2);
SANITY_CHECK(gpu_rects); SANITY_CHECK(gpu_rects);
} }
...@@ -146,18 +145,17 @@ PERF_TEST_P(ImageAndCascade, ObjDetect_LBPClassifier, ...@@ -146,18 +145,17 @@ PERF_TEST_P(ImageAndCascade, ObjDetect_LBPClassifier,
if (PERF_RUN_CUDA()) if (PERF_RUN_CUDA())
{ {
cv::cuda::CascadeClassifier_CUDA d_cascade; cv::Ptr<cv::cuda::CascadeClassifier> d_cascade =
ASSERT_TRUE(d_cascade.load(perf::TestBase::getDataPath(GetParam().second))); cv::cuda::CascadeClassifier::create(perf::TestBase::getDataPath(GetParam().second));
const cv::cuda::GpuMat d_img(img); const cv::cuda::GpuMat d_img(img);
cv::cuda::GpuMat objects_buffer; cv::cuda::GpuMat objects_buffer;
int detections_num = 0;
TEST_CYCLE() detections_num = d_cascade.detectMultiScale(d_img, objects_buffer); TEST_CYCLE() d_cascade->detectMultiScale(d_img, objects_buffer);
std::vector<cv::Rect> gpu_rects;
d_cascade->convert(objects_buffer, gpu_rects);
std::vector<cv::Rect> gpu_rects(detections_num);
cv::Mat gpu_rects_mat(1, detections_num, cv::DataType<cv::Rect>::type, &gpu_rects[0]);
objects_buffer.colRange(0, detections_num).download(gpu_rects_mat);
cv::groupRectangles(gpu_rects, 3, 0.2); cv::groupRectangles(gpu_rects, 3, 0.2);
SANITY_CHECK(gpu_rects); SANITY_CHECK(gpu_rects);
} }
......
...@@ -287,9 +287,15 @@ PARAM_TEST_CASE(LBP_Read_classifier, cv::cuda::DeviceInfo, int) ...@@ -287,9 +287,15 @@ PARAM_TEST_CASE(LBP_Read_classifier, cv::cuda::DeviceInfo, int)
CUDA_TEST_P(LBP_Read_classifier, Accuracy) CUDA_TEST_P(LBP_Read_classifier, Accuracy)
{ {
cv::cuda::CascadeClassifier_CUDA classifier;
std::string classifierXmlPath = std::string(cvtest::TS::ptr()->get_data_path()) + "lbpcascade/lbpcascade_frontalface.xml"; std::string classifierXmlPath = std::string(cvtest::TS::ptr()->get_data_path()) + "lbpcascade/lbpcascade_frontalface.xml";
ASSERT_TRUE(classifier.load(classifierXmlPath));
cv::Ptr<cv::cuda::CascadeClassifier> d_cascade;
ASSERT_NO_THROW(
d_cascade = cv::cuda::CascadeClassifier::create(classifierXmlPath);
);
ASSERT_FALSE(d_cascade.empty());
} }
INSTANTIATE_TEST_CASE_P(CUDA_ObjDetect, LBP_Read_classifier, INSTANTIATE_TEST_CASE_P(CUDA_ObjDetect, LBP_Read_classifier,
...@@ -329,29 +335,28 @@ CUDA_TEST_P(LBP_classify, Accuracy) ...@@ -329,29 +335,28 @@ CUDA_TEST_P(LBP_classify, Accuracy)
for (; it != rects.end(); ++it) for (; it != rects.end(); ++it)
cv::rectangle(markedImage, *it, cv::Scalar(255, 0, 0)); cv::rectangle(markedImage, *it, cv::Scalar(255, 0, 0));
cv::cuda::CascadeClassifier_CUDA gpuClassifier; cv::Ptr<cv::cuda::CascadeClassifier> gpuClassifier =
ASSERT_TRUE(gpuClassifier.load(classifierXmlPath)); cv::cuda::CascadeClassifier::create(classifierXmlPath);
cv::cuda::GpuMat gpu_rects;
cv::cuda::GpuMat tested(grey); cv::cuda::GpuMat tested(grey);
int count = gpuClassifier.detectMultiScale(tested, gpu_rects); cv::cuda::GpuMat gpu_rects_buf;
gpuClassifier->detectMultiScale(tested, gpu_rects_buf);
std::vector<cv::Rect> gpu_rects;
gpuClassifier->convert(gpu_rects_buf, gpu_rects);
#if defined (LOG_CASCADE_STATISTIC) #if defined (LOG_CASCADE_STATISTIC)
cv::Mat downloaded(gpu_rects); for (size_t i = 0; i < gpu_rects.size(); i++)
const cv::Rect* faces = downloaded.ptr<cv::Rect>();
for (int i = 0; i < count; i++)
{ {
cv::Rect r = faces[i]; cv::Rect r = gpu_rects[i];
std::cout << r.x << " " << r.y << " " << r.width << " " << r.height << std::endl; std::cout << r.x << " " << r.y << " " << r.width << " " << r.height << std::endl;
cv::rectangle(markedImage, r , CV_RGB(255, 0, 0)); cv::rectangle(markedImage, r , CV_RGB(255, 0, 0));
} }
#endif
#if defined (LOG_CASCADE_STATISTIC) cv::imshow("Res", markedImage);
cv::imshow("Res", markedImage); cv::waitKey(); cv::waitKey();
#endif #endif
(void)count;
} }
INSTANTIATE_TEST_CASE_P(CUDA_ObjDetect, LBP_classify, INSTANTIATE_TEST_CASE_P(CUDA_ObjDetect, LBP_classify,
......
...@@ -173,13 +173,9 @@ int main(int argc, const char *argv[]) ...@@ -173,13 +173,9 @@ int main(int argc, const char *argv[])
} }
} }
CascadeClassifier_CUDA cascade_gpu; Ptr<cuda::CascadeClassifier> cascade_gpu = cuda::CascadeClassifier::create(cascadeName);
if (!cascade_gpu.load(cascadeName))
{
return cerr << "ERROR: Could not load cascade classifier \"" << cascadeName << "\"" << endl, help(), -1;
}
CascadeClassifier cascade_cpu; cv::CascadeClassifier cascade_cpu;
if (!cascade_cpu.load(cascadeName)) if (!cascade_cpu.load(cascadeName))
{ {
return cerr << "ERROR: Could not load cascade classifier \"" << cascadeName << "\"" << endl, help(), -1; return cerr << "ERROR: Could not load cascade classifier \"" << cascadeName << "\"" << endl, help(), -1;
...@@ -206,8 +202,8 @@ int main(int argc, const char *argv[]) ...@@ -206,8 +202,8 @@ int main(int argc, const char *argv[])
namedWindow("result", 1); namedWindow("result", 1);
Mat frame, frame_cpu, gray_cpu, resized_cpu, faces_downloaded, frameDisp; Mat frame, frame_cpu, gray_cpu, resized_cpu, frameDisp;
vector<Rect> facesBuf_cpu; vector<Rect> faces;
GpuMat frame_gpu, gray_gpu, resized_gpu, facesBuf_gpu; GpuMat frame_gpu, gray_gpu, resized_gpu, facesBuf_gpu;
...@@ -218,7 +214,6 @@ int main(int argc, const char *argv[]) ...@@ -218,7 +214,6 @@ int main(int argc, const char *argv[])
bool filterRects = true; bool filterRects = true;
bool helpScreen = false; bool helpScreen = false;
int detections_num;
for (;;) for (;;)
{ {
if (isInputCamera || isInputVideo) if (isInputCamera || isInputVideo)
...@@ -241,40 +236,26 @@ int main(int argc, const char *argv[]) ...@@ -241,40 +236,26 @@ int main(int argc, const char *argv[])
if (useGPU) if (useGPU)
{ {
//cascade_gpu.visualizeInPlace = true; cascade_gpu->setFindLargestObject(findLargestObject);
cascade_gpu.findLargestObject = findLargestObject; cascade_gpu->setScaleFactor(1.2);
cascade_gpu->setMinNeighbors((filterRects || findLargestObject) ? 4 : 0);
detections_num = cascade_gpu.detectMultiScale(resized_gpu, facesBuf_gpu, 1.2, cascade_gpu->detectMultiScale(resized_gpu, facesBuf_gpu);
(filterRects || findLargestObject) ? 4 : 0); cascade_gpu->convert(facesBuf_gpu, faces);
facesBuf_gpu.colRange(0, detections_num).download(faces_downloaded);
} }
else else
{ {
Size minSize = cascade_gpu.getClassifierSize(); Size minSize = cascade_gpu->getClassifierSize();
cascade_cpu.detectMultiScale(resized_cpu, facesBuf_cpu, 1.2, cascade_cpu.detectMultiScale(resized_cpu, faces, 1.2,
(filterRects || findLargestObject) ? 4 : 0, (filterRects || findLargestObject) ? 4 : 0,
(findLargestObject ? CASCADE_FIND_BIGGEST_OBJECT : 0) (findLargestObject ? CASCADE_FIND_BIGGEST_OBJECT : 0)
| CASCADE_SCALE_IMAGE, | CASCADE_SCALE_IMAGE,
minSize); minSize);
detections_num = (int)facesBuf_cpu.size();
}
if (!useGPU && detections_num)
{
for (int i = 0; i < detections_num; ++i)
{
rectangle(resized_cpu, facesBuf_cpu[i], Scalar(255));
}
} }
if (useGPU) for (size_t i = 0; i < faces.size(); ++i)
{ {
resized_gpu.download(resized_cpu); rectangle(resized_cpu, faces[i], Scalar(255));
for (int i = 0; i < detections_num; ++i)
{
rectangle(resized_cpu, faces_downloaded.ptr<cv::Rect>()[i], Scalar(255));
}
} }
tm.stop(); tm.stop();
...@@ -283,16 +264,15 @@ int main(int argc, const char *argv[]) ...@@ -283,16 +264,15 @@ int main(int argc, const char *argv[])
//print detections to console //print detections to console
cout << setfill(' ') << setprecision(2); cout << setfill(' ') << setprecision(2);
cout << setw(6) << fixed << fps << " FPS, " << detections_num << " det"; cout << setw(6) << fixed << fps << " FPS, " << faces.size() << " det";
if ((filterRects || findLargestObject) && detections_num > 0) if ((filterRects || findLargestObject) && !faces.empty())
{ {
Rect *faceRects = useGPU ? faces_downloaded.ptr<Rect>() : &facesBuf_cpu[0]; for (size_t i = 0; i < faces.size(); ++i)
for (int i = 0; i < min(detections_num, 2); ++i)
{ {
cout << ", [" << setw(4) << faceRects[i].x cout << ", [" << setw(4) << faces[i].x
<< ", " << setw(4) << faceRects[i].y << ", " << setw(4) << faces[i].y
<< ", " << setw(4) << faceRects[i].width << ", " << setw(4) << faces[i].width
<< ", " << setw(4) << faceRects[i].height << "]"; << ", " << setw(4) << faces[i].height << "]";
} }
} }
cout << endl; cout << endl;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment