diff --git a/modules/imgproc/doc/pics/polar_remap_doc.png b/modules/imgproc/doc/pics/polar_remap_doc.png index e3e4105bb8..b0b8431c51 100644 Binary files a/modules/imgproc/doc/pics/polar_remap_doc.png and b/modules/imgproc/doc/pics/polar_remap_doc.png differ diff --git a/modules/imgproc/doc/pics/polar_remap_doc.svg b/modules/imgproc/doc/pics/polar_remap_doc.svg index 544221569d..7d7a2f43b1 100644 --- a/modules/imgproc/doc/pics/polar_remap_doc.svg +++ b/modules/imgproc/doc/pics/polar_remap_doc.svg @@ -19,11 +19,199 @@ version="1.0" inkscape:output_extension="org.inkscape.output.svg.inkscape" inkscape:export-ydpi="90" - inkscape:version="0.91 r13725" - width="1280" - height="1000"> + inkscape:version="0.92.1 r15371" + width="1279.0021" + height="1282.9999"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - @@ -119,8 +277,8 @@ inkscape:stockid="Arrow1Lend"> @@ -134,41 +292,11 @@ inkscape:stockid="Arrow1Lend"> - - - - - - @@ -257,8 +385,8 @@ inkscape:stockid="Arrow1Lend"> @@ -272,9 +400,9 @@ inkscape:isstock="true"> @@ -304,8 +432,8 @@ - - - - - - - - - - - - @@ -412,8 +478,8 @@ inkscape:isstock="true"> @@ -427,8 +493,8 @@ inkscape:stockid="Arrow1Lend"> @@ -444,8 +510,8 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -472,27 +742,27 @@ guidetolerance="10" pagecolor="#ffffff" gridtolerance="10000" - inkscape:zoom="0.881" + inkscape:zoom="0.62296106" objecttolerance="10" showgrid="true" borderopacity="1.0" inkscape:current-layer="layer1" - inkscape:cx="640" - inkscape:cy="454.59705" + inkscape:cx="311.69474" + inkscape:cy="489.42857" showguides="true" inkscape:guide-bbox="true" inkscape:pageopacity="0.0" inkscape:document-units="px" inkscape:window-width="1920" - inkscape:window-height="1058" - inkscape:window-x="-8" + inkscape:window-height="1017" + inkscape:window-x="1912" inkscape:window-y="-8" inkscape:window-maximized="1" inkscape:snap-smooth-nodes="true" inkscape:snap-object-midpoints="true" fit-margin-top="0" - fit-margin-left="0" - fit-margin-right="0" + fit-margin-left="10" + fit-margin-right="10" fit-margin-bottom="0" inkscape:snap-bbox="true" inkscape:object-nodes="true" @@ -506,12 +776,15 @@ inkscape:snap-intersection-paths="true" inkscape:bbox-paths="true" inkscape:bbox-nodes="true" - inkscape:snap-global="true"> + inkscape:snap-global="true" + inkscape:snap-text-baseline="false"> + enabled="false" + originx="2.3367188e-005" + originy="283" /> + height="723" + width="230" /> @@ -2734,7 +2044,7 @@ TkSuQmCC height="100%" /> maxRadius + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:serif;-inkscape-font-specification:'serif Italic';text-align:center;text-anchor:middle;fill:#000000">maxRadius angle° magnitude + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:serif;-inkscape-font-specification:'serif Italic';fill:#ff00ff">magnitude dx dy centercenterC(xc , yc) A(xA(xA , yA) BOUNDING CIRCLE + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:serif;-inkscape-font-specification:'serif Italic';stroke:#ffffff;stroke-opacity:1">BOUNDING CIRCLE a) Source Image + y="-200.22424" + style="font-size:20px;line-height:1.25">a) Source Image ρ: 0 .. Kx * maxRadius = src.cols + id="textPath8906">ρ: 0 .. dsize.cols = Kx * maxRadius centercenterC(0,0) + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:serif;-inkscape-font-specification:'serif Italic';fill:#ffffff;fill-opacity:1">C(0,0) centercenterC(0,0) + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:serif;-inkscape-font-specification:'serif Italic';fill:#000000;fill-opacity:1">C(0,0) - ρA = Kx * magnitude ϕA = Ky * angle° A(ρA(ρA , ϕA) if 2 * maxRadius > min(srcSize)CV_WARP_FILL_OUTLIERS + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;line-height:1.25;font-family:serif;-inkscape-font-specification:'serif Italic'">CV_WARP_FILL_OUTLIERS M = src.cols / logKangle = dsize.height / 2PIKlog = dsize.width / loge(maxRadius)Ky = src.rows / 360.0 + x="2081.5286" + y="784.16187" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.33333397px;line-height:1.25;font-family:serif;-inkscape-font-specification:'serif Italic';text-align:start;text-anchor:start;fill:#000000;stroke:none" + id="tspan5743" /> Blue cross in the center - if 2 * maxRadius > min(srcSize)CV_WARP_FILL_OUTLIERS - Blue cross in the center + x="1168.9106" + y="1572.3779" /> c) linearPolar Result Image + x="1469.0718" + y="1592.7539" + style="font-size:20px;line-height:1.25">c) linearPolar() Deprecated + x="1814.2898" + y="972.29041" /> d) logPolar Result Image - - + x="1930.574" + y="992.42224" + style="font-size:20px;line-height:1.25">d) semiLog=true - ϕA = Ky * angle° - + id="tspan25233">A = Kangle * angle A(ρA(ρA , ϕA) Kx = src.cols / maxRadiusKx = src.cols / maxRadiusKy = src.rows / 360.0 + x="1264.3926" + y="1342.166" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;line-height:1.25;font-family:serif;-inkscape-font-specification:'serif Italic';text-align:start;text-anchor:start" + id="tspan9683">Ky = src.rows / 2PI ρ: 0 .. M * loge(maxRadius) = src.cols + style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:0%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + y="226.32253" + x="1967.1508" + id="text7021">ρ: 0..dsize.width = Klog * loge(maxRadius) ϕ: 0 .. Ky * 360 = src.rows + id="textPath9479">ϕ: 0 .. dsize.height = Kangle * 2PI ρA = M * loge(magnitude ) + x="1921.4661" + y="399.7308">ρA = Klog*loge(magnitude ) + inkscape:export-xdpi="96" + inkscape:export-ydpi="96"> + style="display:inline;fill:#008080;fill-opacity:1;stroke:none;stroke-width:0.99999994;stroke-miterlimit:4;stroke-dasharray:1, 1;stroke-dashoffset:0;stroke-opacity:1" + inkscape:export-filename=".\polar_remap_src.png" /> 270 + sodipodi:role="line" + style="font-size:20.00708008px;line-height:1.25">270 240 + sodipodi:role="line" + style="font-size:20.00708008px;line-height:1.25">240 210 + sodipodi:role="line" + style="font-size:20.00708008px;line-height:1.25">210 180 + sodipodi:role="line" + style="font-size:20.00708008px;line-height:1.25">180 150 + sodipodi:role="line" + style="font-size:20.00708008px;line-height:1.25">150 120 + sodipodi:role="line" + style="font-size:20.00708008px;line-height:1.25">120 90 + sodipodi:role="line" + style="font-size:20.00708008px;line-height:1.25">90 60 + sodipodi:role="line" + style="font-size:20.00708008px;line-height:1.25">60 30 + sodipodi:role="line" + style="font-size:20.00708008px;line-height:1.25">30 300 + sodipodi:role="line" + style="font-size:20.00708008px;line-height:1.25">330 330 + sodipodi:role="line" + style="font-size:20.00708008px;line-height:1.25">300 0 + sodipodi:role="line" + style="font-size:20.00708008px;line-height:1.25">0 - Size: W:600 H:440 pxSize: W:600 H:440 pxCenter = x:240, y:220magnitude=100pxangle = 60degangle = 60degmaxRadius= 230px + style="font-size:17.5px;line-height:1.25;text-align:start;text-anchor:start">maxRadius= 230px Kx = 600px / 230px = 2.609 => rho = 260.869pxKx = 600px / 230px = 2.609 => rho = 260.869pxKy = 440px / 360deg = 1.222 pix/deg = phi = 73.333pxM = 600px / ln(230px) = 110.33 pn/ln(px) => rho = 508.103px b) Params References + y="-200.08264" + style="font-size:20px;line-height:1.25">b) Params References + OUTLIERS + + + ρA = Kx * magnitude + + + ρ: 0 .. dsize.width = Klin * maxRadius + + centerC(0,0) + + + ϕA = Kphi * angle° + A(ρA , ϕA) + Blue crossin thecenter + + + + + ρA = Klin * magnitude + CV_WARP_FILL_OUTLIERS + + + + ϕ: 0 .. dsize.height = Kangle * 2PI + + c) semiLog=false + Kangle = dsize.height / 2PIKlin = dsize.width / maxRadius + Blue crossin thecenter + CV_WARP_FILL_OUTLIERS + + @@ -3744,7 +3265,7 @@ TkSuQmCC OpenCV - + 2016-08-08 diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index b93a10c0d9..ab46838160 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -295,6 +295,15 @@ enum InterpolationFlags{ WARP_INVERSE_MAP = 16 }; +/** \brief Specify the polar mapping mode +@sa warpPolar +*/ +enum WarpPolarMode +{ + WARP_POLAR_LINEAR = 0, ///< Remaps an image to/from polar space. + WARP_POLAR_LOG = 256 ///< Remaps an image to/from semilog-polar space. +}; + enum InterpolationMasks { INTER_BITS = 5, INTER_BITS2 = INTER_BITS * 2, @@ -2546,7 +2555,10 @@ An example using the cv::linearPolar and cv::logPolar operations /** @brief Remaps an image to semilog-polar coordinates space. -Transform the source image using the following transformation (See @ref polar_remaps_reference_image "Polar remaps reference image"): +@deprecated This function produces same result as cv::warpPolar(src, dst, src.size(), center, maxRadius, flags+WARP_POLAR_LOG); + +@internal +Transform the source image using the following transformation (See @ref polar_remaps_reference_image "Polar remaps reference image d)"): \f[\begin{array}{l} dst( \rho , \phi ) = src(x,y) \\ dst.size() \leftarrow src.size() @@ -2556,13 +2568,13 @@ where \f[\begin{array}{l} I = (dx,dy) = (x - center.x,y - center.y) \\ \rho = M \cdot log_e(\texttt{magnitude} (I)) ,\\ - \phi = Ky \cdot \texttt{angle} (I)_{0..360 deg} \\ + \phi = Kangle \cdot \texttt{angle} (I) \\ \end{array}\f] and \f[\begin{array}{l} M = src.cols / log_e(maxRadius) \\ - Ky = src.rows / 360 \\ + Kangle = src.rows / 2\Pi \\ \end{array}\f] The function emulates the human "foveal" vision and can be used for fast scale and @@ -2576,16 +2588,19 @@ rotation-invariant template matching, for object tracking and so forth. @note - The function can not operate in-place. - To calculate magnitude and angle in degrees #cartToPolar is used internally thus angles are measured from 0 to 360 with accuracy about 0.3 degrees. + +@sa cv::linearPolar +@endinternal */ CV_EXPORTS_W void logPolar( InputArray src, OutputArray dst, Point2f center, double M, int flags ); /** @brief Remaps an image to polar coordinates space. -@anchor polar_remaps_reference_image -![Polar remaps reference](pics/polar_remap_doc.png) +@deprecated This function produces same result as cv::warpPolar(src, dst, src.size(), center, maxRadius, flags) -Transform the source image using the following transformation: +@internal +Transform the source image using the following transformation (See @ref polar_remaps_reference_image "Polar remaps reference image c)"): \f[\begin{array}{l} dst( \rho , \phi ) = src(x,y) \\ dst.size() \leftarrow src.size() @@ -2594,14 +2609,14 @@ Transform the source image using the following transformation: where \f[\begin{array}{l} I = (dx,dy) = (x - center.x,y - center.y) \\ - \rho = Kx \cdot \texttt{magnitude} (I) ,\\ - \phi = Ky \cdot \texttt{angle} (I)_{0..360 deg} + \rho = Kmag \cdot \texttt{magnitude} (I) ,\\ + \phi = angle \cdot \texttt{angle} (I) \end{array}\f] and \f[\begin{array}{l} Kx = src.cols / maxRadius \\ - Ky = src.rows / 360 + Ky = src.rows / 2\Pi \end{array}\f] @@ -2615,10 +2630,104 @@ and - The function can not operate in-place. - To calculate magnitude and angle in degrees #cartToPolar is used internally thus angles are measured from 0 to 360 with accuracy about 0.3 degrees. +@sa cv::logPolar +@endinternal */ CV_EXPORTS_W void linearPolar( InputArray src, OutputArray dst, Point2f center, double maxRadius, int flags ); + +/** \brief Remaps an image to polar or semilog-polar coordinates space + +@anchor polar_remaps_reference_image +![Polar remaps reference](pics/polar_remap_doc.png) + +Transform the source image using the following transformation: +\f[ +dst(\rho , \phi ) = src(x,y) +\f] + +where +\f[ +\begin{array}{l} +\vec{I} = (x - center.x, \;y - center.y) \\ +\phi = Kangle \cdot \texttt{angle} (\vec{I}) \\ +\rho = \left\{\begin{matrix} +Klin \cdot \texttt{magnitude} (\vec{I}) & default \\ +Klog \cdot log_e(\texttt{magnitude} (\vec{I})) & if \; semilog \\ +\end{matrix}\right. +\end{array} +\f] + +and +\f[ +\begin{array}{l} +Kangle = dsize.height / 2\Pi \\ +Klin = dsize.width / maxRadius \\ +Klog = dsize.width / log_e(maxRadius) \\ +\end{array} +\f] + + +\par Linear vs semilog mapping + +Polar mapping can be linear or semi-log. Add one of #WarpPolarMode to `flags` to specify the polar mapping mode. + +Linear is the default mode. + +The semilog mapping emulates the human "foveal" vision that permit very high acuity on the line of sight (central vision) +in contrast to peripheral vision where acuity is minor. + +\par Option on `dsize`: + +- if both values in `dsize <=0 ` (default), +the destination image will have (almost) same area of source bounding circle: +\f[\begin{array}{l} +dsize.area \leftarrow (maxRadius^2 \cdot \Pi) \\ +dsize.width = \texttt{cvRound}(maxRadius) \\ +dsize.height = \texttt{cvRound}(maxRadius \cdot \Pi) \\ +\end{array}\f] + + +- if only `dsize.height <= 0`, +the destination image area will be proportional to the bounding circle area but scaled by `Kx * Kx`: +\f[\begin{array}{l} +dsize.height = \texttt{cvRound}(dsize.width \cdot \Pi) \\ +\end{array} +\f] + +- if both values in `dsize > 0 `, +the destination image will have the given size therefore the area of the bounding circle will be scaled to `dsize`. + + +\par Reverse mapping + +You can get reverse mapping adding #WARP_INVERSE_MAP to `flags` +\snippet polar_transforms.cpp InverseMap + +In addiction, to calculate the original coordinate from a polar mapped coordinate \f$(rho, phi)->(x, y)\f$: +\snippet polar_transforms.cpp InverseCoordinate + +@param src Source image. +@param dst Destination image. It will have same type as src. +@param dsize The destination image size (see description for valid options). +@param center The transformation center. +@param maxRadius The radius of the bounding circle to transform. It determines the inverse magnitude scale parameter too. +@param flags A combination of interpolation methods, #InterpolationFlags + #WarpPolarMode. + - Add #WARP_POLAR_LINEAR to select linear polar mapping (default) + - Add #WARP_POLAR_LOG to select semilog polar mapping + - Add #WARP_INVERSE_MAP for reverse mapping. +@note +- The function can not operate in-place. +- To calculate magnitude and angle in degrees #cartToPolar is used internally thus angles are measured from 0 to 360 with accuracy about 0.3 degrees. +- This function uses #remap. Due to current implementation limitations the size of an input and output images should be less than 32767x32767. + +@sa cv::remap +*/ +CV_EXPORTS_W void warpPolar(InputArray src, OutputArray dst, Size dsize, + Point2f center, double maxRadius, int flags); + + //! @} imgproc_transform //! @addtogroup imgproc_misc diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc_c.h b/modules/imgproc/include/opencv2/imgproc/imgproc_c.h index fadb8820b7..78032b7626 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc_c.h +++ b/modules/imgproc/include/opencv2/imgproc/imgproc_c.h @@ -260,14 +260,14 @@ CVAPI(void) cvConvertMaps( const CvArr* mapx, const CvArr* mapy, CvArr* mapxy, CvArr* mapalpha ); /** @brief Performs forward or inverse log-polar image transform -@see cv::logPolar +@see cv::warpPolar */ CVAPI(void) cvLogPolar( const CvArr* src, CvArr* dst, CvPoint2D32f center, double M, int flags CV_DEFAULT(CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS)); /** Performs forward or inverse linear-polar image transform -@see cv::linearPolar +@see cv::warpPolar */ CVAPI(void) cvLinearPolar( const CvArr* src, CvArr* dst, CvPoint2D32f center, double maxRadius, diff --git a/modules/imgproc/perf/opencl/perf_imgwarp.cpp b/modules/imgproc/perf/opencl/perf_imgwarp.cpp index 44fb84d1cc..d13b54bdce 100644 --- a/modules/imgproc/perf/opencl/perf_imgwarp.cpp +++ b/modules/imgproc/perf/opencl/perf_imgwarp.cpp @@ -204,7 +204,7 @@ OCL_PERF_TEST_P(RemapFixture, Remap, const RemapParams params = GetParam(); const Size srcSize = get<0>(params); const int type = get<1>(params), interpolation = get<2>(params), borderMode = BORDER_CONSTANT; - const double eps = CV_MAT_DEPTH(type) <= CV_32S ? 1 : 1e-4; + //const double eps = CV_MAT_DEPTH(type) <= CV_32S ? 1 : 1e-4; checkDeviceMaxMemoryAllocSize(srcSize, type); diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index 58ae385585..adc201acbe 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -1377,6 +1377,10 @@ static bool ocl_remap(InputArray _src, OutputArray _dst, InputArray _map1, Input return k.run(2, globalThreads, NULL, false); } +#if 0 +/** +@deprecated with old version of cv::linearPolar +*/ static bool ocl_linearPolar(InputArray _src, OutputArray _dst, Point2f center, double maxRadius, int flags) { @@ -1517,6 +1521,8 @@ static bool ocl_logPolar(InputArray _src, OutputArray _dst, } #endif +#endif + #ifdef HAVE_OPENVX static bool openvx_remap(Mat src, Mat dst, Mat map1, Mat map2, int interpolation, const Scalar& borderValue) { @@ -3252,397 +3258,86 @@ cvConvertMaps( const CvArr* arr1, const CvArr* arr2, CvArr* dstarr1, CvArr* dsta cv::convertMaps( map1, map2, dstmap1, dstmap2, dstmap1.type(), false ); } -/****************************************************************************************\ -* Log-Polar Transform * -\****************************************************************************************/ - -/* now it is done via Remap; more correct implementation should use - some super-sampling technique outside of the "fovea" circle */ -CV_IMPL void -cvLogPolar( const CvArr* srcarr, CvArr* dstarr, - CvPoint2D32f center, double M, int flags ) -{ - Mat src_with_border; // don't scope this variable (it holds image data) - - cv::Ptr mapx, mapy; - - CvMat srcstub, *src = cvGetMat(srcarr, &srcstub); - CvMat dststub, *dst = cvGetMat(dstarr, &dststub); - CvSize dsize; - - if( !CV_ARE_TYPES_EQ( src, dst )) - CV_Error( CV_StsUnmatchedFormats, "" ); - - if( M <= 0 ) - CV_Error( CV_StsOutOfRange, "M should be >0" ); - - dsize = cvGetMatSize(dst); - - mapx.reset(cvCreateMat( dsize.height, dsize.width, CV_32F )); - mapy.reset(cvCreateMat( dsize.height, dsize.width, CV_32F )); - - if( !(flags & CV_WARP_INVERSE_MAP) ) - { - int phi, rho; - cv::AutoBuffer _exp_tab(dsize.width); - double* exp_tab = _exp_tab; - - for( rho = 0; rho < dst->width; rho++ ) - exp_tab[rho] = std::exp(rho/M) - 1.0; - - for( phi = 0; phi < dsize.height; phi++ ) - { - double cp = cos(phi*2*CV_PI/dsize.height); - double sp = sin(phi*2*CV_PI/dsize.height); - float* mx = (float*)(mapx->data.ptr + phi*mapx->step); - float* my = (float*)(mapy->data.ptr + phi*mapy->step); - - for( rho = 0; rho < dsize.width; rho++ ) - { - double r = exp_tab[rho]; - double x = r*cp + center.x; - double y = r*sp + center.y; - - mx[rho] = (float)x; - my[rho] = (float)y; - } - } - } - else - { - const int ANGLE_BORDER = 1; - Mat src_ = cv::cvarrToMat(src); - cv::copyMakeBorder(src_, src_with_border, ANGLE_BORDER, ANGLE_BORDER, 0, 0, BORDER_WRAP); - srcstub = src_with_border; src = &srcstub; - CvSize ssize = cvGetMatSize(src); - ssize.height -= 2*ANGLE_BORDER; - - int x, y; - CvMat bufx, bufy, bufp, bufa; - double ascale = ssize.height/(2*CV_PI); - cv::AutoBuffer _buf(4*dsize.width); - float* buf = _buf; - - bufx = cvMat( 1, dsize.width, CV_32F, buf ); - bufy = cvMat( 1, dsize.width, CV_32F, buf + dsize.width ); - bufp = cvMat( 1, dsize.width, CV_32F, buf + dsize.width*2 ); - bufa = cvMat( 1, dsize.width, CV_32F, buf + dsize.width*3 ); - - for( x = 0; x < dsize.width; x++ ) - bufx.data.fl[x] = (float)x - center.x; - - for( y = 0; y < dsize.height; y++ ) - { - float* mx = (float*)(mapx->data.ptr + y*mapx->step); - float* my = (float*)(mapy->data.ptr + y*mapy->step); - - for( x = 0; x < dsize.width; x++ ) - bufy.data.fl[x] = (float)y - center.y; - -#if 1 - cvCartToPolar( &bufx, &bufy, &bufp, &bufa ); - - for( x = 0; x < dsize.width; x++ ) - bufp.data.fl[x] += 1.f; - - cvLog( &bufp, &bufp ); - - for( x = 0; x < dsize.width; x++ ) - { - double rho = bufp.data.fl[x]*M; - double phi = bufa.data.fl[x]*ascale; - - mx[x] = (float)rho; - my[x] = (float)phi + ANGLE_BORDER; - } -#else - for( x = 0; x < dsize.width; x++ ) - { - double xx = bufx.data.fl[x]; - double yy = bufy.data.fl[x]; - - double p = log(std::sqrt(xx*xx + yy*yy) + 1.)*M; - double a = atan2(yy,xx); - if( a < 0 ) - a = 2*CV_PI + a; - a *= ascale; - - mx[x] = (float)p; - my[x] = (float)a; - } -#endif - } - } - - cvRemap( src, dst, mapx, mapy, flags, cvScalarAll(0) ); -} - -void cv::logPolar( InputArray _src, OutputArray _dst, - Point2f center, double M, int flags ) -{ - CV_INSTRUMENT_REGION() - - CV_OCL_RUN(_src.isUMat() && _dst.isUMat(), - ocl_logPolar(_src, _dst, center, M, flags)); - Mat src_with_border; // don't scope this variable (it holds image data) - - Mat mapx, mapy; - - Mat srcstub, src = _src.getMat(); - _dst.create(src.size(), src.type()); - Size dsize = src.size(); - - if (M <= 0) - CV_Error(CV_StsOutOfRange, "M should be >0"); - - - mapx.create(dsize, CV_32F); - mapy.create(dsize, CV_32F); - - if (!(flags & CV_WARP_INVERSE_MAP)) - { - int phi, rho; - cv::AutoBuffer _exp_tab(dsize.width); - double* exp_tab = _exp_tab; - - for (rho = 0; rho < dsize.width; rho++) - exp_tab[rho] = std::exp(rho / M) - 1.0; - - for (phi = 0; phi < dsize.height; phi++) - { - double cp = std::cos(phi * 2 * CV_PI / dsize.height); - double sp = std::sin(phi * 2 * CV_PI / dsize.height); - float* mx = (float*)(mapx.data + phi*mapx.step); - float* my = (float*)(mapy.data + phi*mapy.step); - - for (rho = 0; rho < dsize.width; rho++) - { - double r = exp_tab[rho]; - double x = r*cp + center.x; - double y = r*sp + center.y; - - mx[rho] = (float)x; - my[rho] = (float)y; - } - } - } - else - { - const int ANGLE_BORDER = 1; - cv::copyMakeBorder(src, src_with_border, ANGLE_BORDER, ANGLE_BORDER, 0, 0, BORDER_WRAP); - srcstub = src_with_border; src = srcstub; - Size ssize = src.size(); - ssize.height -= 2 * ANGLE_BORDER; - - int x, y; - Mat bufx, bufy, bufp, bufa; - double ascale = ssize.height / (2 * CV_PI); - - bufx = Mat(1, dsize.width, CV_32F); - bufy = Mat(1, dsize.width, CV_32F); - bufp = Mat(1, dsize.width, CV_32F); - bufa = Mat(1, dsize.width, CV_32F); - - for (x = 0; x < dsize.width; x++) - bufx.at(0, x) = (float)x - center.x; - - for (y = 0; y < dsize.height; y++) - { - float* mx = (float*)(mapx.data + y*mapx.step); - float* my = (float*)(mapy.data + y*mapy.step); - - for (x = 0; x < dsize.width; x++) - bufy.at(0, x) = (float)y - center.y; - -#if 1 - cartToPolar(bufx, bufy, bufp, bufa); - - for (x = 0; x < dsize.width; x++) - bufp.at(0, x) += 1.f; - - log(bufp, bufp); - - for (x = 0; x < dsize.width; x++) - { - double rho = bufp.at(0, x) * M; - double phi = bufa.at(0, x) * ascale; - - mx[x] = (float)rho; - my[x] = (float)phi + ANGLE_BORDER; - } -#else - for (x = 0; x < dsize.width; x++) - { - double xx = bufx.at(0, x); - double yy = bufy.at(0, x); - double p = log(std::sqrt(xx*xx + yy*yy) + 1.)*M; - double a = atan2(yy, xx); - if (a < 0) - a = 2 * CV_PI + a; - a *= ascale; - mx[x] = (float)p; - my[x] = (float)a; - } -#endif - } - } - - remap(src, _dst, mapx, mapy, flags & cv::INTER_MAX, - (flags & CV_WARP_FILL_OUTLIERS) ? cv::BORDER_CONSTANT : cv::BORDER_TRANSPARENT); -} - /**************************************************************************************** - Linear-Polar Transform - J.L. Blanco, Apr 2009 - ****************************************************************************************/ -CV_IMPL -void cvLinearPolar( const CvArr* srcarr, CvArr* dstarr, - CvPoint2D32f center, double maxRadius, int flags ) +PkLab.net 2018 based on cv::linearPolar from OpenCV by J.L. Blanco, Apr 2009 +****************************************************************************************/ +void cv::warpPolar(InputArray _src, OutputArray _dst, Size dsize, + Point2f center, double maxRadius, int flags) { - Mat src_with_border; // don't scope this variable (it holds image data) - - cv::Ptr mapx, mapy; - - CvMat srcstub, *src = (CvMat*)srcarr; - CvMat dststub, *dst = (CvMat*)dstarr; - CvSize dsize; - - src = cvGetMat( srcarr, &srcstub,0,0 ); - dst = cvGetMat( dstarr, &dststub,0,0 ); - - if( !CV_ARE_TYPES_EQ( src, dst )) - CV_Error( CV_StsUnmatchedFormats, "" ); - - dsize = cvGetMatSize(dst); - - mapx.reset(cvCreateMat( dsize.height, dsize.width, CV_32F )); - mapy.reset(cvCreateMat( dsize.height, dsize.width, CV_32F )); - - if( !(flags & CV_WARP_INVERSE_MAP) ) + // if dest size is empty given than calculate using proportional setting + // thus we calculate needed angles to keep same area as bounding circle + if ((dsize.width <= 0) && (dsize.height <= 0)) { - int phi, rho; - - for( phi = 0; phi < dsize.height; phi++ ) - { - double cp = cos(phi*2*CV_PI/dsize.height); - double sp = sin(phi*2*CV_PI/dsize.height); - float* mx = (float*)(mapx->data.ptr + phi*mapx->step); - float* my = (float*)(mapy->data.ptr + phi*mapy->step); - - for( rho = 0; rho < dsize.width; rho++ ) - { - double r = maxRadius*rho/dsize.width; - double x = r*cp + center.x; - double y = r*sp + center.y; - - mx[rho] = (float)x; - my[rho] = (float)y; - } - } + dsize.width = cvRound(maxRadius); + dsize.height = cvRound(maxRadius * CV_PI); } - else + else if (dsize.height <= 0) { - const int ANGLE_BORDER = 1; - Mat src_ = cv::cvarrToMat(src); - cv::copyMakeBorder(src_, src_with_border, ANGLE_BORDER, ANGLE_BORDER, 0, 0, BORDER_WRAP); - srcstub = src_with_border; src = &srcstub; - CvSize ssize = cvGetMatSize(src); - ssize.height -= 2*ANGLE_BORDER; - - int x, y; - CvMat bufx, bufy, bufp, bufa; - const double ascale = ssize.height/(2*CV_PI); - const double pscale = ssize.width/maxRadius; - - cv::AutoBuffer _buf(4*dsize.width); - float* buf = _buf; - - bufx = cvMat( 1, dsize.width, CV_32F, buf ); - bufy = cvMat( 1, dsize.width, CV_32F, buf + dsize.width ); - bufp = cvMat( 1, dsize.width, CV_32F, buf + dsize.width*2 ); - bufa = cvMat( 1, dsize.width, CV_32F, buf + dsize.width*3 ); - - for( x = 0; x < dsize.width; x++ ) - bufx.data.fl[x] = (float)x - center.x; - - for( y = 0; y < dsize.height; y++ ) - { - float* mx = (float*)(mapx->data.ptr + y*mapx->step); - float* my = (float*)(mapy->data.ptr + y*mapy->step); - - for( x = 0; x < dsize.width; x++ ) - bufy.data.fl[x] = (float)y - center.y; - - cvCartToPolar( &bufx, &bufy, &bufp, &bufa, 0 ); - - for( x = 0; x < dsize.width; x++ ) - { - double rho = bufp.data.fl[x]*pscale; - double phi = bufa.data.fl[x]*ascale; - mx[x] = (float)rho; - my[x] = (float)phi + ANGLE_BORDER; - } - } + dsize.height = cvRound(dsize.width * CV_PI); } - cvRemap( src, dst, mapx, mapy, flags, cvScalarAll(0) ); -} - -void cv::linearPolar( InputArray _src, OutputArray _dst, - Point2f center, double maxRadius, int flags ) -{ - CV_INSTRUMENT_REGION() - - CV_OCL_RUN(_src.isUMat() && _dst.isUMat(), - ocl_linearPolar(_src, _dst, center, maxRadius, flags)); - Mat src_with_border; // don't scope this variable (it holds image data) - Mat mapx, mapy; - Mat srcstub, src = _src.getMat(); - _dst.create(src.size(), src.type()); - Size dsize = src.size(); - - mapx.create(dsize, CV_32F); mapy.create(dsize, CV_32F); + bool semiLog = (flags & WARP_POLAR_LOG) != 0; if (!(flags & CV_WARP_INVERSE_MAP)) { + double Kangle = CV_2PI / dsize.height; int phi, rho; + // precalculate scaled rho + Mat rhos = Mat(1, dsize.width, CV_32F); + float* bufRhos = (float*)(rhos.data); + if (semiLog) + { + double Kmag = std::log(maxRadius) / dsize.width; + for (rho = 0; rho < dsize.width; rho++) + bufRhos[rho] = (float)(std::exp(rho * Kmag) - 1.0); + + } + else + { + double Kmag = maxRadius / dsize.width; + for (rho = 0; rho < dsize.width; rho++) + bufRhos[rho] = (float)(rho * Kmag); + } + for (phi = 0; phi < dsize.height; phi++) { - double cp = std::cos(phi * 2 * CV_PI / dsize.height); - double sp = std::sin(phi * 2 * CV_PI / dsize.height); + double KKy = Kangle * phi; + double cp = std::cos(KKy); + double sp = std::sin(KKy); float* mx = (float*)(mapx.data + phi*mapx.step); float* my = (float*)(mapy.data + phi*mapy.step); for (rho = 0; rho < dsize.width; rho++) { - double r = maxRadius*rho / dsize.width; - double x = r*cp + center.x; - double y = r*sp + center.y; + double x = bufRhos[rho] * cp + center.x; + double y = bufRhos[rho] * sp + center.y; mx[rho] = (float)x; my[rho] = (float)y; } } + remap(_src, _dst, mapx, mapy, flags & cv::INTER_MAX, (flags & CV_WARP_FILL_OUTLIERS) ? cv::BORDER_CONSTANT : cv::BORDER_TRANSPARENT); } else { const int ANGLE_BORDER = 1; - - cv::copyMakeBorder(src, src_with_border, ANGLE_BORDER, ANGLE_BORDER, 0, 0, BORDER_WRAP); - src = src_with_border; - Size ssize = src_with_border.size(); + cv::copyMakeBorder(_src, _dst, ANGLE_BORDER, ANGLE_BORDER, 0, 0, BORDER_WRAP); + Mat src = _dst.getMat(); + Size ssize = _dst.size(); ssize.height -= 2 * ANGLE_BORDER; + const double Kangle = CV_2PI / ssize.height; + double Kmag; + if (semiLog) + Kmag = std::log(maxRadius) / ssize.width; + else + Kmag = maxRadius / ssize.width; int x, y; Mat bufx, bufy, bufp, bufa; - const double ascale = ssize.height / (2 * CV_PI); - const double pscale = ssize.width / maxRadius; - - bufx = Mat(1, dsize.width, CV_32F); bufy = Mat(1, dsize.width, CV_32F); @@ -3662,17 +3357,63 @@ void cv::linearPolar( InputArray _src, OutputArray _dst, cartToPolar(bufx, bufy, bufp, bufa, 0); + if (semiLog) + { + bufp += 1.f; + log(bufp, bufp); + } + for (x = 0; x < dsize.width; x++) { - double rho = bufp.at(0, x) * pscale; - double phi = bufa.at(0, x) * ascale; + double rho = bufp.at(0, x) / Kmag; + double phi = bufa.at(0, x) / Kangle; mx[x] = (float)rho; my[x] = (float)phi + ANGLE_BORDER; } } + remap(src, _dst, mapx, mapy, flags & cv::INTER_MAX, + (flags & CV_WARP_FILL_OUTLIERS) ? cv::BORDER_CONSTANT : cv::BORDER_TRANSPARENT); } +} - remap(src, _dst, mapx, mapy, flags & cv::INTER_MAX, (flags & CV_WARP_FILL_OUTLIERS) ? cv::BORDER_CONSTANT : cv::BORDER_TRANSPARENT); +void cv::linearPolar( InputArray _src, OutputArray _dst, + Point2f center, double maxRadius, int flags ) +{ + warpPolar(_src, _dst, _src.size(), center, maxRadius, flags & ~WARP_POLAR_LOG); +} + +void cv::logPolar( InputArray _src, OutputArray _dst, + Point2f center, double maxRadius, int flags ) +{ + Size ssize = _src.size(); + double M = maxRadius > 0 ? std::exp(ssize.width / maxRadius) : 1; + warpPolar(_src, _dst, ssize, center, M, flags | WARP_POLAR_LOG); +} + +CV_IMPL +void cvLinearPolar( const CvArr* srcarr, CvArr* dstarr, + CvPoint2D32f center, double maxRadius, int flags ) +{ + Mat src = cvarrToMat(srcarr); + Mat dst = cvarrToMat(dstarr); + + CV_Assert(src.size == dst.size); + CV_Assert(src.type() == dst.type()); + + cv::linearPolar(src, dst, center, maxRadius, flags); +} + +CV_IMPL +void cvLogPolar( const CvArr* srcarr, CvArr* dstarr, + CvPoint2D32f center, double M, int flags ) +{ + Mat src = cvarrToMat(srcarr); + Mat dst = cvarrToMat(dstarr); + + CV_Assert(src.size == dst.size); + CV_Assert(src.type() == dst.type()); + + cv::logPolar(src, dst, center, M, flags); } /* End of file. */ diff --git a/modules/imgproc/test/test_imgwarp.cpp b/modules/imgproc/test/test_imgwarp.cpp index 62907c4d17..741ec7736e 100644 --- a/modules/imgproc/test/test_imgwarp.cpp +++ b/modules/imgproc/test/test_imgwarp.cpp @@ -1781,7 +1781,7 @@ TEST(Imgproc_Remap, DISABLED_memleak) } } - +//** @deprecated */ TEST(Imgproc_linearPolar, identity) { const int N = 33; @@ -1821,7 +1821,7 @@ TEST(Imgproc_linearPolar, identity) #endif } - +//** @deprecated */ TEST(Imgproc_logPolar, identity) { const int N = 33; @@ -1862,6 +1862,52 @@ TEST(Imgproc_logPolar, identity) #endif } +TEST(Imgproc_warpPolar, identity) +{ + const int N = 33; + Mat in(N, N, CV_8UC3, Scalar(255, 0, 0)); + in(cv::Rect(N / 3, N / 3, N / 3, N / 3)).setTo(Scalar::all(255)); + cv::blur(in, in, Size(5, 5)); + cv::blur(in, in, Size(5, 5)); + + Mat src = in.clone(); + Mat dst; + + Rect roi = Rect(0, 0, in.cols - ((N + 19) / 20), in.rows); + Point2f center = Point2f((N - 1) * 0.5f, (N - 1) * 0.5f); + double radius = N * 0.5; + int flags = CV_WARP_FILL_OUTLIERS | CV_INTER_LINEAR; + // test linearPolar + for (int ki = 1; ki <= 5; ki++) + { + warpPolar(src, dst, src.size(), center, radius, flags + WARP_POLAR_LINEAR + CV_WARP_INVERSE_MAP); + warpPolar(dst, src, src.size(), center, radius, flags + WARP_POLAR_LINEAR); + + double psnr = cv::PSNR(in(roi), src(roi)); + EXPECT_LE(25, psnr) << "iteration=" << ki; + } + // test logPolar + src = in.clone(); + for (int ki = 1; ki <= 5; ki++) + { + warpPolar(src, dst, src.size(),center, radius, flags + WARP_POLAR_LOG + CV_WARP_INVERSE_MAP ); + warpPolar(dst, src, src.size(),center, radius, flags + WARP_POLAR_LOG); + + double psnr = cv::PSNR(in(roi), src(roi)); + EXPECT_LE(25, psnr) << "iteration=" << ki; + } + +#if 0 + Mat all(N*2+2,N*2+2, src.type(), Scalar(0,0,255)); + in.copyTo(all(Rect(0,0,N,N))); + src.copyTo(all(Rect(0,N+1,N,N))); + src.copyTo(all(Rect(N+1,0,N,N))); + dst.copyTo(all(Rect(N+1,N+1,N,N))); + imwrite("linearPolar.png", all); + imshow("input", in); imshow("result", dst); imshow("restore", src); imshow("all", all); + cv::waitKey(); +#endif +} }} // namespace /* End of file. */ diff --git a/samples/cpp/polar_transforms.cpp b/samples/cpp/polar_transforms.cpp index d860e27981..4a6014f433 100644 --- a/samples/cpp/polar_transforms.cpp +++ b/samples/cpp/polar_transforms.cpp @@ -43,33 +43,82 @@ int main( int argc, char** argv ) moveWindow( "Log-Polar", 700,20 ); moveWindow( "Recovered Linear-Polar", 20, 350 ); moveWindow( "Recovered Log-Polar", 700, 350 ); - + int flags = INTER_LINEAR + WARP_FILL_OUTLIERS; + Mat src; for(;;) { - Mat frame; - capture >> frame; + capture >> src; - if( frame.empty() ) + if(src.empty() ) break; - Point2f center( (float)frame.cols / 2, (float)frame.rows / 2 ); - double M = 70; + Point2f center( (float)src.cols / 2, (float)src.rows / 2 ); + double maxRadius = 0.7*min(center.y, center.x); - logPolar(frame,log_polar_img, center, M, INTER_LINEAR + WARP_FILL_OUTLIERS); - linearPolar(frame,lin_polar_img, center, M, INTER_LINEAR + WARP_FILL_OUTLIERS); +#if 0 //deprecated + double M = frame.cols / log(maxRadius); + logPolar(frame, log_polar_img, center, M, flags); + linearPolar(frame, lin_polar_img, center, maxRadius, flags); - logPolar(log_polar_img, recovered_log_polar, center, M, WARP_INVERSE_MAP + INTER_LINEAR); - linearPolar(lin_polar_img, recovered_lin_polar_img, center, M, WARP_INVERSE_MAP + INTER_LINEAR + WARP_FILL_OUTLIERS); + logPolar(log_polar_img, recovered_log_polar, center, M, flags + WARP_INVERSE_MAP); + linearPolar(lin_polar_img, recovered_lin_polar_img, center, maxRadius, flags + WARP_INVERSE_MAP); +#endif + //! [InverseMap] + // direct transform + warpPolar(src, lin_polar_img, Size(),center, maxRadius, flags); // linear Polar + warpPolar(src, log_polar_img, Size(),center, maxRadius, flags + WARP_POLAR_LOG); // semilog Polar + // inverse transform + warpPolar(lin_polar_img, recovered_lin_polar_img, src.size(), center, maxRadius, flags + WARP_INVERSE_MAP); + warpPolar(log_polar_img, recovered_log_polar, src.size(), center, maxRadius, flags + WARP_POLAR_LOG + WARP_INVERSE_MAP); + //! [InverseMap] - imshow("Log-Polar", log_polar_img ); - imshow("Linear-Polar", lin_polar_img ); + // Below is the reverse transformation for (rho, phi)->(x, y) : + Mat dst; + if (flags & WARP_POLAR_LOG) + dst = log_polar_img; + else + dst = lin_polar_img; + //get a point from the polar image + int rho = cvRound(dst.cols * 0.75); + int phi = cvRound(dst.rows / 2.0); + + //! [InverseCoordinate] + double angleRad, magnitude; + double Kangle = dst.rows / CV_2PI; + angleRad = phi / Kangle; + if (flags & WARP_POLAR_LOG) + { + double Klog = dst.cols / std::log(maxRadius); + magnitude = std::exp(rho / Klog); + } + else + { + double Klin = dst.cols / maxRadius; + magnitude = rho / Klin; + } + int x = cvRound(center.x + magnitude * cos(angleRad)); + int y = cvRound(center.y + magnitude * sin(angleRad)); + //! [InverseCoordinate] + drawMarker(src, Point(x, y), Scalar(0, 255, 0)); + drawMarker(dst, Point(rho, phi), Scalar(0, 255, 0)); + + +#if 0 //C version + CvMat src = frame; + CvMat dst = lin_polar_img; + CvMat inverse = recovered_lin_polar_img; + cvLinearPolar(&src, &dst, center, maxRadius, flags); + cvLinearPolar(&dst, &inverse, center, maxRadius,flags + WARP_INVERSE_MAP); +#endif + + imshow("Src frame", src); + imshow("Log-Polar", log_polar_img); + imshow("Linear-Polar", lin_polar_img); imshow("Recovered Linear-Polar", recovered_lin_polar_img ); imshow("Recovered Log-Polar", recovered_log_polar ); if( waitKey(10) >= 0 ) break; } - - waitKey(0); return 0; }