From 7b87d72d80f03181e5de68def3d7f54c27d9e6ca Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Tue, 25 Jun 2013 17:39:05 +0400 Subject: [PATCH] refactored generalized hough (both CPU and GPU): removed set/get methods from Algorithm (implement owns) removed GHT_* enumeration --- modules/gpuimgproc/doc/hough.rst | 93 +- .../gpuimgproc/include/opencv2/gpuimgproc.hpp | 21 +- modules/gpuimgproc/perf/perf_hough.cpp | 91 +- .../gpuimgproc/src/cuda/generalized_hough.cu | 262 ---- modules/gpuimgproc/src/generalized_hough.cpp | 1113 ++++++---------- modules/gpuimgproc/test/test_hough.cpp | 12 +- modules/imgproc/include/opencv2/imgproc.hpp | 119 +- modules/imgproc/src/generalized_hough.cpp | 1121 ++++++----------- samples/gpu/generalized_hough.cpp | 131 +- 9 files changed, 1028 insertions(+), 1935 deletions(-) diff --git a/modules/gpuimgproc/doc/hough.rst b/modules/gpuimgproc/doc/hough.rst index eb0f83c97e..fa653657c0 100644 --- a/modules/gpuimgproc/doc/hough.rst +++ b/modules/gpuimgproc/doc/hough.rst @@ -213,98 +213,19 @@ Creates implementation for :ocv:class:`gpu::HoughCirclesDetector` . -gpu::GeneralizedHough ---------------------- -.. ocv:class:: gpu::GeneralizedHough : public Algorithm - -Base class for generalized hough transform. :: - - class CV_EXPORTS GeneralizedHough : public Algorithm - { - public: - static Ptr create(int method); - - virtual void setTemplate(InputArray templ, int cannyThreshold = 100, Point templCenter = Point(-1, -1)) = 0; - virtual void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter = Point(-1, -1)) = 0; - - virtual void detect(InputArray image, OutputArray positions, int cannyThreshold = 100) = 0; - virtual void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions) = 0; - - virtual void downloadResults(InputArray d_positions, OutputArray h_positions, OutputArray h_votes = noArray()) = 0; - }; - - -Finds arbitrary template in the grayscale image using Generalized Hough Transform. - - - -gpu::GeneralizedHough::create ------------------------------ -Creates implementation for :ocv:class:`gpu::GeneralizedHough` . - -.. ocv:function:: Ptr gpu::GeneralizedHough::create(int method) - - :param method: Combination of flags ( ``cv::GeneralizedHough::GHT_POSITION`` , ``cv::GeneralizedHough::GHT_SCALE`` , ``cv::GeneralizedHough::GHT_ROTATION`` ) specifying transformation to find. - -For full affine transformations (move + scale + rotation) [Guil1999]_ algorithm is used, otherwise [Ballard1981]_ algorithm is used. - - - -gpu::GeneralizedHough::setTemplate +gpu::createGeneralizedHoughBallard ---------------------------------- -Set template to search. - -.. ocv:function:: void gpu::GeneralizedHough::setTemplate(InputArray templ, int cannyThreshold = 100, Point templCenter = Point(-1, -1)) - -.. ocv:function:: void gpu::GeneralizedHough::setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter = Point(-1, -1)) +Creates implementation for generalized hough transform from [Ballard1981]_ . - :param templ: Template image. Canny edge detector will be applied to extract template edges. +.. ocv:function:: Ptr gpu::createGeneralizedHoughBallard() - :param cannyThreshold: Threshold value for Canny edge detector. - :param templCenter: Center for rotation. By default image center will be used. - :param edges: Edge map for template image. - - :param dx: First derivative of template image in the vertical direction. Support only ``CV_32S`` type. - - :param dy: First derivative of template image in the horizontal direction. Support only ``CV_32S`` type. - - - -gpu::GeneralizedHough::detect ------------------------------ -Finds template (set by :ocv:func:`gpu::GeneralizedHough::setTemplate` ) in the grayscale image. - -.. ocv:function:: void gpu::GeneralizedHough::detect(InputArray image, OutputArray positions, int cannyThreshold = 100) - -.. ocv:function:: void gpu::GeneralizedHough::detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions) - - :param templ: Input image. Canny edge detector will be applied to extract template edges. - - :param positions: Output vector of found objects. Each vector is encoded as a 4-element floating-point vector :math:`(x, y, scale, angle)` . - - :param cannyThreshold: Threshold value for Canny edge detector. - - :param edges: Edge map for input image. - - :param dx: First derivative of input image in the vertical direction. Support only ``CV_32S`` type. - - :param dy: First derivative of input image in the horizontal direction. Support only ``CV_32S`` type. - - - -gpu::GeneralizedHough::downloadResults --------------------------------------- -Downloads results from :ocv:func:`gpu::GeneralizedHough::detect` to host memory. - -.. ocv:function:: void gpu::GeneralizedHough::downloadResult(InputArray d_positions, OutputArray h_positions, OutputArray h_votes = noArray()) - - :param d_lines: Result of :ocv:func:`gpu::GeneralizedHough::detect` . - - :param h_lines: Output host array. +gpu::createGeneralizedHoughGuil +------------------------------- +Creates implementation for generalized hough transform from [Guil1999]_ . - :param h_votes: Optional output array for votes. Each vector is encoded as a 3-element integer-point vector :math:`(position_votes, scale_votes, angle_votes)` . +.. ocv:function:: Ptr gpu::createGeneralizedHoughGuil() diff --git a/modules/gpuimgproc/include/opencv2/gpuimgproc.hpp b/modules/gpuimgproc/include/opencv2/gpuimgproc.hpp index 330476560d..f0a0f1260a 100644 --- a/modules/gpuimgproc/include/opencv2/gpuimgproc.hpp +++ b/modules/gpuimgproc/include/opencv2/gpuimgproc.hpp @@ -283,24 +283,13 @@ CV_EXPORTS Ptr createHoughCirclesDetector(float dp, float ////////////////////////////////////// // GeneralizedHough -//! finds arbitrary template in the grayscale image using Generalized Hough Transform //! Ballard, D.H. (1981). Generalizing the Hough transform to detect arbitrary shapes. Pattern Recognition 13 (2): 111-122. -//! Guil, N., González-Linares, J.M. and Zapata, E.L. (1999). Bidimensional shape detection using an invariant approach. Pattern Recognition 32 (6): 1025-1038. -class CV_EXPORTS GeneralizedHough : public Algorithm -{ -public: - static Ptr create(int method); - - //! set template to search - virtual void setTemplate(InputArray templ, int cannyThreshold = 100, Point templCenter = Point(-1, -1)) = 0; - virtual void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter = Point(-1, -1)) = 0; +//! Detects position only without traslation and rotation +CV_EXPORTS Ptr createGeneralizedHoughBallard(); - //! find template on image - virtual void detect(InputArray image, OutputArray positions, int cannyThreshold = 100) = 0; - virtual void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions) = 0; - - virtual void downloadResults(InputArray d_positions, OutputArray h_positions, OutputArray h_votes = noArray()) = 0; -}; +//! Guil, N., González-Linares, J.M. and Zapata, E.L. (1999). Bidimensional shape detection using an invariant approach. Pattern Recognition 32 (6): 1025-1038. +//! Detects position, traslation and rotation +CV_EXPORTS Ptr createGeneralizedHoughGuil(); ////////////////////////// Corners Detection /////////////////////////// diff --git a/modules/gpuimgproc/perf/perf_hough.cpp b/modules/gpuimgproc/perf/perf_hough.cpp index f72a820f5b..cce8e7432e 100644 --- a/modules/gpuimgproc/perf/perf_hough.cpp +++ b/modules/gpuimgproc/perf/perf_hough.cpp @@ -227,23 +227,59 @@ PERF_TEST_P(Sz_Dp_MinDist, HoughCircles, ////////////////////////////////////////////////////////////////////// // GeneralizedHough -enum { GHT_POSITION = cv::GeneralizedHough::GHT_POSITION, - GHT_SCALE = cv::GeneralizedHough::GHT_SCALE, - GHT_ROTATION = cv::GeneralizedHough::GHT_ROTATION - }; +PERF_TEST_P(Sz, GeneralizedHoughBallard, GPU_TYPICAL_MAT_SIZES) +{ + declare.time(10); + + const cv::Size imageSize = GetParam(); + + const cv::Mat templ = readImage("cv/shared/templ.png", cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(templ.empty()); + + cv::Mat image(imageSize, CV_8UC1, cv::Scalar::all(0)); + templ.copyTo(image(cv::Rect(50, 50, templ.cols, templ.rows))); + + cv::Mat edges; + cv::Canny(image, edges, 50, 100); + + cv::Mat dx, dy; + cv::Sobel(image, dx, CV_32F, 1, 0); + cv::Sobel(image, dy, CV_32F, 0, 1); + + if (PERF_RUN_GPU()) + { + cv::Ptr alg = cv::gpu::createGeneralizedHoughBallard(); + + const cv::gpu::GpuMat d_edges(edges); + const cv::gpu::GpuMat d_dx(dx); + const cv::gpu::GpuMat d_dy(dy); + cv::gpu::GpuMat positions; + + alg->setTemplate(cv::gpu::GpuMat(templ)); + + TEST_CYCLE() alg->detect(d_edges, d_dx, d_dy, positions); + + GPU_SANITY_CHECK(positions); + } + else + { + cv::Ptr alg = cv::createGeneralizedHoughBallard(); + + cv::Mat positions; + + alg->setTemplate(templ); -CV_FLAGS(GHMethod, GHT_POSITION, GHT_SCALE, GHT_ROTATION); + TEST_CYCLE() alg->detect(edges, dx, dy, positions); -DEF_PARAM_TEST(Method_Sz, GHMethod, cv::Size); + CPU_SANITY_CHECK(positions); + } +} -PERF_TEST_P(Method_Sz, GeneralizedHough, - Combine(Values(GHMethod(GHT_POSITION), GHMethod(GHT_POSITION | GHT_SCALE), GHMethod(GHT_POSITION | GHT_ROTATION), GHMethod(GHT_POSITION | GHT_SCALE | GHT_ROTATION)), - GPU_TYPICAL_MAT_SIZES)) +PERF_TEST_P(Sz, GeneralizedHoughGuil, GPU_TYPICAL_MAT_SIZES) { declare.time(10); - const int method = GET_PARAM(0); - const cv::Size imageSize = GET_PARAM(1); + const cv::Size imageSize = GetParam(); const cv::Mat templ = readImage("cv/shared/templ.png", cv::IMREAD_GRAYSCALE); ASSERT_FALSE(templ.empty()); @@ -281,39 +317,32 @@ PERF_TEST_P(Method_Sz, GeneralizedHough, if (PERF_RUN_GPU()) { + cv::Ptr alg = cv::gpu::createGeneralizedHoughGuil(); + alg->setMaxAngle(90.0); + alg->setAngleStep(2.0); + const cv::gpu::GpuMat d_edges(edges); const cv::gpu::GpuMat d_dx(dx); const cv::gpu::GpuMat d_dy(dy); - cv::gpu::GpuMat posAndVotes; + cv::gpu::GpuMat positions; - cv::Ptr d_hough = cv::gpu::GeneralizedHough::create(method); - if (method & GHT_ROTATION) - { - d_hough->set("maxAngle", 90.0); - d_hough->set("angleStep", 2.0); - } + alg->setTemplate(cv::gpu::GpuMat(templ)); - d_hough->setTemplate(cv::gpu::GpuMat(templ)); + TEST_CYCLE() alg->detect(d_edges, d_dx, d_dy, positions); - TEST_CYCLE() d_hough->detect(d_edges, d_dx, d_dy, posAndVotes); - - const cv::gpu::GpuMat positions(1, posAndVotes.cols, CV_32FC4, posAndVotes.data); GPU_SANITY_CHECK(positions); } else { - cv::Mat positions; + cv::Ptr alg = cv::createGeneralizedHoughGuil(); + alg->setMaxAngle(90.0); + alg->setAngleStep(2.0); - cv::Ptr hough = cv::GeneralizedHough::create(method); - if (method & GHT_ROTATION) - { - hough->set("maxAngle", 90.0); - hough->set("angleStep", 2.0); - } + cv::Mat positions; - hough->setTemplate(templ); + alg->setTemplate(templ); - TEST_CYCLE() hough->detect(edges, dx, dy, positions); + TEST_CYCLE() alg->detect(edges, dx, dy, positions); CPU_SANITY_CHECK(positions); } diff --git a/modules/gpuimgproc/src/cuda/generalized_hough.cu b/modules/gpuimgproc/src/cuda/generalized_hough.cu index 14c8600104..fdf691ff4a 100644 --- a/modules/gpuimgproc/src/cuda/generalized_hough.cu +++ b/modules/gpuimgproc/src/cuda/generalized_hough.cu @@ -307,268 +307,6 @@ namespace cv { namespace gpu { namespace cudev return totalCount; } - //////////////////////////////////////////////////////////////////////// - // Ballard_PosScale - - __global__ void Ballard_PosScale_calcHist(const unsigned int* coordList, const float* thetaList, - PtrStep r_table, const int* r_sizes, - PtrStepi hist, const int rows, const int cols, - const float minScale, const float scaleStep, const int scaleRange, - const float idp, const float thetaScale) - { - const unsigned int coord = coordList[blockIdx.x]; - float2 p; - p.x = (coord & 0xFFFF); - p.y = (coord >> 16) & 0xFFFF; - - const float theta = thetaList[blockIdx.x]; - const int n = __float2int_rn(theta * thetaScale); - - const short2* r_row = r_table.ptr(n); - const int r_row_size = r_sizes[n]; - - for (int j = 0; j < r_row_size; ++j) - { - const float2 d = saturate_cast(r_row[j]); - - for (int s = threadIdx.x; s < scaleRange; s += blockDim.x) - { - const float scale = minScale + s * scaleStep; - - float2 c = p - scale * d; - - c.x *= idp; - c.y *= idp; - - if (c.x >= 0 && c.x < cols && c.y >= 0 && c.y < rows) - ::atomicAdd(hist.ptr((s + 1) * (rows + 2) + __float2int_rn(c.y + 1)) + __float2int_rn(c.x + 1), 1); - } - } - } - - void Ballard_PosScale_calcHist_gpu(const unsigned int* coordList, const float* thetaList, int pointsCount, - PtrStepSz r_table, const int* r_sizes, - PtrStepi hist, int rows, int cols, - float minScale, float scaleStep, int scaleRange, - float dp, int levels) - { - const dim3 block(256); - const dim3 grid(pointsCount); - - const float idp = 1.0f / dp; - const float thetaScale = levels / (2.0f * CV_PI_F); - - Ballard_PosScale_calcHist<<>>(coordList, thetaList, - r_table, r_sizes, - hist, rows, cols, - minScale, scaleStep, scaleRange, - idp, thetaScale); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - } - - __global__ void Ballard_PosScale_findPosInHist(const PtrStepi hist, const int rows, const int cols, const int scaleRange, - float4* out, int3* votes, const int maxSize, - const float minScale, const float scaleStep, const float dp, const int threshold) - { - const int x = blockIdx.x * blockDim.x + threadIdx.x; - const int y = blockIdx.y * blockDim.y + threadIdx.y; - - if (x >= cols || y >= rows) - return; - - for (int s = 0; s < scaleRange; ++s) - { - const float scale = minScale + s * scaleStep; - - const int prevScaleIdx = (s) * (rows + 2); - const int curScaleIdx = (s + 1) * (rows + 2); - const int nextScaleIdx = (s + 2) * (rows + 2); - - const int curVotes = hist(curScaleIdx + y + 1, x + 1); - - if (curVotes > threshold && - curVotes > hist(curScaleIdx + y + 1, x) && - curVotes >= hist(curScaleIdx + y + 1, x + 2) && - curVotes > hist(curScaleIdx + y, x + 1) && - curVotes >= hist(curScaleIdx + y + 2, x + 1) && - curVotes > hist(prevScaleIdx + y + 1, x + 1) && - curVotes >= hist(nextScaleIdx + y + 1, x + 1)) - { - const int ind = ::atomicAdd(&g_counter, 1); - - if (ind < maxSize) - { - out[ind] = make_float4(x * dp, y * dp, scale, 0.0f); - votes[ind] = make_int3(curVotes, curVotes, 0); - } - } - } - } - - int Ballard_PosScale_findPosInHist_gpu(PtrStepi hist, int rows, int cols, int scaleRange, float4* out, int3* votes, int maxSize, - float minScale, float scaleStep, float dp, int threshold) - { - void* counterPtr; - cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); - - cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); - - const dim3 block(32, 8); - const dim3 grid(divUp(cols, block.x), divUp(rows, block.y)); - - cudaSafeCall( cudaFuncSetCacheConfig(Ballard_PosScale_findPosInHist, cudaFuncCachePreferL1) ); - - Ballard_PosScale_findPosInHist<<>>(hist, rows, cols, scaleRange, out, votes, - maxSize, minScale, scaleStep, dp, threshold); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - - int totalCount; - cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); - - totalCount = ::min(totalCount, maxSize); - - return totalCount; - } - - //////////////////////////////////////////////////////////////////////// - // Ballard_PosRotation - - __global__ void Ballard_PosRotation_calcHist(const unsigned int* coordList, const float* thetaList, - PtrStep r_table, const int* r_sizes, - PtrStepi hist, const int rows, const int cols, - const float minAngle, const float angleStep, const int angleRange, - const float idp, const float thetaScale) - { - const unsigned int coord = coordList[blockIdx.x]; - float2 p; - p.x = (coord & 0xFFFF); - p.y = (coord >> 16) & 0xFFFF; - - const float thetaVal = thetaList[blockIdx.x]; - - for (int a = threadIdx.x; a < angleRange; a += blockDim.x) - { - const float angle = (minAngle + a * angleStep) * (CV_PI_F / 180.0f); - float sinA, cosA; - sincosf(angle, &sinA, &cosA); - - float theta = thetaVal - angle; - if (theta < 0) - theta += 2.0f * CV_PI_F; - - const int n = __float2int_rn(theta * thetaScale); - - const short2* r_row = r_table.ptr(n); - const int r_row_size = r_sizes[n]; - - for (int j = 0; j < r_row_size; ++j) - { - const float2 d = saturate_cast(r_row[j]); - - const float2 dr = make_float2(d.x * cosA - d.y * sinA, d.x * sinA + d.y * cosA); - - float2 c = make_float2(p.x - dr.x, p.y - dr.y); - c.x *= idp; - c.y *= idp; - - if (c.x >= 0 && c.x < cols && c.y >= 0 && c.y < rows) - ::atomicAdd(hist.ptr((a + 1) * (rows + 2) + __float2int_rn(c.y + 1)) + __float2int_rn(c.x + 1), 1); - } - } - } - - void Ballard_PosRotation_calcHist_gpu(const unsigned int* coordList, const float* thetaList, int pointsCount, - PtrStepSz r_table, const int* r_sizes, - PtrStepi hist, int rows, int cols, - float minAngle, float angleStep, int angleRange, - float dp, int levels) - { - const dim3 block(256); - const dim3 grid(pointsCount); - - const float idp = 1.0f / dp; - const float thetaScale = levels / (2.0f * CV_PI_F); - - Ballard_PosRotation_calcHist<<>>(coordList, thetaList, - r_table, r_sizes, - hist, rows, cols, - minAngle, angleStep, angleRange, - idp, thetaScale); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - } - - __global__ void Ballard_PosRotation_findPosInHist(const PtrStepi hist, const int rows, const int cols, const int angleRange, - float4* out, int3* votes, const int maxSize, - const float minAngle, const float angleStep, const float dp, const int threshold) - { - const int x = blockIdx.x * blockDim.x + threadIdx.x; - const int y = blockIdx.y * blockDim.y + threadIdx.y; - - if (x >= cols || y >= rows) - return; - - for (int a = 0; a < angleRange; ++a) - { - const float angle = minAngle + a * angleStep; - - const int prevAngleIdx = (a) * (rows + 2); - const int curAngleIdx = (a + 1) * (rows + 2); - const int nextAngleIdx = (a + 2) * (rows + 2); - - const int curVotes = hist(curAngleIdx + y + 1, x + 1); - - if (curVotes > threshold && - curVotes > hist(curAngleIdx + y + 1, x) && - curVotes >= hist(curAngleIdx + y + 1, x + 2) && - curVotes > hist(curAngleIdx + y, x + 1) && - curVotes >= hist(curAngleIdx + y + 2, x + 1) && - curVotes > hist(prevAngleIdx + y + 1, x + 1) && - curVotes >= hist(nextAngleIdx + y + 1, x + 1)) - { - const int ind = ::atomicAdd(&g_counter, 1); - - if (ind < maxSize) - { - out[ind] = make_float4(x * dp, y * dp, 1.0f, angle); - votes[ind] = make_int3(curVotes, 0, curVotes); - } - } - } - } - - int Ballard_PosRotation_findPosInHist_gpu(PtrStepi hist, int rows, int cols, int angleRange, float4* out, int3* votes, int maxSize, - float minAngle, float angleStep, float dp, int threshold) - { - void* counterPtr; - cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); - - cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); - - const dim3 block(32, 8); - const dim3 grid(divUp(cols, block.x), divUp(rows, block.y)); - - cudaSafeCall( cudaFuncSetCacheConfig(Ballard_PosRotation_findPosInHist, cudaFuncCachePreferL1) ); - - Ballard_PosRotation_findPosInHist<<>>(hist, rows, cols, angleRange, out, votes, - maxSize, minAngle, angleStep, dp, threshold); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - - int totalCount; - cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); - - totalCount = ::min(totalCount, maxSize); - - return totalCount; - } - //////////////////////////////////////////////////////////////////////// // Guil_Full diff --git a/modules/gpuimgproc/src/generalized_hough.cpp b/modules/gpuimgproc/src/generalized_hough.cpp index 0d01301744..6adfcb7a26 100644 --- a/modules/gpuimgproc/src/generalized_hough.cpp +++ b/modules/gpuimgproc/src/generalized_hough.cpp @@ -47,7 +47,9 @@ using namespace cv::gpu; #if !defined (HAVE_CUDA) || defined (CUDA_DISABLER) || !defined(HAVE_OPENCV_GPUARITHM) -Ptr cv::gpu::GeneralizedHough::create(int) { throw_no_cuda(); return Ptr(); } +Ptr cv::gpu::createGeneralizedHoughBallard() { throw_no_cuda(); return Ptr(); } + +Ptr cv::gpu::createGeneralizedHoughGuil() { throw_no_cuda(); return Ptr(); } #else /* !defined (HAVE_CUDA) */ @@ -67,22 +69,6 @@ namespace cv { namespace gpu { namespace cudev float dp, int levels); int Ballard_Pos_findPosInHist_gpu(PtrStepSzi hist, float4* out, int3* votes, int maxSize, float dp, int threshold); - void Ballard_PosScale_calcHist_gpu(const unsigned int* coordList, const float* thetaList, int pointsCount, - PtrStepSz r_table, const int* r_sizes, - PtrStepi hist, int rows, int cols, - float minScale, float scaleStep, int scaleRange, - float dp, int levels); - int Ballard_PosScale_findPosInHist_gpu(PtrStepi hist, int rows, int cols, int scaleRange, float4* out, int3* votes, int maxSize, - float minScale, float scaleStep, float dp, int threshold); - - void Ballard_PosRotation_calcHist_gpu(const unsigned int* coordList, const float* thetaList, int pointsCount, - PtrStepSz r_table, const int* r_sizes, - PtrStepi hist, int rows, int cols, - float minAngle, float angleStep, int angleRange, - float dp, int levels); - int Ballard_PosRotation_findPosInHist_gpu(PtrStepi hist, int rows, int cols, int angleRange, float4* out, int3* votes, int maxSize, - float minAngle, float angleStep, float dp, int threshold); - void Guil_Full_setTemplFeatures(PtrStepb p1_pos, PtrStepb p1_theta, PtrStepb p2_pos, PtrStepb d12, PtrStepb r1, PtrStepb r2); void Guil_Full_setImageFeatures(PtrStepb p1_pos, PtrStepb p1_theta, PtrStepb p2_pos, PtrStepb d12, PtrStepb r1, PtrStepb r2); void Guil_Full_buildTemplFeatureList_gpu(const unsigned int* coordList, const float* thetaList, int pointsCount, @@ -110,278 +96,207 @@ namespace cv { namespace gpu { namespace cudev } }}} +// common + namespace { - ///////////////////////////////////// - // GeneralizedHoughBase - - class GeneralizedHoughBase : public gpu::GeneralizedHough + class GeneralizedHoughBase { - public: + protected: GeneralizedHoughBase(); + virtual ~GeneralizedHoughBase() {} - void setTemplate(InputArray templ, int cannyThreshold = 100, Point templCenter = Point(-1, -1)); - void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter = Point(-1, -1)); + void setTemplateImpl(InputArray templ, Point templCenter); + void setTemplateImpl(InputArray edges, InputArray dx, InputArray dy, Point templCenter); - void detect(InputArray image, OutputArray positions, int cannyThreshold = 100); - void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions); + void detectImpl(InputArray image, OutputArray positions, OutputArray votes); + void detectImpl(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes); - void downloadResults(InputArray d_positions, OutputArray h_positions, OutputArray h_votes = noArray()); + void buildEdgePointList(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy); - protected: - virtual void setTemplateImpl(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, Point templCenter) = 0; - virtual void detectImpl(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, OutputArray positions) = 0; + virtual void processTempl() = 0; + virtual void processImage() = 0; + + int cannyLowThresh_; + int cannyHighThresh_; + double minDist_; + double dp_; + int maxBufferSize_; + + Size templSize_; + Point templCenter_; + GpuMat templEdges_; + GpuMat templDx_; + GpuMat templDy_; + + Size imageSize_; + GpuMat imageEdges_; + GpuMat imageDx_; + GpuMat imageDy_; + + GpuMat edgePointList_; + + GpuMat outBuf_; + int posCount_; private: #ifdef HAVE_OPENCV_GPUFILTERS - GpuMat dx_, dy_; - GpuMat edges_; + void calcEdges(InputArray src, GpuMat& edges, GpuMat& dx, GpuMat& dy); +#endif + + void filterMinDist(); + void convertTo(OutputArray positions, OutputArray votes); + +#ifdef HAVE_OPENCV_GPUFILTERS Ptr canny_; Ptr filterDx_; Ptr filterDy_; #endif + + std::vector oldPosBuf_; + std::vector oldVoteBuf_; + std::vector newPosBuf_; + std::vector newVoteBuf_; + std::vector indexies_; }; GeneralizedHoughBase::GeneralizedHoughBase() { + cannyLowThresh_ = 50; + cannyHighThresh_ = 100; + minDist_ = 1.0; + dp_ = 1.0; + + maxBufferSize_ = 10000; + #ifdef HAVE_OPENCV_GPUFILTERS - canny_ = gpu::createCannyEdgeDetector(50, 100); + canny_ = gpu::createCannyEdgeDetector(cannyLowThresh_, cannyHighThresh_); filterDx_ = gpu::createSobelFilter(CV_8UC1, CV_32S, 1, 0); filterDy_ = gpu::createSobelFilter(CV_8UC1, CV_32S, 0, 1); #endif } - void GeneralizedHoughBase::setTemplate(InputArray _templ, int cannyThreshold, Point templCenter) +#ifdef HAVE_OPENCV_GPUFILTERS + void GeneralizedHoughBase::calcEdges(InputArray _src, GpuMat& edges, GpuMat& dx, GpuMat& dy) { -#ifndef HAVE_OPENCV_GPUFILTERS - (void) _templ; - (void) cannyThreshold; - (void) templCenter; - throw_no_cuda(); -#else - GpuMat templ = _templ.getGpuMat(); - - CV_Assert( templ.type() == CV_8UC1 ); - CV_Assert( cannyThreshold > 0 ); + GpuMat src = _src.getGpuMat(); - ensureSizeIsEnough(templ.size(), CV_32SC1, dx_); - ensureSizeIsEnough(templ.size(), CV_32SC1, dy_); + CV_Assert( src.type() == CV_8UC1 ); + CV_Assert( cannyLowThresh_ > 0 && cannyLowThresh_ < cannyHighThresh_ ); - filterDx_->apply(templ, dx_); - filterDy_->apply(templ, dy_); + ensureSizeIsEnough(src.size(), CV_32SC1, dx); + ensureSizeIsEnough(src.size(), CV_32SC1, dy); - ensureSizeIsEnough(templ.size(), CV_8UC1, edges_); + filterDx_->apply(src, dx); + filterDy_->apply(src, dy); - canny_->setLowThreshold(cannyThreshold / 2); - canny_->setHighThreshold(cannyThreshold); - canny_->detect(dx_, dy_, edges_); + ensureSizeIsEnough(src.size(), CV_8UC1, edges); - if (templCenter == Point(-1, -1)) - templCenter = Point(templ.cols / 2, templ.rows / 2); - - setTemplateImpl(edges_, dx_, dy_, templCenter); -#endif - } - - void GeneralizedHoughBase::setTemplate(InputArray _edges, InputArray _dx, InputArray _dy, Point templCenter) - { - GpuMat edges = _edges.getGpuMat(); - GpuMat dx = _dx.getGpuMat(); - GpuMat dy = _dy.getGpuMat(); - - if (templCenter == Point(-1, -1)) - templCenter = Point(edges.cols / 2, edges.rows / 2); - - setTemplateImpl(edges, dx, dy, templCenter); + canny_->setLowThreshold(cannyLowThresh_); + canny_->setHighThreshold(cannyHighThresh_); + canny_->detect(dx, dy, edges); } +#endif - void GeneralizedHoughBase::detect(InputArray _image, OutputArray positions, int cannyThreshold) + void GeneralizedHoughBase::setTemplateImpl(InputArray templ, Point templCenter) { #ifndef HAVE_OPENCV_GPUFILTERS - (void) _image; - (void) positions; - (void) cannyThreshold; + (void) templ; + (void) templCenter; throw_no_cuda(); #else - GpuMat image = _image.getGpuMat(); - - CV_Assert( image.type() == CV_8UC1 ); - CV_Assert( cannyThreshold > 0 ); - - ensureSizeIsEnough(image.size(), CV_32SC1, dx_); - ensureSizeIsEnough(image.size(), CV_32SC1, dy_); - - filterDx_->apply(image, dx_); - filterDy_->apply(image, dy_); + calcEdges(templ, templEdges_, templDx_, templDy_); - ensureSizeIsEnough(image.size(), CV_8UC1, edges_); + if (templCenter == Point(-1, -1)) + templCenter = Point(templEdges_.cols / 2, templEdges_.rows / 2); - canny_->setLowThreshold(cannyThreshold / 2); - canny_->setHighThreshold(cannyThreshold); - canny_->detect(dx_, dy_, edges_); + templSize_ = templEdges_.size(); + templCenter_ = templCenter; - detectImpl(edges_, dx_, dy_, positions); + processTempl(); #endif } - void GeneralizedHoughBase::detect(InputArray _edges, InputArray _dx, InputArray _dy, OutputArray positions) + void GeneralizedHoughBase::setTemplateImpl(InputArray edges, InputArray dx, InputArray dy, Point templCenter) { - GpuMat edges = _edges.getGpuMat(); - GpuMat dx = _dx.getGpuMat(); - GpuMat dy = _dy.getGpuMat(); - - detectImpl(edges, dx, dy, positions); - } - - void GeneralizedHoughBase::downloadResults(InputArray _d_positions, OutputArray h_positions, OutputArray h_votes) - { - GpuMat d_positions = _d_positions.getGpuMat(); - - if (d_positions.empty()) - { - h_positions.release(); - if (h_votes.needed()) - h_votes.release(); - return; - } + edges.getGpuMat().copyTo(templEdges_); + dx.getGpuMat().copyTo(templDx_); + dy.getGpuMat().copyTo(templDy_); - CV_Assert( d_positions.rows == 2 && d_positions.type() == CV_32FC4 ); + CV_Assert( templEdges_.type() == CV_8UC1 ); + CV_Assert( templDx_.type() == CV_32FC1 && templDx_.size() == templEdges_.size() ); + CV_Assert( templDy_.type() == templDx_.type() && templDy_.size() == templEdges_.size() ); - d_positions.row(0).download(h_positions); - - if (h_votes.needed()) - { - GpuMat d_votes(1, d_positions.cols, CV_32SC3, d_positions.ptr(1)); - d_votes.download(h_votes); - } - } + if (templCenter == Point(-1, -1)) + templCenter = Point(templEdges_.cols / 2, templEdges_.rows / 2); - ///////////////////////////////////// - // GHT_Pos + templSize_ = templEdges_.size(); + templCenter_ = templCenter; - template void releaseVector(std::vector& v) - { - std::vector empty; - empty.swap(v); + processTempl(); } - class GHT_Pos : public GeneralizedHoughBase + void GeneralizedHoughBase::detectImpl(InputArray image, OutputArray positions, OutputArray votes) { - public: - GHT_Pos(); - - protected: - void setTemplateImpl(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, Point templCenter); - void detectImpl(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, OutputArray positions); - void releaseImpl(); - - virtual void processTempl() = 0; - virtual void processImage() = 0; - - void buildEdgePointList(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy); - void filterMinDist(); - void convertTo(OutputArray positions); - - int maxSize; - double minDist; - - Size templSize; - Point templCenter; - GpuMat templEdges; - GpuMat templDx; - GpuMat templDy; - - Size imageSize; - GpuMat imageEdges; - GpuMat imageDx; - GpuMat imageDy; - - GpuMat edgePointList; - - GpuMat outBuf; - int posCount; - - std::vector oldPosBuf; - std::vector oldVoteBuf; - std::vector newPosBuf; - std::vector newVoteBuf; - std::vector indexies; - }; - - GHT_Pos::GHT_Pos() - { - maxSize = 10000; - minDist = 1.0; - } +#ifndef HAVE_OPENCV_GPUFILTERS + (void) templ; + (void) templCenter; + throw_no_cuda(); +#else + calcEdges(image, imageEdges_, imageDx_, imageDy_); - void GHT_Pos::setTemplateImpl(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, Point templCenter_) - { - templSize = edges.size(); - templCenter = templCenter_; + imageSize_ = imageEdges_.size(); - ensureSizeIsEnough(templSize, edges.type(), templEdges); - ensureSizeIsEnough(templSize, dx.type(), templDx); - ensureSizeIsEnough(templSize, dy.type(), templDy); + posCount_ = 0; - edges.copyTo(templEdges); - dx.copyTo(templDx); - dy.copyTo(templDy); + processImage(); - processTempl(); + if (posCount_ == 0) + { + positions.release(); + if (votes.needed()) + votes.release(); + } + else + { + if (minDist_ > 1) + filterMinDist(); + convertTo(positions, votes); + } +#endif } - void GHT_Pos::detectImpl(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, OutputArray positions) + void GeneralizedHoughBase::detectImpl(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes) { - imageSize = edges.size(); + edges.getGpuMat().copyTo(imageEdges_); + dx.getGpuMat().copyTo(imageDx_); + dy.getGpuMat().copyTo(imageDy_); - ensureSizeIsEnough(imageSize, edges.type(), imageEdges); - ensureSizeIsEnough(imageSize, dx.type(), imageDx); - ensureSizeIsEnough(imageSize, dy.type(), imageDy); + CV_Assert( imageEdges_.type() == CV_8UC1 ); + CV_Assert( imageDx_.type() == CV_32FC1 && imageDx_.size() == imageEdges_.size() ); + CV_Assert( imageDy_.type() == imageDx_.type() && imageDy_.size() == imageEdges_.size() ); - edges.copyTo(imageEdges); - dx.copyTo(imageDx); - dy.copyTo(imageDy); + imageSize_ = imageEdges_.size(); - posCount = 0; + posCount_ = 0; processImage(); - if (posCount == 0) + if (posCount_ == 0) + { positions.release(); + if (votes.needed()) + votes.release(); + } else { - if (minDist > 1) + if (minDist_ > 1) filterMinDist(); - convertTo(positions); + convertTo(positions, votes); } } - void GHT_Pos::releaseImpl() - { - templSize = Size(); - templCenter = Point(-1, -1); - templEdges.release(); - templDx.release(); - templDy.release(); - - imageSize = Size(); - imageEdges.release(); - imageDx.release(); - imageDy.release(); - - edgePointList.release(); - - outBuf.release(); - posCount = 0; - - releaseVector(oldPosBuf); - releaseVector(oldVoteBuf); - releaseVector(newPosBuf); - releaseVector(newVoteBuf); - releaseVector(indexies); - } - - void GHT_Pos::buildEdgePointList(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy) + void GeneralizedHoughBase::buildEdgePointList(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy) { using namespace cv::gpu::cudev::ght; @@ -397,17 +312,17 @@ namespace 0 }; - CV_Assert(edges.type() == CV_8UC1); - CV_Assert(dx.size() == edges.size()); - CV_Assert(dy.type() == dx.type() && dy.size() == edges.size()); + CV_Assert( edges.type() == CV_8UC1 ); + CV_Assert( dx.size() == edges.size() ); + CV_Assert( dy.type() == dx.type() && dy.size() == edges.size() ); const func_t func = funcs[dx.depth()]; - CV_Assert(func != 0); + CV_Assert( func != 0 ); - edgePointList.cols = (int) (edgePointList.step / sizeof(int)); - ensureSizeIsEnough(2, edges.size().area(), CV_32SC1, edgePointList); + edgePointList_.cols = (int) (edgePointList_.step / sizeof(int)); + ensureSizeIsEnough(2, edges.size().area(), CV_32SC1, edgePointList_); - edgePointList.cols = func(edges, dx, dy, edgePointList.ptr(0), edgePointList.ptr(1)); + edgePointList_.cols = func(edges, dx, dy, edgePointList_.ptr(0), edgePointList_.ptr(1)); } struct IndexCmp @@ -422,37 +337,37 @@ namespace } }; - void GHT_Pos::filterMinDist() + void GeneralizedHoughBase::filterMinDist() { - oldPosBuf.resize(posCount); - oldVoteBuf.resize(posCount); + oldPosBuf_.resize(posCount_); + oldVoteBuf_.resize(posCount_); - cudaSafeCall( cudaMemcpy(&oldPosBuf[0], outBuf.ptr(0), posCount * sizeof(float4), cudaMemcpyDeviceToHost) ); - cudaSafeCall( cudaMemcpy(&oldVoteBuf[0], outBuf.ptr(1), posCount * sizeof(int3), cudaMemcpyDeviceToHost) ); + cudaSafeCall( cudaMemcpy(&oldPosBuf_[0], outBuf_.ptr(0), posCount_ * sizeof(float4), cudaMemcpyDeviceToHost) ); + cudaSafeCall( cudaMemcpy(&oldVoteBuf_[0], outBuf_.ptr(1), posCount_ * sizeof(int3), cudaMemcpyDeviceToHost) ); - indexies.resize(posCount); - for (int i = 0; i < posCount; ++i) - indexies[i] = i; - std::sort(indexies.begin(), indexies.end(), IndexCmp(&oldVoteBuf[0])); + indexies_.resize(posCount_); + for (int i = 0; i < posCount_; ++i) + indexies_[i] = i; + std::sort(indexies_.begin(), indexies_.end(), IndexCmp(&oldVoteBuf_[0])); - newPosBuf.clear(); - newVoteBuf.clear(); - newPosBuf.reserve(posCount); - newVoteBuf.reserve(posCount); + newPosBuf_.clear(); + newVoteBuf_.clear(); + newPosBuf_.reserve(posCount_); + newVoteBuf_.reserve(posCount_); - const int cellSize = cvRound(minDist); - const int gridWidth = (imageSize.width + cellSize - 1) / cellSize; - const int gridHeight = (imageSize.height + cellSize - 1) / cellSize; + const int cellSize = cvRound(minDist_); + const int gridWidth = (imageSize_.width + cellSize - 1) / cellSize; + const int gridHeight = (imageSize_.height + cellSize - 1) / cellSize; std::vector< std::vector > grid(gridWidth * gridHeight); - const double minDist2 = minDist * minDist; + const double minDist2 = minDist_ * minDist_; - for (int i = 0; i < posCount; ++i) + for (int i = 0; i < posCount_; ++i) { - const int ind = indexies[i]; + const int ind = indexies_[i]; - Point2f p(oldPosBuf[ind].x, oldPosBuf[ind].y); + Point2f p(oldPosBuf_[ind].x, oldPosBuf_[ind].y); bool good = true; @@ -495,353 +410,238 @@ namespace { grid[yCell * gridWidth + xCell].push_back(p); - newPosBuf.push_back(oldPosBuf[ind]); - newVoteBuf.push_back(oldVoteBuf[ind]); + newPosBuf_.push_back(oldPosBuf_[ind]); + newVoteBuf_.push_back(oldVoteBuf_[ind]); } } - posCount = static_cast(newPosBuf.size()); - cudaSafeCall( cudaMemcpy(outBuf.ptr(0), &newPosBuf[0], posCount * sizeof(float4), cudaMemcpyHostToDevice) ); - cudaSafeCall( cudaMemcpy(outBuf.ptr(1), &newVoteBuf[0], posCount * sizeof(int3), cudaMemcpyHostToDevice) ); + posCount_ = static_cast(newPosBuf_.size()); + cudaSafeCall( cudaMemcpy(outBuf_.ptr(0), &newPosBuf_[0], posCount_ * sizeof(float4), cudaMemcpyHostToDevice) ); + cudaSafeCall( cudaMemcpy(outBuf_.ptr(1), &newVoteBuf_[0], posCount_ * sizeof(int3), cudaMemcpyHostToDevice) ); } - void GHT_Pos::convertTo(OutputArray positions) + void GeneralizedHoughBase::convertTo(OutputArray positions, OutputArray votes) { - ensureSizeIsEnough(2, posCount, CV_32FC4, positions); - GpuMat(2, posCount, CV_32FC4, outBuf.data, outBuf.step).copyTo(positions); + ensureSizeIsEnough(1, posCount_, CV_32FC4, positions); + GpuMat(1, posCount_, CV_32FC4, outBuf_.ptr(0), outBuf_.step).copyTo(positions); + + if (votes.needed()) + { + ensureSizeIsEnough(1, posCount_, CV_32FC3, votes); + GpuMat(1, posCount_, CV_32FC4, outBuf_.ptr(1), outBuf_.step).copyTo(votes); + } } +} - ///////////////////////////////////// - // POSITION Ballard +// GeneralizedHoughBallard - class GHT_Ballard_Pos : public GHT_Pos +namespace +{ + class GeneralizedHoughBallardImpl : public GeneralizedHoughBallard, private GeneralizedHoughBase { public: - AlgorithmInfo* info() const; - - GHT_Ballard_Pos(); - - protected: - void releaseImpl(); + GeneralizedHoughBallardImpl(); - void processTempl(); - void processImage(); + void setTemplate(InputArray templ, Point templCenter) { setTemplateImpl(templ, templCenter); } + void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter) { setTemplateImpl(edges, dx, dy, templCenter); } - virtual void calcHist(); - virtual void findPosInHist(); + void detect(InputArray image, OutputArray positions, OutputArray votes) { detectImpl(image, positions, votes); } + void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes) { detectImpl(edges, dx, dy, positions, votes); } - int levels; - int votesThreshold; - double dp; + void setCannyLowThresh(int cannyLowThresh) { cannyLowThresh_ = cannyLowThresh; } + int getCannyLowThresh() const { return cannyLowThresh_; } - GpuMat r_table; - GpuMat r_sizes; + void setCannyHighThresh(int cannyHighThresh) { cannyHighThresh_ = cannyHighThresh; } + int getCannyHighThresh() const { return cannyHighThresh_; } - GpuMat hist; - }; + void setMinDist(double minDist) { minDist_ = minDist; } + double getMinDist() const { return minDist_; } - CV_INIT_ALGORITHM(GHT_Ballard_Pos, "GeneralizedHough_GPU.POSITION", - obj.info()->addParam(obj, "maxSize", obj.maxSize, false, 0, 0, - "Maximal size of inner buffers."); - obj.info()->addParam(obj, "minDist", obj.minDist, false, 0, 0, - "Minimum distance between the centers of the detected objects."); - obj.info()->addParam(obj, "levels", obj.levels, false, 0, 0, - "R-Table levels."); - obj.info()->addParam(obj, "votesThreshold", obj.votesThreshold, false, 0, 0, - "The accumulator threshold for the template centers at the detection stage. The smaller it is, the more false positions may be detected."); - obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, - "Inverse ratio of the accumulator resolution to the image resolution.")); - - GHT_Ballard_Pos::GHT_Ballard_Pos() - { - levels = 360; - votesThreshold = 100; - dp = 1.0; - } + void setDp(double dp) { dp_ = dp; } + double getDp() const { return dp_; } - void GHT_Ballard_Pos::releaseImpl() - { - GHT_Pos::releaseImpl(); + void setMaxBufferSize(int maxBufferSize) { maxBufferSize_ = maxBufferSize; } + int getMaxBufferSize() const { return maxBufferSize_; } - r_table.release(); - r_sizes.release(); + void setLevels(int levels) { levels_ = levels; } + int getLevels() const { return levels_; } - hist.release(); - } + void setVotesThreshold(int votesThreshold) { votesThreshold_ = votesThreshold; } + int getVotesThreshold() const { return votesThreshold_; } - void GHT_Ballard_Pos::processTempl() - { - using namespace cv::gpu::cudev::ght; + private: + void processTempl(); + void processImage(); - CV_Assert(levels > 0); + void calcHist(); + void findPosInHist(); - buildEdgePointList(templEdges, templDx, templDy); + int levels_; + int votesThreshold_; - ensureSizeIsEnough(levels + 1, maxSize, CV_16SC2, r_table); - ensureSizeIsEnough(1, levels + 1, CV_32SC1, r_sizes); - r_sizes.setTo(Scalar::all(0)); + GpuMat r_table_; + GpuMat r_sizes_; - if (edgePointList.cols > 0) - { - buildRTable_gpu(edgePointList.ptr(0), edgePointList.ptr(1), edgePointList.cols, - r_table, r_sizes.ptr(), make_short2(templCenter.x, templCenter.y), levels); - gpu::min(r_sizes, maxSize, r_sizes); - } - } + GpuMat hist_; + }; - void GHT_Ballard_Pos::processImage() + GeneralizedHoughBallardImpl::GeneralizedHoughBallardImpl() { - calcHist(); - findPosInHist(); + levels_ = 360; + votesThreshold_ = 100; } - void GHT_Ballard_Pos::calcHist() + void GeneralizedHoughBallardImpl::processTempl() { using namespace cv::gpu::cudev::ght; - CV_Assert(levels > 0 && r_table.rows == (levels + 1) && r_sizes.cols == (levels + 1)); - CV_Assert(dp > 0.0); - - const double idp = 1.0 / dp; + CV_Assert( levels_ > 0 ); - buildEdgePointList(imageEdges, imageDx, imageDy); + buildEdgePointList(templEdges_, templDx_, templDy_); - ensureSizeIsEnough(cvCeil(imageSize.height * idp) + 2, cvCeil(imageSize.width * idp) + 2, CV_32SC1, hist); - hist.setTo(Scalar::all(0)); + ensureSizeIsEnough(levels_ + 1, maxBufferSize_, CV_16SC2, r_table_); + ensureSizeIsEnough(1, levels_ + 1, CV_32SC1, r_sizes_); + r_sizes_.setTo(Scalar::all(0)); - if (edgePointList.cols > 0) + if (edgePointList_.cols > 0) { - Ballard_Pos_calcHist_gpu(edgePointList.ptr(0), edgePointList.ptr(1), edgePointList.cols, - r_table, r_sizes.ptr(), - hist, - (float)dp, levels); + buildRTable_gpu(edgePointList_.ptr(0), edgePointList_.ptr(1), edgePointList_.cols, + r_table_, r_sizes_.ptr(), make_short2(templCenter_.x, templCenter_.y), levels_); + gpu::min(r_sizes_, maxBufferSize_, r_sizes_); } } - void GHT_Ballard_Pos::findPosInHist() + void GeneralizedHoughBallardImpl::processImage() { - using namespace cv::gpu::cudev::ght; - - CV_Assert(votesThreshold > 0); - - ensureSizeIsEnough(2, maxSize, CV_32FC4, outBuf); - - posCount = Ballard_Pos_findPosInHist_gpu(hist, outBuf.ptr(0), outBuf.ptr(1), maxSize, (float)dp, votesThreshold); - } - - ///////////////////////////////////// - // POSITION & SCALE - - class GHT_Ballard_PosScale : public GHT_Ballard_Pos - { - public: - AlgorithmInfo* info() const; - - GHT_Ballard_PosScale(); - - protected: - void calcHist(); - void findPosInHist(); - - double minScale; - double maxScale; - double scaleStep; - }; - - CV_INIT_ALGORITHM(GHT_Ballard_PosScale, "GeneralizedHough_GPU.POSITION_SCALE", - obj.info()->addParam(obj, "maxSize", obj.maxSize, false, 0, 0, - "Maximal size of inner buffers."); - obj.info()->addParam(obj, "minDist", obj.minDist, false, 0, 0, - "Minimum distance between the centers of the detected objects."); - obj.info()->addParam(obj, "levels", obj.levels, false, 0, 0, - "R-Table levels."); - obj.info()->addParam(obj, "votesThreshold", obj.votesThreshold, false, 0, 0, - "The accumulator threshold for the template centers at the detection stage. The smaller it is, the more false positions may be detected."); - obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, - "Inverse ratio of the accumulator resolution to the image resolution."); - obj.info()->addParam(obj, "minScale", obj.minScale, false, 0, 0, - "Minimal scale to detect."); - obj.info()->addParam(obj, "maxScale", obj.maxScale, false, 0, 0, - "Maximal scale to detect."); - obj.info()->addParam(obj, "scaleStep", obj.scaleStep, false, 0, 0, - "Scale step.")); - - GHT_Ballard_PosScale::GHT_Ballard_PosScale() - { - minScale = 0.5; - maxScale = 2.0; - scaleStep = 0.05; + calcHist(); + findPosInHist(); } - void GHT_Ballard_PosScale::calcHist() + void GeneralizedHoughBallardImpl::calcHist() { using namespace cv::gpu::cudev::ght; - CV_Assert(levels > 0 && r_table.rows == (levels + 1) && r_sizes.cols == (levels + 1)); - CV_Assert(dp > 0.0); - CV_Assert(minScale > 0.0 && minScale < maxScale); - CV_Assert(scaleStep > 0.0); + CV_Assert( levels_ > 0 && r_table_.rows == (levels_ + 1) && r_sizes_.cols == (levels_ + 1) ); + CV_Assert( dp_ > 0.0); - const double idp = 1.0 / dp; - const int scaleRange = cvCeil((maxScale - minScale) / scaleStep); - const int rows = cvCeil(imageSize.height * idp); - const int cols = cvCeil(imageSize.width * idp); + const double idp = 1.0 / dp_; - buildEdgePointList(imageEdges, imageDx, imageDy); + buildEdgePointList(imageEdges_, imageDx_, imageDy_); - ensureSizeIsEnough((scaleRange + 2) * (rows + 2), cols + 2, CV_32SC1, hist); - hist.setTo(Scalar::all(0)); + ensureSizeIsEnough(cvCeil(imageSize_.height * idp) + 2, cvCeil(imageSize_.width * idp) + 2, CV_32SC1, hist_); + hist_.setTo(Scalar::all(0)); - if (edgePointList.cols > 0) + if (edgePointList_.cols > 0) { - Ballard_PosScale_calcHist_gpu(edgePointList.ptr(0), edgePointList.ptr(1), edgePointList.cols, - r_table, r_sizes.ptr(), - hist, rows, cols, - (float)minScale, (float)scaleStep, scaleRange, (float)dp, levels); + Ballard_Pos_calcHist_gpu(edgePointList_.ptr(0), edgePointList_.ptr(1), edgePointList_.cols, + r_table_, r_sizes_.ptr(), + hist_, + (float)dp_, levels_); } } - void GHT_Ballard_PosScale::findPosInHist() + void GeneralizedHoughBallardImpl::findPosInHist() { using namespace cv::gpu::cudev::ght; - CV_Assert(votesThreshold > 0); - - const double idp = 1.0 / dp; - const int scaleRange = cvCeil((maxScale - minScale) / scaleStep); - const int rows = cvCeil(imageSize.height * idp); - const int cols = cvCeil(imageSize.width * idp); + CV_Assert( votesThreshold_ > 0 ); - ensureSizeIsEnough(2, maxSize, CV_32FC4, outBuf); + ensureSizeIsEnough(2, maxBufferSize_, CV_32FC4, outBuf_); - posCount = Ballard_PosScale_findPosInHist_gpu(hist, rows, cols, scaleRange, outBuf.ptr(0), outBuf.ptr(1), maxSize, (float)minScale, (float)scaleStep, (float)dp, votesThreshold); + posCount_ = Ballard_Pos_findPosInHist_gpu(hist_, outBuf_.ptr(0), outBuf_.ptr(1), maxBufferSize_, (float)dp_, votesThreshold_); } +} - ///////////////////////////////////// - // POSITION & Rotation +Ptr cv::gpu::createGeneralizedHoughBallard() +{ + return new GeneralizedHoughBallardImpl; +} - class GHT_Ballard_PosRotation : public GHT_Ballard_Pos +// GeneralizedHoughGuil + +namespace +{ + class GeneralizedHoughGuilImpl : public GeneralizedHoughGuil, private GeneralizedHoughBase { public: - AlgorithmInfo* info() const; + GeneralizedHoughGuilImpl(); - GHT_Ballard_PosRotation(); + void setTemplate(InputArray templ, Point templCenter) { setTemplateImpl(templ, templCenter); } + void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter) { setTemplateImpl(edges, dx, dy, templCenter); } - protected: - void calcHist(); - void findPosInHist(); + void detect(InputArray image, OutputArray positions, OutputArray votes) { detectImpl(image, positions, votes); } + void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes) { detectImpl(edges, dx, dy, positions, votes); } - double minAngle; - double maxAngle; - double angleStep; - }; + void setCannyLowThresh(int cannyLowThresh) { cannyLowThresh_ = cannyLowThresh; } + int getCannyLowThresh() const { return cannyLowThresh_; } - CV_INIT_ALGORITHM(GHT_Ballard_PosRotation, "GeneralizedHough_GPU.POSITION_ROTATION", - obj.info()->addParam(obj, "maxSize", obj.maxSize, false, 0, 0, - "Maximal size of inner buffers."); - obj.info()->addParam(obj, "minDist", obj.minDist, false, 0, 0, - "Minimum distance between the centers of the detected objects."); - obj.info()->addParam(obj, "levels", obj.levels, false, 0, 0, - "R-Table levels."); - obj.info()->addParam(obj, "votesThreshold", obj.votesThreshold, false, 0, 0, - "The accumulator threshold for the template centers at the detection stage. The smaller it is, the more false positions may be detected."); - obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, - "Inverse ratio of the accumulator resolution to the image resolution."); - obj.info()->addParam(obj, "minAngle", obj.minAngle, false, 0, 0, - "Minimal rotation angle to detect in degrees."); - obj.info()->addParam(obj, "maxAngle", obj.maxAngle, false, 0, 0, - "Maximal rotation angle to detect in degrees."); - obj.info()->addParam(obj, "angleStep", obj.angleStep, false, 0, 0, - "Angle step in degrees.")); - - GHT_Ballard_PosRotation::GHT_Ballard_PosRotation() - { - minAngle = 0.0; - maxAngle = 360.0; - angleStep = 1.0; - } + void setCannyHighThresh(int cannyHighThresh) { cannyHighThresh_ = cannyHighThresh; } + int getCannyHighThresh() const { return cannyHighThresh_; } - void GHT_Ballard_PosRotation::calcHist() - { - using namespace cv::gpu::cudev::ght; + void setMinDist(double minDist) { minDist_ = minDist; } + double getMinDist() const { return minDist_; } - CV_Assert(levels > 0 && r_table.rows == (levels + 1) && r_sizes.cols == (levels + 1)); - CV_Assert(dp > 0.0); - CV_Assert(minAngle >= 0.0 && minAngle < maxAngle && maxAngle <= 360.0); - CV_Assert(angleStep > 0.0 && angleStep < 360.0); + void setDp(double dp) { dp_ = dp; } + double getDp() const { return dp_; } - const double idp = 1.0 / dp; - const int angleRange = cvCeil((maxAngle - minAngle) / angleStep); - const int rows = cvCeil(imageSize.height * idp); - const int cols = cvCeil(imageSize.width * idp); + void setMaxBufferSize(int maxBufferSize) { maxBufferSize_ = maxBufferSize; } + int getMaxBufferSize() const { return maxBufferSize_; } - buildEdgePointList(imageEdges, imageDx, imageDy); + void setXi(double xi) { xi_ = xi; } + double getXi() const { return xi_; } - ensureSizeIsEnough((angleRange + 2) * (rows + 2), cols + 2, CV_32SC1, hist); - hist.setTo(Scalar::all(0)); + void setLevels(int levels) { levels_ = levels; } + int getLevels() const { return levels_; } - if (edgePointList.cols > 0) - { - Ballard_PosRotation_calcHist_gpu(edgePointList.ptr(0), edgePointList.ptr(1), edgePointList.cols, - r_table, r_sizes.ptr(), - hist, rows, cols, - (float)minAngle, (float)angleStep, angleRange, (float)dp, levels); - } - } + void setAngleEpsilon(double angleEpsilon) { angleEpsilon_ = angleEpsilon; } + double getAngleEpsilon() const { return angleEpsilon_; } - void GHT_Ballard_PosRotation::findPosInHist() - { - using namespace cv::gpu::cudev::ght; + void setMinAngle(double minAngle) { minAngle_ = minAngle; } + double getMinAngle() const { return minAngle_; } - CV_Assert(votesThreshold > 0); + void setMaxAngle(double maxAngle) { maxAngle_ = maxAngle; } + double getMaxAngle() const { return maxAngle_; } - const double idp = 1.0 / dp; - const int angleRange = cvCeil((maxAngle - minAngle) / angleStep); - const int rows = cvCeil(imageSize.height * idp); - const int cols = cvCeil(imageSize.width * idp); + void setAngleStep(double angleStep) { angleStep_ = angleStep; } + double getAngleStep() const { return angleStep_; } - ensureSizeIsEnough(2, maxSize, CV_32FC4, outBuf); + void setAngleThresh(int angleThresh) { angleThresh_ = angleThresh; } + int getAngleThresh() const { return angleThresh_; } - posCount = Ballard_PosRotation_findPosInHist_gpu(hist, rows, cols, angleRange, outBuf.ptr(0), outBuf.ptr(1), maxSize, (float)minAngle, (float)angleStep, (float)dp, votesThreshold); - } + void setMinScale(double minScale) { minScale_ = minScale; } + double getMinScale() const { return minScale_; } - ///////////////////////////////////////// - // POSITION & SCALE & ROTATION + void setMaxScale(double maxScale) { maxScale_ = maxScale; } + double getMaxScale() const { return maxScale_; } - double toRad(double a) - { - return a * CV_PI / 180.0; - } + void setScaleStep(double scaleStep) { scaleStep_ = scaleStep; } + double getScaleStep() const { return scaleStep_; } - double clampAngle(double a) - { - double res = a; - - while (res > 360.0) - res -= 360.0; - while (res < 0) - res += 360.0; + void setScaleThresh(int scaleThresh) { scaleThresh_ = scaleThresh; } + int getScaleThresh() const { return scaleThresh_; } - return res; - } + void setPosThresh(int posThresh) { posThresh_ = posThresh; } + int getPosThresh() const { return posThresh_; } - bool angleEq(double a, double b, double eps = 1.0) - { - return (fabs(clampAngle(a - b)) <= eps); - } + private: + void processTempl(); + void processImage(); - class GHT_Guil_Full : public GHT_Pos - { - public: - AlgorithmInfo* info() const; + double xi_; + int levels_; + double angleEpsilon_; - GHT_Guil_Full(); + double minAngle_; + double maxAngle_; + double angleStep_; + int angleThresh_; - protected: - void releaseImpl(); + double minScale_; + double maxScale_; + double scaleStep_; + int scaleThresh_; - void processTempl(); - void processImage(); + int posThresh_; struct Feature { @@ -858,7 +658,6 @@ namespace int maxSize; void create(int levels, int maxCapacity, bool isTempl); - void release(); }; typedef void (*set_func_t)(PtrStepb p1_pos, PtrStepb p1_theta, PtrStepb p2_pos, PtrStepb d12, PtrStepb r1, PtrStepb r2); @@ -874,167 +673,126 @@ namespace void calcScale(double angle); void calcPosition(double angle, int angleVotes, double scale, int scaleVotes); - double xi; - int levels; - double angleEpsilon; + Feature templFeatures_; + Feature imageFeatures_; - double minAngle; - double maxAngle; - double angleStep; - int angleThresh; + std::vector< std::pair > angles_; + std::vector< std::pair > scales_; - double minScale; - double maxScale; - double scaleStep; - int scaleThresh; + GpuMat hist_; + std::vector h_buf_; + }; - double dp; - int posThresh; + double toRad(double a) + { + return a * CV_PI / 180.0; + } - Feature templFeatures; - Feature imageFeatures; + double clampAngle(double a) + { + double res = a; - std::vector< std::pair > angles; - std::vector< std::pair > scales; + while (res > 360.0) + res -= 360.0; + while (res < 0) + res += 360.0; - GpuMat hist; - std::vector h_buf; - }; + return res; + } - CV_INIT_ALGORITHM(GHT_Guil_Full, "GeneralizedHough_GPU.POSITION_SCALE_ROTATION", - obj.info()->addParam(obj, "minDist", obj.minDist, false, 0, 0, - "Minimum distance between the centers of the detected objects."); - obj.info()->addParam(obj, "maxSize", obj.maxSize, false, 0, 0, - "Maximal size of inner buffers."); - obj.info()->addParam(obj, "xi", obj.xi, false, 0, 0, - "Angle difference in degrees between two points in feature."); - obj.info()->addParam(obj, "levels", obj.levels, false, 0, 0, - "Feature table levels."); - obj.info()->addParam(obj, "angleEpsilon", obj.angleEpsilon, false, 0, 0, - "Maximal difference between angles that treated as equal."); - obj.info()->addParam(obj, "minAngle", obj.minAngle, false, 0, 0, - "Minimal rotation angle to detect in degrees."); - obj.info()->addParam(obj, "maxAngle", obj.maxAngle, false, 0, 0, - "Maximal rotation angle to detect in degrees."); - obj.info()->addParam(obj, "angleStep", obj.angleStep, false, 0, 0, - "Angle step in degrees."); - obj.info()->addParam(obj, "angleThresh", obj.angleThresh, false, 0, 0, - "Angle threshold."); - obj.info()->addParam(obj, "minScale", obj.minScale, false, 0, 0, - "Minimal scale to detect."); - obj.info()->addParam(obj, "maxScale", obj.maxScale, false, 0, 0, - "Maximal scale to detect."); - obj.info()->addParam(obj, "scaleStep", obj.scaleStep, false, 0, 0, - "Scale step."); - obj.info()->addParam(obj, "scaleThresh", obj.scaleThresh, false, 0, 0, - "Scale threshold."); - obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, - "Inverse ratio of the accumulator resolution to the image resolution."); - obj.info()->addParam(obj, "posThresh", obj.posThresh, false, 0, 0, - "Position threshold.")); - - GHT_Guil_Full::GHT_Guil_Full() + bool angleEq(double a, double b, double eps = 1.0) { - maxSize = 1000; - xi = 90.0; - levels = 360; - angleEpsilon = 1.0; - - minAngle = 0.0; - maxAngle = 360.0; - angleStep = 1.0; - angleThresh = 15000; - - minScale = 0.5; - maxScale = 2.0; - scaleStep = 0.05; - scaleThresh = 1000; - - dp = 1.0; - posThresh = 100; + return (fabs(clampAngle(a - b)) <= eps); } - void GHT_Guil_Full::releaseImpl() + GeneralizedHoughGuilImpl::GeneralizedHoughGuilImpl() { - GHT_Pos::releaseImpl(); + maxBufferSize_ = 1000; + + xi_ = 90.0; + levels_ = 360; + angleEpsilon_ = 1.0; - templFeatures.release(); - imageFeatures.release(); + minAngle_ = 0.0; + maxAngle_ = 360.0; + angleStep_ = 1.0; + angleThresh_ = 15000; - releaseVector(angles); - releaseVector(scales); + minScale_ = 0.5; + maxScale_ = 2.0; + scaleStep_ = 0.05; + scaleThresh_ = 1000; - hist.release(); - releaseVector(h_buf); + posThresh_ = 100; } - void GHT_Guil_Full::processTempl() + void GeneralizedHoughGuilImpl::processTempl() { using namespace cv::gpu::cudev::ght; - buildFeatureList(templEdges, templDx, templDy, templFeatures, + buildFeatureList(templEdges_, templDx_, templDy_, templFeatures_, Guil_Full_setTemplFeatures, Guil_Full_buildTemplFeatureList_gpu, - true, templCenter); + true, templCenter_); - h_buf.resize(templFeatures.sizes.cols); - cudaSafeCall( cudaMemcpy(&h_buf[0], templFeatures.sizes.data, h_buf.size() * sizeof(int), cudaMemcpyDeviceToHost) ); - templFeatures.maxSize = *max_element(h_buf.begin(), h_buf.end()); + h_buf_.resize(templFeatures_.sizes.cols); + cudaSafeCall( cudaMemcpy(&h_buf_[0], templFeatures_.sizes.data, h_buf_.size() * sizeof(int), cudaMemcpyDeviceToHost) ); + templFeatures_.maxSize = *std::max_element(h_buf_.begin(), h_buf_.end()); } - void GHT_Guil_Full::processImage() + void GeneralizedHoughGuilImpl::processImage() { using namespace cv::gpu::cudev::ght; - CV_Assert(levels > 0); - CV_Assert(templFeatures.sizes.cols == levels + 1); - CV_Assert(minAngle >= 0.0 && minAngle < maxAngle && maxAngle <= 360.0); - CV_Assert(angleStep > 0.0 && angleStep < 360.0); - CV_Assert(angleThresh > 0); - CV_Assert(minScale > 0.0 && minScale < maxScale); - CV_Assert(scaleStep > 0.0); - CV_Assert(scaleThresh > 0); - CV_Assert(dp > 0.0); - CV_Assert(posThresh > 0); + CV_Assert( levels_ > 0 ); + CV_Assert( templFeatures_.sizes.cols == levels_ + 1 ); + CV_Assert( minAngle_ >= 0.0 && minAngle_ < maxAngle_ && maxAngle_ <= 360.0 ); + CV_Assert( angleStep_ > 0.0 && angleStep_ < 360.0 ); + CV_Assert( angleThresh_ > 0 ); + CV_Assert( minScale_ > 0.0 && minScale_ < maxScale_ ); + CV_Assert( scaleStep_ > 0.0 ); + CV_Assert( scaleThresh_ > 0 ); + CV_Assert( dp_ > 0.0 ); + CV_Assert( posThresh_ > 0 ); - const double iAngleStep = 1.0 / angleStep; - const int angleRange = cvCeil((maxAngle - minAngle) * iAngleStep); + const double iAngleStep = 1.0 / angleStep_; + const int angleRange = cvCeil((maxAngle_ - minAngle_) * iAngleStep); - const double iScaleStep = 1.0 / scaleStep; - const int scaleRange = cvCeil((maxScale - minScale) * iScaleStep); + const double iScaleStep = 1.0 / scaleStep_; + const int scaleRange = cvCeil((maxScale_ - minScale_) * iScaleStep); - const double idp = 1.0 / dp; - const int histRows = cvCeil(imageSize.height * idp); - const int histCols = cvCeil(imageSize.width * idp); + const double idp = 1.0 / dp_; + const int histRows = cvCeil(imageSize_.height * idp); + const int histCols = cvCeil(imageSize_.width * idp); - ensureSizeIsEnough(histRows + 2, std::max(angleRange + 1, std::max(scaleRange + 1, histCols + 2)), CV_32SC1, hist); - h_buf.resize(std::max(angleRange + 1, scaleRange + 1)); + ensureSizeIsEnough(histRows + 2, std::max(angleRange + 1, std::max(scaleRange + 1, histCols + 2)), CV_32SC1, hist_); + h_buf_.resize(std::max(angleRange + 1, scaleRange + 1)); - ensureSizeIsEnough(2, maxSize, CV_32FC4, outBuf); + ensureSizeIsEnough(2, maxBufferSize_, CV_32FC4, outBuf_); - buildFeatureList(imageEdges, imageDx, imageDy, imageFeatures, + buildFeatureList(imageEdges_, imageDx_, imageDy_, imageFeatures_, Guil_Full_setImageFeatures, Guil_Full_buildImageFeatureList_gpu, false); calcOrientation(); - for (size_t i = 0; i < angles.size(); ++i) + for (size_t i = 0; i < angles_.size(); ++i) { - const double angle = angles[i].first; - const int angleVotes = angles[i].second; + const double angle = angles_[i].first; + const int angleVotes = angles_[i].second; calcScale(angle); - for (size_t j = 0; j < scales.size(); ++j) + for (size_t j = 0; j < scales_.size(); ++j) { - const double scale = scales[j].first; - const int scaleVotes = scales[j].second; + const double scale = scales_[j].first; + const int scaleVotes = scales_[j].second; calcPosition(angle, angleVotes, scale, scaleVotes); } } } - void GHT_Guil_Full::Feature::create(int levels, int maxCapacity, bool isTempl) + void GeneralizedHoughGuilImpl::Feature::create(int levels, int maxCapacity, bool isTempl) { if (!isTempl) { @@ -1058,128 +816,91 @@ namespace maxSize = 0; } - void GHT_Guil_Full::Feature::release() + void GeneralizedHoughGuilImpl::buildFeatureList(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, Feature& features, + set_func_t set_func, build_func_t build_func, bool isTempl, Point2d center) { - p1_pos.release(); - p1_theta.release(); - p2_pos.release(); + CV_Assert( levels_ > 0 ); - d12.release(); + const double maxDist = sqrt((double) templSize_.width * templSize_.width + templSize_.height * templSize_.height) * maxScale_; - r1.release(); - r2.release(); - - sizes.release(); - - maxSize = 0; - } - - void GHT_Guil_Full::buildFeatureList(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, Feature& features, - set_func_t set_func, build_func_t build_func, bool isTempl, Point2d center) - { - CV_Assert(levels > 0); - - const double maxDist = sqrt((double) templSize.width * templSize.width + templSize.height * templSize.height) * maxScale; - - features.create(levels, maxSize, isTempl); + features.create(levels_, maxBufferSize_, isTempl); set_func(features.p1_pos, features.p1_theta, features.p2_pos, features.d12, features.r1, features.r2); buildEdgePointList(edges, dx, dy); - if (edgePointList.cols > 0) + if (edgePointList_.cols > 0) { - build_func(edgePointList.ptr(0), edgePointList.ptr(1), edgePointList.cols, - features.sizes.ptr(), maxSize, (float)xi, (float)angleEpsilon, levels, make_float2((float)center.x, (float)center.y), (float)maxDist); + build_func(edgePointList_.ptr(0), edgePointList_.ptr(1), edgePointList_.cols, + features.sizes.ptr(), maxBufferSize_, (float)xi_, (float)angleEpsilon_, levels_, make_float2((float)center.x, (float)center.y), (float)maxDist); } } - void GHT_Guil_Full::calcOrientation() + void GeneralizedHoughGuilImpl::calcOrientation() { using namespace cv::gpu::cudev::ght; - const double iAngleStep = 1.0 / angleStep; - const int angleRange = cvCeil((maxAngle - minAngle) * iAngleStep); + const double iAngleStep = 1.0 / angleStep_; + const int angleRange = cvCeil((maxAngle_ - minAngle_) * iAngleStep); - hist.setTo(Scalar::all(0)); - Guil_Full_calcOHist_gpu(templFeatures.sizes.ptr(), imageFeatures.sizes.ptr(0), hist.ptr(), - (float)minAngle, (float)maxAngle, (float)angleStep, angleRange, levels, templFeatures.maxSize); - cudaSafeCall( cudaMemcpy(&h_buf[0], hist.data, h_buf.size() * sizeof(int), cudaMemcpyDeviceToHost) ); + hist_.setTo(Scalar::all(0)); + Guil_Full_calcOHist_gpu(templFeatures_.sizes.ptr(), imageFeatures_.sizes.ptr(0), hist_.ptr(), + (float)minAngle_, (float)maxAngle_, (float)angleStep_, angleRange, levels_, templFeatures_.maxSize); + cudaSafeCall( cudaMemcpy(&h_buf_[0], hist_.data, h_buf_.size() * sizeof(int), cudaMemcpyDeviceToHost) ); - angles.clear(); + angles_.clear(); for (int n = 0; n < angleRange; ++n) { - if (h_buf[n] >= angleThresh) + if (h_buf_[n] >= angleThresh_) { - const double angle = minAngle + n * angleStep; - angles.push_back(std::make_pair(angle, h_buf[n])); + const double angle = minAngle_ + n * angleStep_; + angles_.push_back(std::make_pair(angle, h_buf_[n])); } } } - void GHT_Guil_Full::calcScale(double angle) + void GeneralizedHoughGuilImpl::calcScale(double angle) { using namespace cv::gpu::cudev::ght; - const double iScaleStep = 1.0 / scaleStep; - const int scaleRange = cvCeil((maxScale - minScale) * iScaleStep); + const double iScaleStep = 1.0 / scaleStep_; + const int scaleRange = cvCeil((maxScale_ - minScale_) * iScaleStep); - hist.setTo(Scalar::all(0)); - Guil_Full_calcSHist_gpu(templFeatures.sizes.ptr(), imageFeatures.sizes.ptr(0), hist.ptr(), - (float)angle, (float)angleEpsilon, (float)minScale, (float)maxScale, - (float)iScaleStep, scaleRange, levels, templFeatures.maxSize); - cudaSafeCall( cudaMemcpy(&h_buf[0], hist.data, h_buf.size() * sizeof(int), cudaMemcpyDeviceToHost) ); + hist_.setTo(Scalar::all(0)); + Guil_Full_calcSHist_gpu(templFeatures_.sizes.ptr(), imageFeatures_.sizes.ptr(0), hist_.ptr(), + (float)angle, (float)angleEpsilon_, (float)minScale_, (float)maxScale_, + (float)iScaleStep, scaleRange, levels_, templFeatures_.maxSize); + cudaSafeCall( cudaMemcpy(&h_buf_[0], hist_.data, h_buf_.size() * sizeof(int), cudaMemcpyDeviceToHost) ); - scales.clear(); + scales_.clear(); for (int s = 0; s < scaleRange; ++s) { - if (h_buf[s] >= scaleThresh) + if (h_buf_[s] >= scaleThresh_) { - const double scale = minScale + s * scaleStep; - scales.push_back(std::make_pair(scale, h_buf[s])); + const double scale = minScale_ + s * scaleStep_; + scales_.push_back(std::make_pair(scale, h_buf_[s])); } } } - void GHT_Guil_Full::calcPosition(double angle, int angleVotes, double scale, int scaleVotes) + void GeneralizedHoughGuilImpl::calcPosition(double angle, int angleVotes, double scale, int scaleVotes) { using namespace cv::gpu::cudev::ght; - hist.setTo(Scalar::all(0)); - Guil_Full_calcPHist_gpu(templFeatures.sizes.ptr(), imageFeatures.sizes.ptr(0), hist, - (float)angle, (float)angleEpsilon, (float)scale, (float)dp, levels, templFeatures.maxSize); + hist_.setTo(Scalar::all(0)); + Guil_Full_calcPHist_gpu(templFeatures_.sizes.ptr(), imageFeatures_.sizes.ptr(0), hist_, + (float)angle, (float)angleEpsilon_, (float)scale, (float)dp_, levels_, templFeatures_.maxSize); - posCount = Guil_Full_findPosInHist_gpu(hist, outBuf.ptr(0), outBuf.ptr(1), - posCount, maxSize, (float)angle, angleVotes, - (float)scale, scaleVotes, (float)dp, posThresh); + posCount_ = Guil_Full_findPosInHist_gpu(hist_, outBuf_.ptr(0), outBuf_.ptr(1), + posCount_, maxBufferSize_, (float)angle, angleVotes, + (float)scale, scaleVotes, (float)dp_, posThresh_); } } -Ptr cv::gpu::GeneralizedHough::create(int method) +Ptr cv::gpu::createGeneralizedHoughGuil() { - switch (method) - { - case cv::GeneralizedHough::GHT_POSITION: - CV_Assert( !GHT_Ballard_Pos_info_auto.name().empty() ); - return new GHT_Ballard_Pos(); - - case (cv::GeneralizedHough::GHT_POSITION | cv::GeneralizedHough::GHT_SCALE): - CV_Assert( !GHT_Ballard_PosScale_info_auto.name().empty() ); - return new GHT_Ballard_PosScale(); - - case (cv::GeneralizedHough::GHT_POSITION | cv::GeneralizedHough::GHT_ROTATION): - CV_Assert( !GHT_Ballard_PosRotation_info_auto.name().empty() ); - return new GHT_Ballard_PosRotation(); - - case (cv::GeneralizedHough::GHT_POSITION | cv::GeneralizedHough::GHT_SCALE | cv::GeneralizedHough::GHT_ROTATION): - CV_Assert( !GHT_Guil_Full_info_auto.name().empty() ); - return new GHT_Guil_Full(); - - default: - CV_Error(Error::StsBadArg, "Unsupported method"); - return Ptr(); - } + return new GeneralizedHoughGuilImpl; } #endif /* !defined (HAVE_CUDA) */ diff --git a/modules/gpuimgproc/test/test_hough.cpp b/modules/gpuimgproc/test/test_hough.cpp index e4319bd219..969899d8b6 100644 --- a/modules/gpuimgproc/test/test_hough.cpp +++ b/modules/gpuimgproc/test/test_hough.cpp @@ -193,7 +193,7 @@ PARAM_TEST_CASE(GeneralizedHough, cv::gpu::DeviceInfo, UseRoi) { }; -GPU_TEST_P(GeneralizedHough, POSITION) +GPU_TEST_P(GeneralizedHough, Ballard) { const cv::gpu::DeviceInfo devInfo = GET_PARAM(0); cv::gpu::setDevice(devInfo.deviceID()); @@ -218,16 +218,16 @@ GPU_TEST_P(GeneralizedHough, POSITION) templ.copyTo(imageROI); } - cv::Ptr hough = cv::gpu::GeneralizedHough::create(cv::GeneralizedHough::GHT_POSITION); - hough->set("votesThreshold", 200); + cv::Ptr alg = cv::gpu::createGeneralizedHoughBallard(); + alg->setVotesThreshold(200); - hough->setTemplate(loadMat(templ, useRoi)); + alg->setTemplate(loadMat(templ, useRoi)); cv::gpu::GpuMat d_pos; - hough->detect(loadMat(image, useRoi), d_pos); + alg->detect(loadMat(image, useRoi), d_pos); std::vector pos; - hough->downloadResults(d_pos, pos); + d_pos.download(pos); ASSERT_EQ(gold_count, pos.size()); diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 6d61088724..d28bea4f65 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -688,39 +688,104 @@ public: //! finds arbitrary template in the grayscale image using Generalized Hough Transform +class CV_EXPORTS GeneralizedHough : public Algorithm +{ +public: + //! set template to search + virtual void setTemplate(InputArray templ, Point templCenter = Point(-1, -1)) = 0; + virtual void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter = Point(-1, -1)) = 0; + + //! find template on image + virtual void detect(InputArray image, OutputArray positions, OutputArray votes = noArray()) = 0; + virtual void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes = noArray()) = 0; + + //! Canny low threshold. + virtual void setCannyLowThresh(int cannyLowThresh) = 0; + virtual int getCannyLowThresh() const = 0; + + //! Canny high threshold. + virtual void setCannyHighThresh(int cannyHighThresh) = 0; + virtual int getCannyHighThresh() const = 0; + + //! Minimum distance between the centers of the detected objects. + virtual void setMinDist(double minDist) = 0; + virtual double getMinDist() const = 0; + + //! Inverse ratio of the accumulator resolution to the image resolution. + virtual void setDp(double dp) = 0; + virtual double getDp() const = 0; + + //! Maximal size of inner buffers. + virtual void setMaxBufferSize(int maxBufferSize) = 0; + virtual int getMaxBufferSize() const = 0; +}; + //! Ballard, D.H. (1981). Generalizing the Hough transform to detect arbitrary shapes. Pattern Recognition 13 (2): 111-122. +//! Detects position only without traslation and rotation +class CV_EXPORTS GeneralizedHoughBallard : public GeneralizedHough +{ +public: + //! R-Table levels. + virtual void setLevels(int levels) = 0; + virtual int getLevels() const = 0; + + //! The accumulator threshold for the template centers at the detection stage. The smaller it is, the more false positions may be detected. + virtual void setVotesThreshold(int votesThreshold) = 0; + virtual int getVotesThreshold() const = 0; +}; + //! Guil, N., González-Linares, J.M. and Zapata, E.L. (1999). Bidimensional shape detection using an invariant approach. Pattern Recognition 32 (6): 1025-1038. -class CV_EXPORTS GeneralizedHough : public Algorithm +//! Detects position, traslation and rotation +class CV_EXPORTS GeneralizedHoughGuil : public GeneralizedHough { public: - enum { GHT_POSITION = 0, - GHT_SCALE = 1, - GHT_ROTATION = 2 - }; + //! Angle difference in degrees between two points in feature. + virtual void setXi(double xi) = 0; + virtual double getXi() const = 0; - static Ptr create(int method); + //! Feature table levels. + virtual void setLevels(int levels) = 0; + virtual int getLevels() const = 0; - virtual ~GeneralizedHough(); + //! Maximal difference between angles that treated as equal. + virtual void setAngleEpsilon(double angleEpsilon) = 0; + virtual double getAngleEpsilon() const = 0; - //! set template to search - void setTemplate(InputArray templ, int cannyThreshold = 100, Point templCenter = Point(-1, -1)); - void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter = Point(-1, -1)); + //! Minimal rotation angle to detect in degrees. + virtual void setMinAngle(double minAngle) = 0; + virtual double getMinAngle() const = 0; - //! find template on image - void detect(InputArray image, OutputArray positions, OutputArray votes = cv::noArray(), int cannyThreshold = 100); - void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes = cv::noArray()); + //! Maximal rotation angle to detect in degrees. + virtual void setMaxAngle(double maxAngle) = 0; + virtual double getMaxAngle() const = 0; - void release(); + //! Angle step in degrees. + virtual void setAngleStep(double angleStep) = 0; + virtual double getAngleStep() const = 0; -protected: - virtual void setTemplateImpl(const Mat& edges, const Mat& dx, const Mat& dy, Point templCenter) = 0; - virtual void detectImpl(const Mat& edges, const Mat& dx, const Mat& dy, OutputArray positions, OutputArray votes) = 0; - virtual void releaseImpl() = 0; - -private: - Mat edges_; - Mat dx_; - Mat dy_; + //! Angle votes threshold. + virtual void setAngleThresh(int angleThresh) = 0; + virtual int getAngleThresh() const = 0; + + //! Minimal scale to detect. + virtual void setMinScale(double minScale) = 0; + virtual double getMinScale() const = 0; + + //! Maximal scale to detect. + virtual void setMaxScale(double maxScale) = 0; + virtual double getMaxScale() const = 0; + + //! Scale step. + virtual void setScaleStep(double scaleStep) = 0; + virtual double getScaleStep() const = 0; + + //! Scale votes threshold. + virtual void setScaleThresh(int scaleThresh) = 0; + virtual int getScaleThresh() const = 0; + + //! Position votes threshold. + virtual void setPosThresh(int posThresh) = 0; + virtual int getPosThresh() const = 0; }; @@ -1355,6 +1420,14 @@ CV_EXPORTS_W double pointPolygonTest( InputArray contour, Point2f pt, bool measu CV_EXPORTS Ptr createCLAHE(double clipLimit = 40.0, Size tileGridSize = Size(8, 8)); +//! Ballard, D.H. (1981). Generalizing the Hough transform to detect arbitrary shapes. Pattern Recognition 13 (2): 111-122. +//! Detects position only without traslation and rotation +CV_EXPORTS Ptr createGeneralizedHoughBallard(); + +//! Guil, N., González-Linares, J.M. and Zapata, E.L. (1999). Bidimensional shape detection using an invariant approach. Pattern Recognition 32 (6): 1025-1038. +//! Detects position, traslation and rotation +CV_EXPORTS Ptr createGeneralizedHoughGuil(); + } // cv #endif diff --git a/modules/imgproc/src/generalized_hough.cpp b/modules/imgproc/src/generalized_hough.cpp index 8eadff200b..7ee3b700da 100644 --- a/modules/imgproc/src/generalized_hough.cpp +++ b/modules/imgproc/src/generalized_hough.cpp @@ -45,17 +45,10 @@ using namespace cv; +// common + namespace { - ///////////////////////////////////// - // Common - - template void releaseVector(std::vector& v) - { - std::vector empty; - empty.swap(v); - } - double toRad(double a) { return a * CV_PI / 180.0; @@ -66,70 +59,112 @@ namespace return fabs(v) > std::numeric_limits::epsilon(); } - class GHT_Pos : public GeneralizedHough + class GeneralizedHoughBase { - public: - GHT_Pos(); - protected: - void setTemplateImpl(const Mat& edges, const Mat& dx, const Mat& dy, Point templCenter); - void detectImpl(const Mat& edges, const Mat& dx, const Mat& dy, OutputArray positions, OutputArray votes); - void releaseImpl(); + GeneralizedHoughBase(); + virtual ~GeneralizedHoughBase() {} + + void setTemplateImpl(InputArray templ, Point templCenter); + void setTemplateImpl(InputArray edges, InputArray dx, InputArray dy, Point templCenter); + + void detectImpl(InputArray image, OutputArray positions, OutputArray votes); + void detectImpl(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes); virtual void processTempl() = 0; virtual void processImage() = 0; + int cannyLowThresh_; + int cannyHighThresh_; + double minDist_; + double dp_; + + Size templSize_; + Point templCenter_; + Mat templEdges_; + Mat templDx_; + Mat templDy_; + + Size imageSize_; + Mat imageEdges_; + Mat imageDx_; + Mat imageDy_; + + std::vector posOutBuf_; + std::vector voteOutBuf_; + + private: + void calcEdges(InputArray src, Mat& edges, Mat& dx, Mat& dy); void filterMinDist(); void convertTo(OutputArray positions, OutputArray votes); + }; - double minDist; + GeneralizedHoughBase::GeneralizedHoughBase() + { + cannyLowThresh_ = 50; + cannyHighThresh_ = 100; + minDist_ = 1.0; + dp_ = 1.0; + } - Size templSize; - Point templCenter; - Mat templEdges; - Mat templDx; - Mat templDy; + void GeneralizedHoughBase::calcEdges(InputArray _src, Mat& edges, Mat& dx, Mat& dy) + { + Mat src = _src.getMat(); - Size imageSize; - Mat imageEdges; - Mat imageDx; - Mat imageDy; + CV_Assert( src.type() == CV_8UC1 ); + CV_Assert( cannyLowThresh_ > 0 && cannyLowThresh_ < cannyHighThresh_ ); - std::vector posOutBuf; - std::vector voteOutBuf; - }; + Canny(src, edges, cannyLowThresh_, cannyHighThresh_); + Sobel(src, dx, CV_32F, 1, 0); + Sobel(src, dy, CV_32F, 0, 1); + } - GHT_Pos::GHT_Pos() + void GeneralizedHoughBase::setTemplateImpl(InputArray templ, Point templCenter) { - minDist = 1.0; + calcEdges(templ, templEdges_, templDx_, templDy_); + + if (templCenter == Point(-1, -1)) + templCenter = Point(templEdges_.cols / 2, templEdges_.rows / 2); + + templSize_ = templEdges_.size(); + templCenter_ = templCenter; + + processTempl(); } - void GHT_Pos::setTemplateImpl(const Mat& edges, const Mat& dx, const Mat& dy, Point templCenter_) + void GeneralizedHoughBase::setTemplateImpl(InputArray edges, InputArray dx, InputArray dy, Point templCenter) { - templSize = edges.size(); - templCenter = templCenter_; - edges.copyTo(templEdges); - dx.copyTo(templDx); - dy.copyTo(templDy); + edges.getMat().copyTo(templEdges_); + dx.getMat().copyTo(templDx_); + dy.getMat().copyTo(templDy_); + + CV_Assert( templEdges_.type() == CV_8UC1 ); + CV_Assert( templDx_.type() == CV_32FC1 && templDx_.size() == templEdges_.size() ); + CV_Assert( templDy_.type() == templDx_.type() && templDy_.size() == templEdges_.size() ); + + if (templCenter == Point(-1, -1)) + templCenter = Point(templEdges_.cols / 2, templEdges_.rows / 2); + + templSize_ = templEdges_.size(); + templCenter_ = templCenter; processTempl(); } - void GHT_Pos::detectImpl(const Mat& edges, const Mat& dx, const Mat& dy, OutputArray positions, OutputArray votes) + void GeneralizedHoughBase::detectImpl(InputArray image, OutputArray positions, OutputArray votes) { - imageSize = edges.size(); - edges.copyTo(imageEdges); - dx.copyTo(imageDx); - dy.copyTo(imageDy); + calcEdges(image, imageEdges_, imageDx_, imageDy_); + + imageSize_ = imageEdges_.size(); - posOutBuf.clear(); - voteOutBuf.clear(); + posOutBuf_.clear(); + voteOutBuf_.clear(); processImage(); - if (!posOutBuf.empty()) + if (!posOutBuf_.empty()) { - if (minDist > 1) + if (minDist_ > 1) filterMinDist(); convertTo(positions, votes); } @@ -141,21 +176,35 @@ namespace } } - void GHT_Pos::releaseImpl() + void GeneralizedHoughBase::detectImpl(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes) { - templSize = Size(); - templCenter = Point(-1, -1); - templEdges.release(); - templDx.release(); - templDy.release(); - - imageSize = Size(); - imageEdges.release(); - imageDx.release(); - imageDy.release(); - - releaseVector(posOutBuf); - releaseVector(voteOutBuf); + edges.getMat().copyTo(imageEdges_); + dx.getMat().copyTo(imageDx_); + dy.getMat().copyTo(imageDy_); + + CV_Assert( imageEdges_.type() == CV_8UC1 ); + CV_Assert( imageDx_.type() == CV_32FC1 && imageDx_.size() == imageEdges_.size() ); + CV_Assert( imageDy_.type() == imageDx_.type() && imageDy_.size() == imageEdges_.size() ); + + imageSize_ = imageEdges_.size(); + + posOutBuf_.clear(); + voteOutBuf_.clear(); + + processImage(); + + if (!posOutBuf_.empty()) + { + if (minDist_ > 1) + filterMinDist(); + convertTo(positions, votes); + } + else + { + positions.release(); + if (votes.needed()) + votes.release(); + } } class Vec3iGreaterThanIdx @@ -166,31 +215,31 @@ namespace const Vec3i* arr; }; - void GHT_Pos::filterMinDist() + void GeneralizedHoughBase::filterMinDist() { - size_t oldSize = posOutBuf.size(); - const bool hasVotes = !voteOutBuf.empty(); + size_t oldSize = posOutBuf_.size(); + const bool hasVotes = !voteOutBuf_.empty(); - CV_Assert(!hasVotes || voteOutBuf.size() == oldSize); + CV_Assert( !hasVotes || voteOutBuf_.size() == oldSize ); - std::vector oldPosBuf(posOutBuf); - std::vector oldVoteBuf(voteOutBuf); + std::vector oldPosBuf(posOutBuf_); + std::vector oldVoteBuf(voteOutBuf_); std::vector indexies(oldSize); for (size_t i = 0; i < oldSize; ++i) indexies[i] = i; std::sort(indexies.begin(), indexies.end(), Vec3iGreaterThanIdx(&oldVoteBuf[0])); - posOutBuf.clear(); - voteOutBuf.clear(); + posOutBuf_.clear(); + voteOutBuf_.clear(); - const int cellSize = cvRound(minDist); - const int gridWidth = (imageSize.width + cellSize - 1) / cellSize; - const int gridHeight = (imageSize.height + cellSize - 1) / cellSize; + const int cellSize = cvRound(minDist_); + const int gridWidth = (imageSize_.width + cellSize - 1) / cellSize; + const int gridHeight = (imageSize_.height + cellSize - 1) / cellSize; std::vector< std::vector > grid(gridWidth * gridHeight); - const double minDist2 = minDist * minDist; + const double minDist2 = minDist_ * minDist_; for (size_t i = 0; i < oldSize; ++i) { @@ -239,108 +288,112 @@ namespace { grid[yCell * gridWidth + xCell].push_back(p); - posOutBuf.push_back(oldPosBuf[ind]); + posOutBuf_.push_back(oldPosBuf[ind]); if (hasVotes) - voteOutBuf.push_back(oldVoteBuf[ind]); + voteOutBuf_.push_back(oldVoteBuf[ind]); } } } - void GHT_Pos::convertTo(OutputArray _positions, OutputArray _votes) + void GeneralizedHoughBase::convertTo(OutputArray _positions, OutputArray _votes) { - const int total = static_cast(posOutBuf.size()); - const bool hasVotes = !voteOutBuf.empty(); + const int total = static_cast(posOutBuf_.size()); + const bool hasVotes = !voteOutBuf_.empty(); - CV_Assert(!hasVotes || voteOutBuf.size() == posOutBuf.size()); + CV_Assert( !hasVotes || voteOutBuf_.size() == posOutBuf_.size() ); _positions.create(1, total, CV_32FC4); Mat positions = _positions.getMat(); - Mat(1, total, CV_32FC4, &posOutBuf[0]).copyTo(positions); + Mat(1, total, CV_32FC4, &posOutBuf_[0]).copyTo(positions); if (_votes.needed()) { if (!hasVotes) + { _votes.release(); + } else { _votes.create(1, total, CV_32SC3); Mat votes = _votes.getMat(); - Mat(1, total, CV_32SC3, &voteOutBuf[0]).copyTo(votes); + Mat(1, total, CV_32SC3, &voteOutBuf_[0]).copyTo(votes); } } } +} - ///////////////////////////////////// - // POSITION Ballard +// GeneralizedHoughBallard - class GHT_Ballard_Pos : public GHT_Pos +namespace +{ + class GeneralizedHoughBallardImpl : public GeneralizedHoughBallard, private GeneralizedHoughBase { public: - AlgorithmInfo* info() const; + GeneralizedHoughBallardImpl(); - GHT_Ballard_Pos(); + void setTemplate(InputArray templ, Point templCenter) { setTemplateImpl(templ, templCenter); } + void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter) { setTemplateImpl(edges, dx, dy, templCenter); } - protected: - void releaseImpl(); + void detect(InputArray image, OutputArray positions, OutputArray votes) { detectImpl(image, positions, votes); } + void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes) { detectImpl(edges, dx, dy, positions, votes); } + + void setCannyLowThresh(int cannyLowThresh) { cannyLowThresh_ = cannyLowThresh; } + int getCannyLowThresh() const { return cannyLowThresh_; } + + void setCannyHighThresh(int cannyHighThresh) { cannyHighThresh_ = cannyHighThresh; } + int getCannyHighThresh() const { return cannyHighThresh_; } + + void setMinDist(double minDist) { minDist_ = minDist; } + double getMinDist() const { return minDist_; } + + void setDp(double dp) { dp_ = dp; } + double getDp() const { return dp_; } + + void setMaxBufferSize(int) { } + int getMaxBufferSize() const { return 0; } + void setLevels(int levels) { levels_ = levels; } + int getLevels() const { return levels_; } + + void setVotesThreshold(int votesThreshold) { votesThreshold_ = votesThreshold; } + int getVotesThreshold() const { return votesThreshold_; } + + private: void processTempl(); void processImage(); - virtual void calcHist(); - virtual void findPosInHist(); + void calcHist(); + void findPosInHist(); - int levels; - int votesThreshold; - double dp; + int levels_; + int votesThreshold_; - std::vector< std::vector > r_table; - Mat hist; + std::vector< std::vector > r_table_; + Mat hist_; }; - CV_INIT_ALGORITHM(GHT_Ballard_Pos, "GeneralizedHough.POSITION", - obj.info()->addParam(obj, "minDist", obj.minDist, false, 0, 0, - "Minimum distance between the centers of the detected objects."); - obj.info()->addParam(obj, "levels", obj.levels, false, 0, 0, - "R-Table levels."); - obj.info()->addParam(obj, "votesThreshold", obj.votesThreshold, false, 0, 0, - "The accumulator threshold for the template centers at the detection stage. The smaller it is, the more false positions may be detected."); - obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, - "Inverse ratio of the accumulator resolution to the image resolution.")); - - GHT_Ballard_Pos::GHT_Ballard_Pos() + GeneralizedHoughBallardImpl::GeneralizedHoughBallardImpl() { - levels = 360; - votesThreshold = 100; - dp = 1.0; + levels_ = 360; + votesThreshold_ = 100; } - void GHT_Ballard_Pos::releaseImpl() + void GeneralizedHoughBallardImpl::processTempl() { - GHT_Pos::releaseImpl(); + CV_Assert( levels_ > 0 ); - releaseVector(r_table); - hist.release(); - } + const double thetaScale = levels_ / 360.0; - void GHT_Ballard_Pos::processTempl() - { - CV_Assert(templEdges.type() == CV_8UC1); - CV_Assert(templDx.type() == CV_32FC1 && templDx.size() == templSize); - CV_Assert(templDy.type() == templDx.type() && templDy.size() == templSize); - CV_Assert(levels > 0); + r_table_.resize(levels_ + 1); + std::for_each(r_table_.begin(), r_table_.end(), std::mem_fun_ref(&std::vector::clear)); - const double thetaScale = levels / 360.0; - - r_table.resize(levels + 1); - for_each(r_table.begin(), r_table.end(), mem_fun_ref(&std::vector::clear)); - - for (int y = 0; y < templSize.height; ++y) + for (int y = 0; y < templSize_.height; ++y) { - const uchar* edgesRow = templEdges.ptr(y); - const float* dxRow = templDx.ptr(y); - const float* dyRow = templDy.ptr(y); + const uchar* edgesRow = templEdges_.ptr(y); + const float* dxRow = templDx_.ptr(y); + const float* dyRow = templDy_.ptr(y); - for (int x = 0; x < templSize.width; ++x) + for (int x = 0; x < templSize_.width; ++x) { const Point p(x, y); @@ -348,42 +401,42 @@ namespace { const float theta = fastAtan2(dyRow[x], dxRow[x]); const int n = cvRound(theta * thetaScale); - r_table[n].push_back(p - templCenter); + r_table_[n].push_back(p - templCenter_); } } } } - void GHT_Ballard_Pos::processImage() + void GeneralizedHoughBallardImpl::processImage() { calcHist(); findPosInHist(); } - void GHT_Ballard_Pos::calcHist() + void GeneralizedHoughBallardImpl::calcHist() { - CV_Assert(imageEdges.type() == CV_8UC1); - CV_Assert(imageDx.type() == CV_32FC1 && imageDx.size() == imageSize); - CV_Assert(imageDy.type() == imageDx.type() && imageDy.size() == imageSize); - CV_Assert(levels > 0 && r_table.size() == static_cast(levels + 1)); - CV_Assert(dp > 0.0); + CV_Assert( imageEdges_.type() == CV_8UC1 ); + CV_Assert( imageDx_.type() == CV_32FC1 && imageDx_.size() == imageSize_); + CV_Assert( imageDy_.type() == imageDx_.type() && imageDy_.size() == imageSize_); + CV_Assert( levels_ > 0 && r_table_.size() == static_cast(levels_ + 1) ); + CV_Assert( dp_ > 0.0 ); - const double thetaScale = levels / 360.0; - const double idp = 1.0 / dp; + const double thetaScale = levels_ / 360.0; + const double idp = 1.0 / dp_; - hist.create(cvCeil(imageSize.height * idp) + 2, cvCeil(imageSize.width * idp) + 2, CV_32SC1); - hist.setTo(0); + hist_.create(cvCeil(imageSize_.height * idp) + 2, cvCeil(imageSize_.width * idp) + 2, CV_32SC1); + hist_.setTo(0); - const int rows = hist.rows - 2; - const int cols = hist.cols - 2; + const int rows = hist_.rows - 2; + const int cols = hist_.cols - 2; - for (int y = 0; y < imageSize.height; ++y) + for (int y = 0; y < imageSize_.height; ++y) { - const uchar* edgesRow = imageEdges.ptr(y); - const float* dxRow = imageDx.ptr(y); - const float* dyRow = imageDy.ptr(y); + const uchar* edgesRow = imageEdges_.ptr(y); + const float* dxRow = imageDx_.ptr(y); + const float* dyRow = imageDy_.ptr(y); - for (int x = 0; x < imageSize.width; ++x) + for (int x = 0; x < imageSize_.width; ++x) { const Point p(x, y); @@ -392,7 +445,7 @@ namespace const float theta = fastAtan2(dyRow[x], dxRow[x]); const int n = cvRound(theta * thetaScale); - const std::vector& r_row = r_table[n]; + const std::vector& r_row = r_table_[n]; for (size_t j = 0; j < r_row.size(); ++j) { @@ -402,406 +455,131 @@ namespace c.y = cvRound(c.y * idp); if (c.x >= 0 && c.x < cols && c.y >= 0 && c.y < rows) - ++hist.at(c.y + 1, c.x + 1); + ++hist_.at(c.y + 1, c.x + 1); } } } } } - void GHT_Ballard_Pos::findPosInHist() + void GeneralizedHoughBallardImpl::findPosInHist() { - CV_Assert(votesThreshold > 0); + CV_Assert( votesThreshold_ > 0 ); - const int histRows = hist.rows - 2; - const int histCols = hist.cols - 2; + const int histRows = hist_.rows - 2; + const int histCols = hist_.cols - 2; for(int y = 0; y < histRows; ++y) { - const int* prevRow = hist.ptr(y); - const int* curRow = hist.ptr(y + 1); - const int* nextRow = hist.ptr(y + 2); + const int* prevRow = hist_.ptr(y); + const int* curRow = hist_.ptr(y + 1); + const int* nextRow = hist_.ptr(y + 2); for(int x = 0; x < histCols; ++x) { const int votes = curRow[x + 1]; - if (votes > votesThreshold && votes > curRow[x] && votes >= curRow[x + 2] && votes > prevRow[x + 1] && votes >= nextRow[x + 1]) + if (votes > votesThreshold_ && votes > curRow[x] && votes >= curRow[x + 2] && votes > prevRow[x + 1] && votes >= nextRow[x + 1]) { - posOutBuf.push_back(Vec4f(static_cast(x * dp), static_cast(y * dp), 1.0f, 0.0f)); - voteOutBuf.push_back(Vec3i(votes, 0, 0)); + posOutBuf_.push_back(Vec4f(static_cast(x * dp_), static_cast(y * dp_), 1.0f, 0.0f)); + voteOutBuf_.push_back(Vec3i(votes, 0, 0)); } } } } +} - ///////////////////////////////////// - // POSITION & SCALE - - class GHT_Ballard_PosScale : public GHT_Ballard_Pos - { - public: - AlgorithmInfo* info() const; - - GHT_Ballard_PosScale(); - - protected: - void calcHist(); - void findPosInHist(); - - double minScale; - double maxScale; - double scaleStep; +Ptr cv::createGeneralizedHoughBallard() +{ + return new GeneralizedHoughBallardImpl; +} - class Worker; - friend class Worker; - }; +// GeneralizedHoughGuil - CV_INIT_ALGORITHM(GHT_Ballard_PosScale, "GeneralizedHough.POSITION_SCALE", - obj.info()->addParam(obj, "minDist", obj.minDist, false, 0, 0, - "Minimum distance between the centers of the detected objects."); - obj.info()->addParam(obj, "levels", obj.levels, false, 0, 0, - "R-Table levels."); - obj.info()->addParam(obj, "votesThreshold", obj.votesThreshold, false, 0, 0, - "The accumulator threshold for the template centers at the detection stage. The smaller it is, the more false positions may be detected."); - obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, - "Inverse ratio of the accumulator resolution to the image resolution."); - obj.info()->addParam(obj, "minScale", obj.minScale, false, 0, 0, - "Minimal scale to detect."); - obj.info()->addParam(obj, "maxScale", obj.maxScale, false, 0, 0, - "Maximal scale to detect."); - obj.info()->addParam(obj, "scaleStep", obj.scaleStep, false, 0, 0, - "Scale step.")); - - GHT_Ballard_PosScale::GHT_Ballard_PosScale() - { - minScale = 0.5; - maxScale = 2.0; - scaleStep = 0.05; - } - - class GHT_Ballard_PosScale::Worker : public ParallelLoopBody +namespace +{ + class GeneralizedHoughGuilImpl : public GeneralizedHoughGuil, private GeneralizedHoughBase { public: - explicit Worker(GHT_Ballard_PosScale* base_) : base(base_) {} + GeneralizedHoughGuilImpl(); - void operator ()(const Range& range) const; + void setTemplate(InputArray templ, Point templCenter) { setTemplateImpl(templ, templCenter); } + void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter) { setTemplateImpl(edges, dx, dy, templCenter); } - private: - GHT_Ballard_PosScale* base; - }; + void detect(InputArray image, OutputArray positions, OutputArray votes) { detectImpl(image, positions, votes); } + void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes) { detectImpl(edges, dx, dy, positions, votes); } - void GHT_Ballard_PosScale::Worker::operator ()(const Range& range) const - { - const double thetaScale = base->levels / 360.0; - const double idp = 1.0 / base->dp; + void setCannyLowThresh(int cannyLowThresh) { cannyLowThresh_ = cannyLowThresh; } + int getCannyLowThresh() const { return cannyLowThresh_; } - for (int s = range.start; s < range.end; ++s) - { - const double scale = base->minScale + s * base->scaleStep; + void setCannyHighThresh(int cannyHighThresh) { cannyHighThresh_ = cannyHighThresh; } + int getCannyHighThresh() const { return cannyHighThresh_; } - Mat curHist(base->hist.size[1], base->hist.size[2], CV_32SC1, base->hist.ptr(s + 1), base->hist.step[1]); + void setMinDist(double minDist) { minDist_ = minDist; } + double getMinDist() const { return minDist_; } - for (int y = 0; y < base->imageSize.height; ++y) - { - const uchar* edgesRow = base->imageEdges.ptr(y); - const float* dxRow = base->imageDx.ptr(y); - const float* dyRow = base->imageDy.ptr(y); + void setDp(double dp) { dp_ = dp; } + double getDp() const { return dp_; } - for (int x = 0; x < base->imageSize.width; ++x) - { - const Point2d p(x, y); + void setMaxBufferSize(int maxBufferSize) { maxBufferSize_ = maxBufferSize; } + int getMaxBufferSize() const { return maxBufferSize_; } - if (edgesRow[x] && (notNull(dyRow[x]) || notNull(dxRow[x]))) - { - const float theta = fastAtan2(dyRow[x], dxRow[x]); - const int n = cvRound(theta * thetaScale); + void setXi(double xi) { xi_ = xi; } + double getXi() const { return xi_; } - const std::vector& r_row = base->r_table[n]; + void setLevels(int levels) { levels_ = levels; } + int getLevels() const { return levels_; } - for (size_t j = 0; j < r_row.size(); ++j) - { - Point2d d = r_row[j]; - Point2d c = p - d * scale; + void setAngleEpsilon(double angleEpsilon) { angleEpsilon_ = angleEpsilon; } + double getAngleEpsilon() const { return angleEpsilon_; } - c.x *= idp; - c.y *= idp; + void setMinAngle(double minAngle) { minAngle_ = minAngle; } + double getMinAngle() const { return minAngle_; } - if (c.x >= 0 && c.x < base->hist.size[2] - 2 && c.y >= 0 && c.y < base->hist.size[1] - 2) - ++curHist.at(cvRound(c.y + 1), cvRound(c.x + 1)); - } - } - } - } - } - } - - void GHT_Ballard_PosScale::calcHist() - { - CV_Assert(imageEdges.type() == CV_8UC1); - CV_Assert(imageDx.type() == CV_32FC1 && imageDx.size() == imageSize); - CV_Assert(imageDy.type() == imageDx.type() && imageDy.size() == imageSize); - CV_Assert(levels > 0 && r_table.size() == static_cast(levels + 1)); - CV_Assert(dp > 0.0); - CV_Assert(minScale > 0.0 && minScale < maxScale); - CV_Assert(scaleStep > 0.0); - - const double idp = 1.0 / dp; - const int scaleRange = cvCeil((maxScale - minScale) / scaleStep); - - const int sizes[] = {scaleRange + 2, cvCeil(imageSize.height * idp) + 2, cvCeil(imageSize.width * idp) + 2}; - hist.create(3, sizes, CV_32SC1); - hist.setTo(0); - - parallel_for_(Range(0, scaleRange), Worker(this)); - } - - void GHT_Ballard_PosScale::findPosInHist() - { - CV_Assert(votesThreshold > 0); - - const int scaleRange = hist.size[0] - 2; - const int histRows = hist.size[1] - 2; - const int histCols = hist.size[2] - 2; - - for (int s = 0; s < scaleRange; ++s) - { - const float scale = static_cast(minScale + s * scaleStep); - - const Mat prevHist(histRows + 2, histCols + 2, CV_32SC1, hist.ptr(s), hist.step[1]); - const Mat curHist(histRows + 2, histCols + 2, CV_32SC1, hist.ptr(s + 1), hist.step[1]); - const Mat nextHist(histRows + 2, histCols + 2, CV_32SC1, hist.ptr(s + 2), hist.step[1]); - - for(int y = 0; y < histRows; ++y) - { - const int* prevHistRow = prevHist.ptr(y + 1); - const int* prevRow = curHist.ptr(y); - const int* curRow = curHist.ptr(y + 1); - const int* nextRow = curHist.ptr(y + 2); - const int* nextHistRow = nextHist.ptr(y + 1); + void setMaxAngle(double maxAngle) { maxAngle_ = maxAngle; } + double getMaxAngle() const { return maxAngle_; } - for(int x = 0; x < histCols; ++x) - { - const int votes = curRow[x + 1]; - - if (votes > votesThreshold && - votes > curRow[x] && - votes >= curRow[x + 2] && - votes > prevRow[x + 1] && - votes >= nextRow[x + 1] && - votes > prevHistRow[x + 1] && - votes >= nextHistRow[x + 1]) - { - posOutBuf.push_back(Vec4f(static_cast(x * dp), static_cast(y * dp), scale, 0.0f)); - voteOutBuf.push_back(Vec3i(votes, votes, 0)); - } - } - } - } - } + void setAngleStep(double angleStep) { angleStep_ = angleStep; } + double getAngleStep() const { return angleStep_; } - ///////////////////////////////////// - // POSITION & ROTATION + void setAngleThresh(int angleThresh) { angleThresh_ = angleThresh; } + int getAngleThresh() const { return angleThresh_; } - class GHT_Ballard_PosRotation : public GHT_Ballard_Pos - { - public: - AlgorithmInfo* info() const; + void setMinScale(double minScale) { minScale_ = minScale; } + double getMinScale() const { return minScale_; } - GHT_Ballard_PosRotation(); + void setMaxScale(double maxScale) { maxScale_ = maxScale; } + double getMaxScale() const { return maxScale_; } - protected: - void calcHist(); - void findPosInHist(); + void setScaleStep(double scaleStep) { scaleStep_ = scaleStep; } + double getScaleStep() const { return scaleStep_; } - double minAngle; - double maxAngle; - double angleStep; + void setScaleThresh(int scaleThresh) { scaleThresh_ = scaleThresh; } + int getScaleThresh() const { return scaleThresh_; } - class Worker; - friend class Worker; - }; - - CV_INIT_ALGORITHM(GHT_Ballard_PosRotation, "GeneralizedHough.POSITION_ROTATION", - obj.info()->addParam(obj, "minDist", obj.minDist, false, 0, 0, - "Minimum distance between the centers of the detected objects."); - obj.info()->addParam(obj, "levels", obj.levels, false, 0, 0, - "R-Table levels."); - obj.info()->addParam(obj, "votesThreshold", obj.votesThreshold, false, 0, 0, - "The accumulator threshold for the template centers at the detection stage. The smaller it is, the more false positions may be detected."); - obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, - "Inverse ratio of the accumulator resolution to the image resolution."); - obj.info()->addParam(obj, "minAngle", obj.minAngle, false, 0, 0, - "Minimal rotation angle to detect in degrees."); - obj.info()->addParam(obj, "maxAngle", obj.maxAngle, false, 0, 0, - "Maximal rotation angle to detect in degrees."); - obj.info()->addParam(obj, "angleStep", obj.angleStep, false, 0, 0, - "Angle step in degrees.")); - - GHT_Ballard_PosRotation::GHT_Ballard_PosRotation() - { - minAngle = 0.0; - maxAngle = 360.0; - angleStep = 1.0; - } - - class GHT_Ballard_PosRotation::Worker : public ParallelLoopBody - { - public: - explicit Worker(GHT_Ballard_PosRotation* base_) : base(base_) {} - - void operator ()(const Range& range) const; + void setPosThresh(int posThresh) { posThresh_ = posThresh; } + int getPosThresh() const { return posThresh_; } private: - GHT_Ballard_PosRotation* base; - }; - - void GHT_Ballard_PosRotation::Worker::operator ()(const Range& range) const - { - const double thetaScale = base->levels / 360.0; - const double idp = 1.0 / base->dp; - - for (int a = range.start; a < range.end; ++a) - { - const double angle = base->minAngle + a * base->angleStep; - - const double sinA = ::sin(toRad(angle)); - const double cosA = ::cos(toRad(angle)); - - Mat curHist(base->hist.size[1], base->hist.size[2], CV_32SC1, base->hist.ptr(a + 1), base->hist.step[1]); - - for (int y = 0; y < base->imageSize.height; ++y) - { - const uchar* edgesRow = base->imageEdges.ptr(y); - const float* dxRow = base->imageDx.ptr(y); - const float* dyRow = base->imageDy.ptr(y); - - for (int x = 0; x < base->imageSize.width; ++x) - { - const Point2d p(x, y); - - if (edgesRow[x] && (notNull(dyRow[x]) || notNull(dxRow[x]))) - { - double theta = fastAtan2(dyRow[x], dxRow[x]) - angle; - if (theta < 0) - theta += 360.0; - const int n = cvRound(theta * thetaScale); - - const std::vector& r_row = base->r_table[n]; - - for (size_t j = 0; j < r_row.size(); ++j) - { - Point2d d = r_row[j]; - Point2d c = p - Point2d(d.x * cosA - d.y * sinA, d.x * sinA + d.y * cosA); - - c.x *= idp; - c.y *= idp; - - if (c.x >= 0 && c.x < base->hist.size[2] - 2 && c.y >= 0 && c.y < base->hist.size[1] - 2) - ++curHist.at(cvRound(c.y + 1), cvRound(c.x + 1)); - } - } - } - } - } - } - - void GHT_Ballard_PosRotation::calcHist() - { - CV_Assert(imageEdges.type() == CV_8UC1); - CV_Assert(imageDx.type() == CV_32FC1 && imageDx.size() == imageSize); - CV_Assert(imageDy.type() == imageDx.type() && imageDy.size() == imageSize); - CV_Assert(levels > 0 && r_table.size() == static_cast(levels + 1)); - CV_Assert(dp > 0.0); - CV_Assert(minAngle >= 0.0 && minAngle < maxAngle && maxAngle <= 360.0); - CV_Assert(angleStep > 0.0 && angleStep < 360.0); - - const double idp = 1.0 / dp; - const int angleRange = cvCeil((maxAngle - minAngle) / angleStep); - - const int sizes[] = {angleRange + 2, cvCeil(imageSize.height * idp) + 2, cvCeil(imageSize.width * idp) + 2}; - hist.create(3, sizes, CV_32SC1); - hist.setTo(0); - - parallel_for_(Range(0, angleRange), Worker(this)); - } - - void GHT_Ballard_PosRotation::findPosInHist() - { - CV_Assert(votesThreshold > 0); - - const int angleRange = hist.size[0] - 2; - const int histRows = hist.size[1] - 2; - const int histCols = hist.size[2] - 2; - - for (int a = 0; a < angleRange; ++a) - { - const float angle = static_cast(minAngle + a * angleStep); - - const Mat prevHist(histRows + 2, histCols + 2, CV_32SC1, hist.ptr(a), hist.step[1]); - const Mat curHist(histRows + 2, histCols + 2, CV_32SC1, hist.ptr(a + 1), hist.step[1]); - const Mat nextHist(histRows + 2, histCols + 2, CV_32SC1, hist.ptr(a + 2), hist.step[1]); - - for(int y = 0; y < histRows; ++y) - { - const int* prevHistRow = prevHist.ptr(y + 1); - const int* prevRow = curHist.ptr(y); - const int* curRow = curHist.ptr(y + 1); - const int* nextRow = curHist.ptr(y + 2); - const int* nextHistRow = nextHist.ptr(y + 1); - - for(int x = 0; x < histCols; ++x) - { - const int votes = curRow[x + 1]; - - if (votes > votesThreshold && - votes > curRow[x] && - votes >= curRow[x + 2] && - votes > prevRow[x + 1] && - votes >= nextRow[x + 1] && - votes > prevHistRow[x + 1] && - votes >= nextHistRow[x + 1]) - { - posOutBuf.push_back(Vec4f(static_cast(x * dp), static_cast(y * dp), 1.0f, angle)); - voteOutBuf.push_back(Vec3i(votes, 0, votes)); - } - } - } - } - } - - ///////////////////////////////////////// - // POSITION & SCALE & ROTATION - - double clampAngle(double a) - { - double res = a; - - while (res > 360.0) - res -= 360.0; - while (res < 0) - res += 360.0; - - return res; - } - - bool angleEq(double a, double b, double eps = 1.0) - { - return (fabs(clampAngle(a - b)) <= eps); - } + void processTempl(); + void processImage(); - class GHT_Guil_Full : public GHT_Pos - { - public: - AlgorithmInfo* info() const; + int maxBufferSize_; + double xi_; + int levels_; + double angleEpsilon_; - GHT_Guil_Full(); + double minAngle_; + double maxAngle_; + double angleStep_; + int angleThresh_; - protected: - void releaseImpl(); + double minScale_; + double maxScale_; + double scaleStep_; + int scaleThresh_; - void processTempl(); - void processImage(); + int posThresh_; struct ContourPoint { @@ -828,137 +606,92 @@ namespace void calcScale(double angle); void calcPosition(double angle, int angleVotes, double scale, int scaleVotes); - int maxSize; - double xi; - int levels; - double angleEpsilon; + std::vector< std::vector > templFeatures_; + std::vector< std::vector > imageFeatures_; - double minAngle; - double maxAngle; - double angleStep; - int angleThresh; - - double minScale; - double maxScale; - double scaleStep; - int scaleThresh; + std::vector< std::pair > angles_; + std::vector< std::pair > scales_; + }; - double dp; - int posThresh; + double clampAngle(double a) + { + double res = a; - std::vector< std::vector > templFeatures; - std::vector< std::vector > imageFeatures; + while (res > 360.0) + res -= 360.0; + while (res < 0) + res += 360.0; - std::vector< std::pair > angles; - std::vector< std::pair > scales; - }; + return res; + } - CV_INIT_ALGORITHM(GHT_Guil_Full, "GeneralizedHough.POSITION_SCALE_ROTATION", - obj.info()->addParam(obj, "minDist", obj.minDist, false, 0, 0, - "Minimum distance between the centers of the detected objects."); - obj.info()->addParam(obj, "maxSize", obj.maxSize, false, 0, 0, - "Maximal size of inner buffers."); - obj.info()->addParam(obj, "xi", obj.xi, false, 0, 0, - "Angle difference in degrees between two points in feature."); - obj.info()->addParam(obj, "levels", obj.levels, false, 0, 0, - "Feature table levels."); - obj.info()->addParam(obj, "angleEpsilon", obj.angleEpsilon, false, 0, 0, - "Maximal difference between angles that treated as equal."); - obj.info()->addParam(obj, "minAngle", obj.minAngle, false, 0, 0, - "Minimal rotation angle to detect in degrees."); - obj.info()->addParam(obj, "maxAngle", obj.maxAngle, false, 0, 0, - "Maximal rotation angle to detect in degrees."); - obj.info()->addParam(obj, "angleStep", obj.angleStep, false, 0, 0, - "Angle step in degrees."); - obj.info()->addParam(obj, "angleThresh", obj.angleThresh, false, 0, 0, - "Angle threshold."); - obj.info()->addParam(obj, "minScale", obj.minScale, false, 0, 0, - "Minimal scale to detect."); - obj.info()->addParam(obj, "maxScale", obj.maxScale, false, 0, 0, - "Maximal scale to detect."); - obj.info()->addParam(obj, "scaleStep", obj.scaleStep, false, 0, 0, - "Scale step."); - obj.info()->addParam(obj, "scaleThresh", obj.scaleThresh, false, 0, 0, - "Scale threshold."); - obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, - "Inverse ratio of the accumulator resolution to the image resolution."); - obj.info()->addParam(obj, "posThresh", obj.posThresh, false, 0, 0, - "Position threshold.")); - - GHT_Guil_Full::GHT_Guil_Full() + bool angleEq(double a, double b, double eps = 1.0) { - maxSize = 1000; - xi = 90.0; - levels = 360; - angleEpsilon = 1.0; - - minAngle = 0.0; - maxAngle = 360.0; - angleStep = 1.0; - angleThresh = 15000; - - minScale = 0.5; - maxScale = 2.0; - scaleStep = 0.05; - scaleThresh = 1000; - - dp = 1.0; - posThresh = 100; + return (fabs(clampAngle(a - b)) <= eps); } - void GHT_Guil_Full::releaseImpl() + GeneralizedHoughGuilImpl::GeneralizedHoughGuilImpl() { - GHT_Pos::releaseImpl(); + maxBufferSize_ = 1000; + xi_ = 90.0; + levels_ = 360; + angleEpsilon_ = 1.0; - releaseVector(templFeatures); - releaseVector(imageFeatures); + minAngle_ = 0.0; + maxAngle_ = 360.0; + angleStep_ = 1.0; + angleThresh_ = 15000; - releaseVector(angles); - releaseVector(scales); + minScale_ = 0.5; + maxScale_ = 2.0; + scaleStep_ = 0.05; + scaleThresh_ = 1000; + + posThresh_ = 100; } - void GHT_Guil_Full::processTempl() + void GeneralizedHoughGuilImpl::processTempl() { - buildFeatureList(templEdges, templDx, templDy, templFeatures, templCenter); + buildFeatureList(templEdges_, templDx_, templDy_, templFeatures_, templCenter_); } - void GHT_Guil_Full::processImage() + void GeneralizedHoughGuilImpl::processImage() { - buildFeatureList(imageEdges, imageDx, imageDy, imageFeatures); + buildFeatureList(imageEdges_, imageDx_, imageDy_, imageFeatures_); calcOrientation(); - for (size_t i = 0; i < angles.size(); ++i) + for (size_t i = 0; i < angles_.size(); ++i) { - const double angle = angles[i].first; - const int angleVotes = angles[i].second; + const double angle = angles_[i].first; + const int angleVotes = angles_[i].second; calcScale(angle); - for (size_t j = 0; j < scales.size(); ++j) + for (size_t j = 0; j < scales_.size(); ++j) { - const double scale = scales[j].first; - const int scaleVotes = scales[j].second; + const double scale = scales_[j].first; + const int scaleVotes = scales_[j].second; calcPosition(angle, angleVotes, scale, scaleVotes); } } } - void GHT_Guil_Full::buildFeatureList(const Mat& edges, const Mat& dx, const Mat& dy, std::vector< std::vector >& features, Point2d center) + void GeneralizedHoughGuilImpl::buildFeatureList(const Mat& edges, const Mat& dx, const Mat& dy, std::vector< std::vector >& features, Point2d center) { - CV_Assert(levels > 0); + CV_Assert( levels_ > 0 ); - const double maxDist = sqrt((double) templSize.width * templSize.width + templSize.height * templSize.height) * maxScale; + const double maxDist = sqrt((double) templSize_.width * templSize_.width + templSize_.height * templSize_.height) * maxScale_; - const double alphaScale = levels / 360.0; + const double alphaScale = levels_ / 360.0; std::vector points; getContourPoints(edges, dx, dy, points); - features.resize(levels + 1); - for_each(features.begin(), features.end(), mem_fun_ref(&std::vector::clear)); - for_each(features.begin(), features.end(), bind2nd(mem_fun_ref(&std::vector::reserve), maxSize)); + features.resize(levels_ + 1); + std::for_each(features.begin(), features.end(), std::mem_fun_ref(&std::vector::clear)); + std::for_each(features.begin(), features.end(), std::bind2nd(std::mem_fun_ref(&std::vector::reserve), maxBufferSize_)); for (size_t i = 0; i < points.size(); ++i) { @@ -968,7 +701,7 @@ namespace { ContourPoint p2 = points[j]; - if (angleEq(p1.theta - p2.theta, xi, angleEpsilon)) + if (angleEq(p1.theta - p2.theta, xi_, angleEpsilon_)) { const Point2d d = p1.pos - p2.pos; @@ -988,18 +721,18 @@ namespace const int n = cvRound(f.alpha12 * alphaScale); - if (features[n].size() < static_cast(maxSize)) + if (features[n].size() < static_cast(maxBufferSize_)) features[n].push_back(f); } } } } - void GHT_Guil_Full::getContourPoints(const Mat& edges, const Mat& dx, const Mat& dy, std::vector& points) + void GeneralizedHoughGuilImpl::getContourPoints(const Mat& edges, const Mat& dx, const Mat& dy, std::vector& points) { - CV_Assert(edges.type() == CV_8UC1); - CV_Assert(dx.type() == CV_32FC1 && dx.size == edges.size); - CV_Assert(dy.type() == dx.type() && dy.size == edges.size); + CV_Assert( edges.type() == CV_8UC1 ); + CV_Assert( dx.type() == CV_32FC1 && dx.size == edges.size ); + CV_Assert( dy.type() == dx.type() && dy.size == edges.size ); points.clear(); points.reserve(edges.size().area()); @@ -1025,23 +758,23 @@ namespace } } - void GHT_Guil_Full::calcOrientation() + void GeneralizedHoughGuilImpl::calcOrientation() { - CV_Assert(levels > 0); - CV_Assert(templFeatures.size() == static_cast(levels + 1)); - CV_Assert(imageFeatures.size() == templFeatures.size()); - CV_Assert(minAngle >= 0.0 && minAngle < maxAngle && maxAngle <= 360.0); - CV_Assert(angleStep > 0.0 && angleStep < 360.0); - CV_Assert(angleThresh > 0); + CV_Assert( levels_ > 0 ); + CV_Assert( templFeatures_.size() == static_cast(levels_ + 1) ); + CV_Assert( imageFeatures_.size() == templFeatures_.size() ); + CV_Assert( minAngle_ >= 0.0 && minAngle_ < maxAngle_ && maxAngle_ <= 360.0 ); + CV_Assert( angleStep_ > 0.0 && angleStep_ < 360.0 ); + CV_Assert( angleThresh_ > 0 ); - const double iAngleStep = 1.0 / angleStep; - const int angleRange = cvCeil((maxAngle - minAngle) * iAngleStep); + const double iAngleStep = 1.0 / angleStep_; + const int angleRange = cvCeil((maxAngle_ - minAngle_) * iAngleStep); std::vector OHist(angleRange + 1, 0); - for (int i = 0; i <= levels; ++i) + for (int i = 0; i <= levels_; ++i) { - const std::vector& templRow = templFeatures[i]; - const std::vector& imageRow = imageFeatures[i]; + const std::vector& templRow = templFeatures_[i]; + const std::vector& imageRow = imageFeatures_[i]; for (size_t j = 0; j < templRow.size(); ++j) { @@ -1052,45 +785,45 @@ namespace Feature imF = imageRow[k]; const double angle = clampAngle(imF.p1.theta - templF.p1.theta); - if (angle >= minAngle && angle <= maxAngle) + if (angle >= minAngle_ && angle <= maxAngle_) { - const int n = cvRound((angle - minAngle) * iAngleStep); + const int n = cvRound((angle - minAngle_) * iAngleStep); ++OHist[n]; } } } } - angles.clear(); + angles_.clear(); for (int n = 0; n < angleRange; ++n) { - if (OHist[n] >= angleThresh) + if (OHist[n] >= angleThresh_) { - const double angle = minAngle + n * angleStep; - angles.push_back(std::make_pair(angle, OHist[n])); + const double angle = minAngle_ + n * angleStep_; + angles_.push_back(std::make_pair(angle, OHist[n])); } } } - void GHT_Guil_Full::calcScale(double angle) + void GeneralizedHoughGuilImpl::calcScale(double angle) { - CV_Assert(levels > 0); - CV_Assert(templFeatures.size() == static_cast(levels + 1)); - CV_Assert(imageFeatures.size() == templFeatures.size()); - CV_Assert(minScale > 0.0 && minScale < maxScale); - CV_Assert(scaleStep > 0.0); - CV_Assert(scaleThresh > 0); + CV_Assert( levels_ > 0 ); + CV_Assert( templFeatures_.size() == static_cast(levels_ + 1) ); + CV_Assert( imageFeatures_.size() == templFeatures_.size() ); + CV_Assert( minScale_ > 0.0 && minScale_ < maxScale_ ); + CV_Assert( scaleStep_ > 0.0 ); + CV_Assert( scaleThresh_ > 0 ); - const double iScaleStep = 1.0 / scaleStep; - const int scaleRange = cvCeil((maxScale - minScale) * iScaleStep); + const double iScaleStep = 1.0 / scaleStep_; + const int scaleRange = cvCeil((maxScale_ - minScale_) * iScaleStep); std::vector SHist(scaleRange + 1, 0); - for (int i = 0; i <= levels; ++i) + for (int i = 0; i <= levels_; ++i) { - const std::vector& templRow = templFeatures[i]; - const std::vector& imageRow = imageFeatures[i]; + const std::vector& templRow = templFeatures_[i]; + const std::vector& imageRow = imageFeatures_[i]; for (size_t j = 0; j < templRow.size(); ++j) { @@ -1102,12 +835,12 @@ namespace { Feature imF = imageRow[k]; - if (angleEq(imF.p1.theta, templF.p1.theta, angleEpsilon)) + if (angleEq(imF.p1.theta, templF.p1.theta, angleEpsilon_)) { const double scale = imF.d12 / templF.d12; - if (scale >= minScale && scale <= maxScale) + if (scale >= minScale_ && scale <= maxScale_) { - const int s = cvRound((scale - minScale) * iScaleStep); + const int s = cvRound((scale - minScale_) * iScaleStep); ++SHist[s]; } } @@ -1115,39 +848,39 @@ namespace } } - scales.clear(); + scales_.clear(); for (int s = 0; s < scaleRange; ++s) { - if (SHist[s] >= scaleThresh) + if (SHist[s] >= scaleThresh_) { - const double scale = minScale + s * scaleStep; - scales.push_back(std::make_pair(scale, SHist[s])); + const double scale = minScale_ + s * scaleStep_; + scales_.push_back(std::make_pair(scale, SHist[s])); } } } - void GHT_Guil_Full::calcPosition(double angle, int angleVotes, double scale, int scaleVotes) + void GeneralizedHoughGuilImpl::calcPosition(double angle, int angleVotes, double scale, int scaleVotes) { - CV_Assert(levels > 0); - CV_Assert(templFeatures.size() == static_cast(levels + 1)); - CV_Assert(imageFeatures.size() == templFeatures.size()); - CV_Assert(dp > 0.0); - CV_Assert(posThresh > 0); + CV_Assert( levels_ > 0 ); + CV_Assert( templFeatures_.size() == static_cast(levels_ + 1) ); + CV_Assert( imageFeatures_.size() == templFeatures_.size() ); + CV_Assert( dp_ > 0.0 ); + CV_Assert( posThresh_ > 0 ); const double sinVal = sin(toRad(angle)); const double cosVal = cos(toRad(angle)); - const double idp = 1.0 / dp; + const double idp = 1.0 / dp_; - const int histRows = cvCeil(imageSize.height * idp); - const int histCols = cvCeil(imageSize.width * idp); + const int histRows = cvCeil(imageSize_.height * idp); + const int histCols = cvCeil(imageSize_.width * idp); Mat DHist(histRows + 2, histCols + 2, CV_32SC1, Scalar::all(0)); - for (int i = 0; i <= levels; ++i) + for (int i = 0; i <= levels_; ++i) { - const std::vector& templRow = templFeatures[i]; - const std::vector& imageRow = imageFeatures[i]; + const std::vector& templRow = templFeatures_[i]; + const std::vector& imageRow = imageFeatures_[i]; for (size_t j = 0; j < templRow.size(); ++j) { @@ -1165,7 +898,7 @@ namespace { Feature imF = imageRow[k]; - if (angleEq(imF.p1.theta, templF.p1.theta, angleEpsilon)) + if (angleEq(imF.p1.theta, templF.p1.theta, angleEpsilon_)) { Point2d c1, c2; @@ -1195,101 +928,17 @@ namespace { const int votes = curRow[x + 1]; - if (votes > posThresh && votes > curRow[x] && votes >= curRow[x + 2] && votes > prevRow[x + 1] && votes >= nextRow[x + 1]) + if (votes > posThresh_ && votes > curRow[x] && votes >= curRow[x + 2] && votes > prevRow[x + 1] && votes >= nextRow[x + 1]) { - posOutBuf.push_back(Vec4f(static_cast(x * dp), static_cast(y * dp), static_cast(scale), static_cast(angle))); - voteOutBuf.push_back(Vec3i(votes, scaleVotes, angleVotes)); + posOutBuf_.push_back(Vec4f(static_cast(x * dp_), static_cast(y * dp_), static_cast(scale), static_cast(angle))); + voteOutBuf_.push_back(Vec3i(votes, scaleVotes, angleVotes)); } } } } } -Ptr cv::GeneralizedHough::create(int method) -{ - switch (method) - { - case GHT_POSITION: - CV_Assert( !GHT_Ballard_Pos_info_auto.name().empty() ); - return new GHT_Ballard_Pos(); - - case (GHT_POSITION | GHT_SCALE): - CV_Assert( !GHT_Ballard_PosScale_info_auto.name().empty() ); - return new GHT_Ballard_PosScale(); - - case (GHT_POSITION | GHT_ROTATION): - CV_Assert( !GHT_Ballard_PosRotation_info_auto.name().empty() ); - return new GHT_Ballard_PosRotation(); - - case (GHT_POSITION | GHT_SCALE | GHT_ROTATION): - CV_Assert( !GHT_Guil_Full_info_auto.name().empty() ); - return new GHT_Guil_Full(); - } - - CV_Error(CV_StsBadArg, "Unsupported method"); - return Ptr(); -} - -cv::GeneralizedHough::~GeneralizedHough() -{ -} - -void cv::GeneralizedHough::setTemplate(InputArray _templ, int cannyThreshold, Point templCenter) -{ - Mat templ = _templ.getMat(); - - CV_Assert(templ.type() == CV_8UC1); - CV_Assert(cannyThreshold > 0); - - Canny(templ, edges_, cannyThreshold / 2, cannyThreshold); - Sobel(templ, dx_, CV_32F, 1, 0); - Sobel(templ, dy_, CV_32F, 0, 1); - - if (templCenter == Point(-1, -1)) - templCenter = Point(templ.cols / 2, templ.rows / 2); - - setTemplateImpl(edges_, dx_, dy_, templCenter); -} - -void cv::GeneralizedHough::setTemplate(InputArray _edges, InputArray _dx, InputArray _dy, Point templCenter) -{ - Mat edges = _edges.getMat(); - Mat dx = _dx.getMat(); - Mat dy = _dy.getMat(); - - if (templCenter == Point(-1, -1)) - templCenter = Point(edges.cols / 2, edges.rows / 2); - - setTemplateImpl(edges, dx, dy, templCenter); -} - -void cv::GeneralizedHough::detect(InputArray _image, OutputArray positions, OutputArray votes, int cannyThreshold) -{ - Mat image = _image.getMat(); - - CV_Assert(image.type() == CV_8UC1); - CV_Assert(cannyThreshold > 0); - - Canny(image, edges_, cannyThreshold / 2, cannyThreshold); - Sobel(image, dx_, CV_32F, 1, 0); - Sobel(image, dy_, CV_32F, 0, 1); - - detectImpl(edges_, dx_, dy_, positions, votes); -} - -void cv::GeneralizedHough::detect(InputArray _edges, InputArray _dx, InputArray _dy, OutputArray positions, OutputArray votes) -{ - cv::Mat edges = _edges.getMat(); - cv::Mat dx = _dx.getMat(); - cv::Mat dy = _dy.getMat(); - - detectImpl(edges, dx, dy, positions, votes); -} - -void cv::GeneralizedHough::release() +Ptr cv::createGeneralizedHoughGuil() { - edges_.release(); - dx_.release(); - dy_.release(); - releaseImpl(); + return new GeneralizedHoughGuilImpl; } diff --git a/samples/gpu/generalized_hough.cpp b/samples/gpu/generalized_hough.cpp index dbd924f752..1863085256 100644 --- a/samples/gpu/generalized_hough.cpp +++ b/samples/gpu/generalized_hough.cpp @@ -5,13 +5,12 @@ #include "opencv2/core.hpp" #include "opencv2/core/utility.hpp" #include "opencv2/imgproc.hpp" -#include "opencv2/gpu.hpp" +#include "opencv2/gpuimgproc.hpp" #include "opencv2/highgui.hpp" #include "opencv2/contrib.hpp" using namespace std; using namespace cv; -using cv::gpu::GpuMat; static Mat loadImage(const string& name) { @@ -29,8 +28,7 @@ int main(int argc, const char* argv[]) CommandLineParser cmd(argc, argv, "{ image i | pic1.png | input image }" "{ template t | templ.png | template image }" - "{ scale s | | estimate scale }" - "{ rotation r | | estimate rotation }" + "{ full | | estimate scale and rotation }" "{ gpu | | use gpu version }" "{ minDist | 100 | minimum distance between the centers of the detected objects }" "{ levels | 360 | R-Table levels }" @@ -45,7 +43,7 @@ int main(int argc, const char* argv[]) "{ minAngle | 0 | minimal rotation angle to detect in degrees }" "{ maxAngle | 360 | maximal rotation angle to detect in degrees }" "{ angleStep | 1 | angle step in degrees }" - "{ maxSize | 1000 | maximal size of inner buffers }" + "{ maxBufSize | 1000 | maximal size of inner buffers }" "{ help h ? | | print help message }" ); @@ -59,8 +57,7 @@ int main(int argc, const char* argv[]) const string templName = cmd.get("template"); const string imageName = cmd.get("image"); - const bool estimateScale = cmd.has("scale"); - const bool estimateRotation = cmd.has("rotation"); + const bool full = cmd.has("full"); const bool useGpu = cmd.has("gpu"); const double minDist = cmd.get("minDist"); const int levels = cmd.get("levels"); @@ -75,7 +72,7 @@ int main(int argc, const char* argv[]) const double minAngle = cmd.get("minAngle"); const double maxAngle = cmd.get("maxAngle"); const double angleStep = cmd.get("angleStep"); - const int maxSize = cmd.get("maxSize"); + const int maxBufSize = cmd.get("maxBufSize"); if (!cmd.check()) { @@ -86,93 +83,69 @@ int main(int argc, const char* argv[]) Mat templ = loadImage(templName); Mat image = loadImage(imageName); - int method = cv::GeneralizedHough::GHT_POSITION; - if (estimateScale) - method += cv::GeneralizedHough::GHT_SCALE; - if (estimateRotation) - method += cv::GeneralizedHough::GHT_ROTATION; + Ptr alg; + + if (!full) + { + Ptr ballard = useGpu ? gpu::createGeneralizedHoughBallard() : createGeneralizedHoughBallard(); + + ballard->setMinDist(minDist); + ballard->setLevels(levels); + ballard->setDp(dp); + ballard->setMaxBufferSize(maxBufSize); + ballard->setVotesThreshold(votesThreshold); + + alg = ballard; + } + else + { + Ptr guil = useGpu ? gpu::createGeneralizedHoughGuil() : createGeneralizedHoughGuil(); + + guil->setMinDist(minDist); + guil->setLevels(levels); + guil->setDp(dp); + guil->setMaxBufferSize(maxBufSize); + + guil->setMinAngle(minAngle); + guil->setMaxAngle(maxAngle); + guil->setAngleStep(angleStep); + guil->setAngleThresh(angleThresh); + + guil->setMinScale(minScale); + guil->setMaxScale(maxScale); + guil->setScaleStep(scaleStep); + guil->setScaleThresh(scaleThresh); + + guil->setPosThresh(posThresh); + + alg = guil; + } vector position; - cv::TickMeter tm; + TickMeter tm; if (useGpu) { - GpuMat d_templ(templ); - GpuMat d_image(image); - GpuMat d_position; - - Ptr d_hough = gpu::GeneralizedHough::create(method); - d_hough->set("minDist", minDist); - d_hough->set("levels", levels); - d_hough->set("dp", dp); - d_hough->set("maxSize", maxSize); - if (estimateScale && estimateRotation) - { - d_hough->set("angleThresh", angleThresh); - d_hough->set("scaleThresh", scaleThresh); - d_hough->set("posThresh", posThresh); - } - else - { - d_hough->set("votesThreshold", votesThreshold); - } - if (estimateScale) - { - d_hough->set("minScale", minScale); - d_hough->set("maxScale", maxScale); - d_hough->set("scaleStep", scaleStep); - } - if (estimateRotation) - { - d_hough->set("minAngle", minAngle); - d_hough->set("maxAngle", maxAngle); - d_hough->set("angleStep", angleStep); - } - - d_hough->setTemplate(d_templ); + gpu::GpuMat d_templ(templ); + gpu::GpuMat d_image(image); + gpu::GpuMat d_position; + + alg->setTemplate(d_templ); tm.start(); - d_hough->detect(d_image, d_position); - d_hough->downloadResults(d_position, position); + alg->detect(d_image, d_position); + d_position.download(position); tm.stop(); } else { - Ptr hough = GeneralizedHough::create(method); - hough->set("minDist", minDist); - hough->set("levels", levels); - hough->set("dp", dp); - if (estimateScale && estimateRotation) - { - hough->set("angleThresh", angleThresh); - hough->set("scaleThresh", scaleThresh); - hough->set("posThresh", posThresh); - hough->set("maxSize", maxSize); - } - else - { - hough->set("votesThreshold", votesThreshold); - } - if (estimateScale) - { - hough->set("minScale", minScale); - hough->set("maxScale", maxScale); - hough->set("scaleStep", scaleStep); - } - if (estimateRotation) - { - hough->set("minAngle", minAngle); - hough->set("maxAngle", maxAngle); - hough->set("angleStep", angleStep); - } - - hough->setTemplate(templ); + alg->setTemplate(templ); tm.start(); - hough->detect(image, position); + alg->detect(image, position); tm.stop(); }