diff --git a/modules/stitching/include/opencv2/stitching/detail/warpers.hpp b/modules/stitching/include/opencv2/stitching/detail/warpers.hpp index bc2c6e3546..ff005e8da2 100644 --- a/modules/stitching/include/opencv2/stitching/detail/warpers.hpp +++ b/modules/stitching/include/opencv2/stitching/detail/warpers.hpp @@ -70,6 +70,23 @@ public: */ virtual Point2f warpPoint(const Point2f &pt, InputArray K, InputArray R) = 0; + /** @brief Projects the image point backward. + + @param pt Projected point + @param K Camera intrinsic parameters + @param R Camera rotation matrix + @return Backward-projected point + */ +#if CV_VERSION_MAJOR == 4 + virtual Point2f warpPointBackward(const Point2f& pt, InputArray K, InputArray R) + { + CV_UNUSED(pt); CV_UNUSED(K); CV_UNUSED(R); + CV_Error(Error::StsNotImplemented, ""); + } +#else + virtual Point2f warpPointBackward(const Point2f& pt, InputArray K, InputArray R) = 0; +#endif + /** @brief Builds the projection maps according to the given camera data. @param src_size Source image size @@ -143,6 +160,8 @@ class CV_EXPORTS_TEMPLATE RotationWarperBase : public RotationWarper public: Point2f warpPoint(const Point2f &pt, InputArray K, InputArray R) CV_OVERRIDE; + Point2f warpPointBackward(const Point2f &pt, InputArray K, InputArray R) CV_OVERRIDE; + Rect buildMaps(Size src_size, InputArray K, InputArray R, OutputArray xmap, OutputArray ymap) CV_OVERRIDE; Point warp(InputArray src, InputArray K, InputArray R, int interp_mode, int border_mode, @@ -189,6 +208,9 @@ public: Point2f warpPoint(const Point2f &pt, InputArray K, InputArray R) CV_OVERRIDE; Point2f warpPoint(const Point2f &pt, InputArray K, InputArray R, InputArray T); + Point2f warpPointBackward(const Point2f& pt, InputArray K, InputArray R) CV_OVERRIDE; + Point2f warpPointBackward(const Point2f& pt, InputArray K, InputArray R, InputArray T); + virtual Rect buildMaps(Size src_size, InputArray K, InputArray R, InputArray T, CV_OUT OutputArray xmap, CV_OUT OutputArray ymap); Rect buildMaps(Size src_size, InputArray K, InputArray R, CV_OUT OutputArray xmap, CV_OUT OutputArray ymap) CV_OVERRIDE; @@ -228,6 +250,15 @@ public: */ Point2f warpPoint(const Point2f &pt, InputArray K, InputArray H) CV_OVERRIDE; + /** @brief Projects the image point backward. + + @param pt Projected point + @param K Camera intrinsic parameters + @param H Camera extrinsic parameters + @return Backward-projected point + */ + Point2f warpPointBackward(const Point2f &pt, InputArray K, InputArray H) CV_OVERRIDE; + /** @brief Builds the projection maps according to the given camera data. @param src_size Source image size diff --git a/modules/stitching/include/opencv2/stitching/detail/warpers_inl.hpp b/modules/stitching/include/opencv2/stitching/detail/warpers_inl.hpp index f4a19d9c24..5e2375621e 100644 --- a/modules/stitching/include/opencv2/stitching/detail/warpers_inl.hpp +++ b/modules/stitching/include/opencv2/stitching/detail/warpers_inl.hpp @@ -61,6 +61,14 @@ Point2f RotationWarperBase

::warpPoint(const Point2f &pt, InputArray K, InputA return uv; } +template +Point2f RotationWarperBase

::warpPointBackward(const Point2f& pt, InputArray K, InputArray R) +{ + projector_.setCameraParams(K, R); + Point2f xy; + projector_.mapBackward(pt.x, pt.y, xy.x, xy.y); + return xy; +} template Rect RotationWarperBase

::buildMaps(Size src_size, InputArray K, InputArray R, OutputArray _xmap, OutputArray _ymap) diff --git a/modules/stitching/include/opencv2/stitching/warpers.hpp b/modules/stitching/include/opencv2/stitching/warpers.hpp index ff43386107..aa1ce5a6a7 100644 --- a/modules/stitching/include/opencv2/stitching/warpers.hpp +++ b/modules/stitching/include/opencv2/stitching/warpers.hpp @@ -65,6 +65,22 @@ namespace cv { */ CV_WRAP Point2f warpPoint(const Point2f &pt, InputArray K, InputArray R); + /** @brief Projects the image point backward. + + @param pt Projected point + @param K Camera intrinsic parameters + @param R Camera rotation matrix + @return Backward-projected point + */ +#if CV_VERSION_MAJOR == 4 + CV_WRAP Point2f warpPointBackward(const Point2f& pt, InputArray K, InputArray R) + { + CV_UNUSED(pt); CV_UNUSED(K); CV_UNUSED(R); + CV_Error(Error::StsNotImplemented, ""); + } +#else + CV_WRAP Point2f warpPointBackward(const Point2f &pt, InputArray K, InputArray R); +#endif /** @brief Builds the projection maps according to the given camera data. @param src_size Source image size diff --git a/modules/stitching/src/warpers.cpp b/modules/stitching/src/warpers.cpp index 4360590c94..85ac939074 100644 --- a/modules/stitching/src/warpers.cpp +++ b/modules/stitching/src/warpers.cpp @@ -92,6 +92,14 @@ Point2f PyRotationWarper::warpPoint(const Point2f &pt, InputArray K, InputArray { return rw.get()->warpPoint(pt, K, R); } + +#if CV_VERSION_MAJOR != 4 +Point2f PyRotationWarper::warpPointBackward(const Point2f& pt, InputArray K, InputArray R) +{ + return rw.get()->warpPointBackward(pt, K, R); +} +#endif + Rect PyRotationWarper::buildMaps(Size src_size, InputArray K, InputArray R, OutputArray xmap, OutputArray ymap) { return rw.get()->buildMaps(src_size, K, R, xmap, ymap); @@ -164,6 +172,20 @@ Point2f PlaneWarper::warpPoint(const Point2f &pt, InputArray K, InputArray R) Mat_ T(3, 1, tz); return warpPoint(pt, K, R, T); } +Point2f PlaneWarper::warpPointBackward(const Point2f& pt, InputArray K, InputArray R, InputArray T) +{ + projector_.setCameraParams(K, R, T); + Point2f xy; + projector_.mapBackward(pt.x, pt.y, xy.x, xy.y); + return xy; +} + +Point2f PlaneWarper::warpPointBackward(const Point2f& pt, InputArray K, InputArray R) +{ + float tz[] = { 0.f, 0.f, 0.f }; + Mat_ T(3, 1, tz); + return warpPointBackward(pt, K, R, T); +} Rect PlaneWarper::buildMaps(Size src_size, InputArray K, InputArray R, OutputArray xmap, OutputArray ymap) { @@ -299,6 +321,12 @@ Point2f AffineWarper::warpPoint(const Point2f &pt, InputArray K, InputArray H) return PlaneWarper::warpPoint(pt, K, R, T); } +Point2f AffineWarper::warpPointBackward(const Point2f& pt, InputArray K, InputArray H) +{ + Mat R, T; + getRTfromHomogeneous(H, R, T); + return PlaneWarper::warpPointBackward(pt, K, R, T); +} Rect AffineWarper::buildMaps(Size src_size, InputArray K, InputArray H, OutputArray xmap, OutputArray ymap) { diff --git a/modules/stitching/test/test_reprojection.cpp b/modules/stitching/test/test_reprojection.cpp new file mode 100644 index 0000000000..076bbb769d --- /dev/null +++ b/modules/stitching/test/test_reprojection.cpp @@ -0,0 +1,131 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "test_precomp.hpp" +#include "opencv2/stitching/warpers.hpp" + +namespace opencv_test { namespace { +class ReprojectionTest : public ::testing::Test { + +protected: + const size_t TEST_COUNT = 15; + Mat K, R; + RNG rng = RNG(0); + ReprojectionTest() + { + K = Mat::eye(3, 3, CV_32FC1); + float angle = (float)(30.0 * CV_PI / 180.0); + float rotationMatrix[9] = { + (float)cos(angle), (float)sin(angle), 0, + (float)-sin(angle), (float)cos(angle), 0, + 0, 0, 1 + }; + Mat(3, 3, CV_32FC1, rotationMatrix).copyTo(R); + } + void TestReprojection(Ptr warper, Point2f pt) { + Point2f projected_pt = warper->warpPoint(pt, K, R); + Point2f reprojected_pt = warper->warpPointBackward(projected_pt, K, R); + EXPECT_NEAR(pt.x, reprojected_pt.x, float( 1e-5)); + EXPECT_NEAR(pt.y, reprojected_pt.y, float( 1e-5)); + } +}; + + +TEST_F(ReprojectionTest, PlaneWarper) +{ + Ptr creator = makePtr(); + for (size_t i = 0; i < TEST_COUNT; ++i) { + TestReprojection(creator->create(1), Point2f(rng.uniform(-1.f, 1.f), rng.uniform(-1.f, 1.f))); + } +} + +TEST_F(ReprojectionTest, AffineWarper) +{ + Ptr creator = makePtr(); + for (size_t i = 0; i < TEST_COUNT; ++i) { + TestReprojection(creator->create(1), Point2f(rng.uniform(-1.f, 1.f), rng.uniform(-1.f, 1.f))); + } +} + +TEST_F(ReprojectionTest, CylindricalWarper) +{ + Ptr creator = makePtr(); + for (size_t i = 0; i < TEST_COUNT; ++i) { + TestReprojection(creator->create(1), Point2f(rng.uniform(-1.f, 1.f), rng.uniform(-1.f, 1.f))); + } +} + +TEST_F(ReprojectionTest, SphericalWarper) +{ + Ptr creator = makePtr(); + for (size_t i = 0; i < TEST_COUNT; ++i) { + TestReprojection(creator->create(1), Point2f(rng.uniform(-1.f, 1.f), rng.uniform(-1.f, 1.f))); + } +} + +TEST_F(ReprojectionTest, FisheyeWarper) +{ + Ptr creator = makePtr(); + for (size_t i = 0; i < TEST_COUNT; ++i) { + TestReprojection(creator->create(1), Point2f(rng.uniform(-1.f, 1.f), rng.uniform(-1.f, 1.f))); + } +} + +TEST_F(ReprojectionTest, StereographicWarper) +{ + Ptr creator = makePtr(); + for (size_t i = 0; i < TEST_COUNT; ++i) { + TestReprojection(creator->create(1), Point2f(rng.uniform(-1.f, 1.f), rng.uniform(-1.f, 1.f))); + } +} + +TEST_F(ReprojectionTest, CompressedRectilinearWarper) +{ + Ptr creator = makePtr(1.5f, 1.0f); + for (size_t i = 0; i < TEST_COUNT; ++i) { + TestReprojection(creator->create(1), Point2f(rng.uniform(-1.f, 1.f), rng.uniform(-1.f, 1.f))); + } +} + +TEST_F(ReprojectionTest, CompressedRectilinearPortraitWarper) +{ + Ptr creator = makePtr(1.5f, 1.0f); + for (size_t i = 0; i < TEST_COUNT; ++i) { + TestReprojection(creator->create(1), Point2f(rng.uniform(-1.f, 1.f), rng.uniform(-1.f, 1.f))); + } +} + +TEST_F(ReprojectionTest, PaniniWarper) +{ + Ptr creator = makePtr(1.5f, 1.0f); + for (size_t i = 0; i < TEST_COUNT; ++i) { + TestReprojection(creator->create(1), Point2f(rng.uniform(-1.f, 1.f), rng.uniform(-1.f, 1.f))); + } +} + +TEST_F(ReprojectionTest, PaniniPortraitWarper) +{ + Ptr creator = makePtr(1.5f, 1.0f); + for (size_t i = 0; i < TEST_COUNT; ++i) { + TestReprojection(creator->create(1), Point2f(rng.uniform(-1.f, 1.f), rng.uniform(-1.f, 1.f))); + } +} + +TEST_F(ReprojectionTest, MercatorWarper) +{ + Ptr creator = makePtr(); + for (size_t i = 0; i < TEST_COUNT; ++i) { + TestReprojection(creator->create(1), Point2f(rng.uniform(-1.f, 1.f), rng.uniform(-1.f, 1.f))); + } +} + +TEST_F(ReprojectionTest, TransverseMercatorWarper) +{ + Ptr creator = makePtr(); + for (size_t i = 0; i < TEST_COUNT; ++i) { + TestReprojection(creator->create(1), Point2f(rng.uniform(-1.f, 1.f), rng.uniform(-1.f, 1.f))); + } +} + +}} // namespace