Unverified Commit b8b7ca73 authored by Vadim Pisarevsky's avatar Vadim Pisarevsky Committed by GitHub

Rewite polar transforms (#11323)

* Rewrite polar transformations

- A new wrapPolar function encapsulate both linear and semi-log remap
- Destination size is a parameter or calculated automatically to keep objects size between remapping
- linearPolar and logPolar has been deprecated

* Fix build warning and error in accuracy test

* Fix function name to warpPolar

* Explicitly specify the mapping mode, so we retain all the parameters as non-optional.

Introduces WarpPolarMode enum to specify the mapping mode in flags

* resolves performance warning on windows build

* removed duplicated logPolar and linearPolar implementations
parent e0fef2bc
......@@ -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
......
......@@ -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,
......
......@@ -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);
......
This diff is collapsed.
......@@ -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. */
......@@ -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;
logPolar(frame,log_polar_img, center, M, INTER_LINEAR + WARP_FILL_OUTLIERS);
linearPolar(frame,lin_polar_img, center, M, INTER_LINEAR + WARP_FILL_OUTLIERS);
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);
imshow("Log-Polar", log_polar_img );
imshow("Linear-Polar", lin_polar_img );
Point2f center( (float)src.cols / 2, (float)src.rows / 2 );
double maxRadius = 0.7*min(center.y, center.x);
#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, 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]
// 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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment