diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index 84df297bf9..eeb83c0744 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -2011,6 +2011,11 @@ public: template MatIterator_<_Tp> begin(); template MatConstIterator_<_Tp> begin() const; + /** @brief Same as begin() but for inverse traversal + */ + template std::reverse_iterator> rbegin(); + template std::reverse_iterator> rbegin() const; + /** @brief Returns the matrix iterator and sets it to the after-last matrix element. The methods return the matrix read-only or read-write iterators, set to the point following the last @@ -2019,6 +2024,12 @@ public: template MatIterator_<_Tp> end(); template MatConstIterator_<_Tp> end() const; + /** @brief Same as end() but for inverse traversal + */ + template std::reverse_iterator< MatIterator_<_Tp>> rend(); + template std::reverse_iterator< MatConstIterator_<_Tp>> rend() const; + + /** @brief Runs the given functor over all matrix elements in parallel. The operation passed as argument has to be a function pointer, a function object or a lambda(C++11). @@ -2250,6 +2261,12 @@ public: const_iterator begin() const; const_iterator end() const; + //reverse iterators + std::reverse_iterator rbegin(); + std::reverse_iterator rend(); + std::reverse_iterator rbegin() const; + std::reverse_iterator rend() const; + //! template methods for for operation over all matrix elements. // the operations take care of skipping gaps in the end of rows (if any) template void forEach(const Functor& operation); diff --git a/modules/core/include/opencv2/core/mat.inl.hpp b/modules/core/include/opencv2/core/mat.inl.hpp index f07c820f33..886b82c6a0 100644 --- a/modules/core/include/opencv2/core/mat.inl.hpp +++ b/modules/core/include/opencv2/core/mat.inl.hpp @@ -1015,6 +1015,17 @@ MatConstIterator_<_Tp> Mat::begin() const return MatConstIterator_<_Tp>((const Mat_<_Tp>*)this); } +template inline +std::reverse_iterator> Mat::rbegin() const +{ + if (empty()) + return std::reverse_iterator>(); + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + MatConstIterator_<_Tp> it((const Mat_<_Tp>*)this); + it += total(); + return std::reverse_iterator> (it); +} + template inline MatConstIterator_<_Tp> Mat::end() const { @@ -1026,6 +1037,15 @@ MatConstIterator_<_Tp> Mat::end() const return it; } +template inline +std::reverse_iterator> Mat::rend() const +{ + if (empty()) + return std::reverse_iterator>(); + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return std::reverse_iterator>((const Mat_<_Tp>*)this); +} + template inline MatIterator_<_Tp> Mat::begin() { @@ -1035,6 +1055,17 @@ MatIterator_<_Tp> Mat::begin() return MatIterator_<_Tp>((Mat_<_Tp>*)this); } +template inline +std::reverse_iterator> Mat::rbegin() +{ + if (empty()) + return std::reverse_iterator>(); + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + MatIterator_<_Tp> it((Mat_<_Tp>*)this); + it += total(); + return std::reverse_iterator>(it); +} + template inline MatIterator_<_Tp> Mat::end() { @@ -1046,6 +1077,15 @@ MatIterator_<_Tp> Mat::end() return it; } +template inline +std::reverse_iterator> Mat::rend() +{ + if (empty()) + return std::reverse_iterator>(); + CV_DbgAssert( elemSize() == sizeof(_Tp) ); + return std::reverse_iterator>(MatIterator_<_Tp>((Mat_<_Tp>*)this)); +} + template inline void Mat::forEach(const Functor& operation) { this->forEach_impl<_Tp>(operation); @@ -1713,24 +1753,48 @@ MatConstIterator_<_Tp> Mat_<_Tp>::begin() const return Mat::begin<_Tp>(); } +template inline +std::reverse_iterator> Mat_<_Tp>::rbegin() const +{ + return Mat::rbegin<_Tp>(); +} + template inline MatConstIterator_<_Tp> Mat_<_Tp>::end() const { return Mat::end<_Tp>(); } +template inline +std::reverse_iterator> Mat_<_Tp>::rend() const +{ + return Mat::rend<_Tp>(); +} + template inline MatIterator_<_Tp> Mat_<_Tp>::begin() { return Mat::begin<_Tp>(); } +template inline +std::reverse_iterator> Mat_<_Tp>::rbegin() +{ + return Mat::rbegin<_Tp>(); +} + template inline MatIterator_<_Tp> Mat_<_Tp>::end() { return Mat::end<_Tp>(); } +template inline +std::reverse_iterator> Mat_<_Tp>::rend() +{ + return Mat::rend<_Tp>(); +} + template template inline void Mat_<_Tp>::forEach(const Functor& operation) { Mat::forEach<_Tp, Functor>(operation); diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 9686f77ebc..a5d844e7ad 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -2371,4 +2371,82 @@ TEST(Mat, ptrVecni_20044) EXPECT_EQ(int(6), *(ci)); } +TEST(Mat, reverse_iterator_19967) +{ + // empty iterator (#16855) + cv::Mat m_empty; + EXPECT_NO_THROW(m_empty.rbegin()); + EXPECT_NO_THROW(m_empty.rend()); + EXPECT_TRUE(m_empty.rbegin() == m_empty.rend()); + + // 1D test + std::vector data{0, 1, 2, 3}; + const std::vector sizes_1d{4}; + + //Base class + cv::Mat m_1d(sizes_1d, CV_8U, data.data()); + auto mismatch_it_pair_1d = std::mismatch(data.rbegin(), data.rend(), m_1d.rbegin()); + EXPECT_EQ(mismatch_it_pair_1d.first, data.rend()); // expect no mismatch + EXPECT_EQ(mismatch_it_pair_1d.second, m_1d.rend()); + + //Templated derived class + cv::Mat_ m_1d_t(static_cast(sizes_1d.size()), sizes_1d.data(), data.data()); + auto mismatch_it_pair_1d_t = std::mismatch(data.rbegin(), data.rend(), m_1d_t.rbegin()); + EXPECT_EQ(mismatch_it_pair_1d_t.first, data.rend()); // expect no mismatch + EXPECT_EQ(mismatch_it_pair_1d_t.second, m_1d_t.rend()); + + + // 2D test + const std::vector sizes_2d{2, 2}; + + //Base class + cv::Mat m_2d(sizes_2d, CV_8U, data.data()); + auto mismatch_it_pair_2d = std::mismatch(data.rbegin(), data.rend(), m_2d.rbegin()); + EXPECT_EQ(mismatch_it_pair_2d.first, data.rend()); + EXPECT_EQ(mismatch_it_pair_2d.second, m_2d.rend()); + + //Templated derived class + cv::Mat_ m_2d_t(static_cast(sizes_2d.size()),sizes_2d.data(), data.data()); + auto mismatch_it_pair_2d_t = std::mismatch(data.rbegin(), data.rend(), m_2d_t.rbegin()); + EXPECT_EQ(mismatch_it_pair_2d_t.first, data.rend()); + EXPECT_EQ(mismatch_it_pair_2d_t.second, m_2d_t.rend()); + + // 3D test + std::vector data_3d{0, 1, 2, 3, 4, 5, 6, 7}; + const std::vector sizes_3d{2, 2, 2}; + + //Base class + cv::Mat m_3d(sizes_3d, CV_8U, data_3d.data()); + auto mismatch_it_pair_3d = std::mismatch(data_3d.rbegin(), data_3d.rend(), m_3d.rbegin()); + EXPECT_EQ(mismatch_it_pair_3d.first, data_3d.rend()); + EXPECT_EQ(mismatch_it_pair_3d.second, m_3d.rend()); + + //Templated derived class + cv::Mat_ m_3d_t(static_cast(sizes_3d.size()),sizes_3d.data(), data_3d.data()); + auto mismatch_it_pair_3d_t = std::mismatch(data_3d.rbegin(), data_3d.rend(), m_3d_t.rbegin()); + EXPECT_EQ(mismatch_it_pair_3d_t.first, data_3d.rend()); + EXPECT_EQ(mismatch_it_pair_3d_t.second, m_3d_t.rend()); + + // const test base class + const cv::Mat m_1d_const(sizes_1d, CV_8U, data.data()); + + auto mismatch_it_pair_1d_const = std::mismatch(data.rbegin(), data.rend(), m_1d_const.rbegin()); + EXPECT_EQ(mismatch_it_pair_1d_const.first, data.rend()); // expect no mismatch + EXPECT_EQ(mismatch_it_pair_1d_const.second, m_1d_const.rend()); + + EXPECT_FALSE((std::is_assignable()), uchar>::value)) << "Constness of const iterator violated."; + EXPECT_FALSE((std::is_assignable()), uchar>::value)) << "Constness of const iterator violated."; + + // const test templated dervied class + const cv::Mat_ m_1d_const_t(static_cast(sizes_1d.size()), sizes_1d.data(), data.data()); + + auto mismatch_it_pair_1d_const_t = std::mismatch(data.rbegin(), data.rend(), m_1d_const_t.rbegin()); + EXPECT_EQ(mismatch_it_pair_1d_const_t.first, data.rend()); // expect no mismatch + EXPECT_EQ(mismatch_it_pair_1d_const_t.second, m_1d_const_t.rend()); + + EXPECT_FALSE((std::is_assignable::value)) << "Constness of const iterator violated."; + EXPECT_FALSE((std::is_assignable::value)) << "Constness of const iterator violated."; + +} + }} // namespace