diff --git a/modules/core/include/opencv2/core.hpp b/modules/core/include/opencv2/core.hpp index 239c849ead..4031034081 100644 --- a/modules/core/include/opencv2/core.hpp +++ b/modules/core/include/opencv2/core.hpp @@ -1509,7 +1509,7 @@ CV_EXPORTS_W void magnitude(InputArray x, InputArray y, OutputArray magnitude); /** @brief Checks every element of an input array for invalid values. -The functions checkRange check that every array element is neither NaN nor infinite. When minVal \< +The functions checkRange check that every array element is neither NaN nor infinite. When minVal \> -DBL_MAX and maxVal \< DBL_MAX, the functions also check that each value is between minVal and maxVal. In case of multi-channel arrays, each channel is processed independently. If some values are out of range, position of the first outlier is stored in pos (when pos != NULL). Then, the @@ -2906,6 +2906,21 @@ public: }; +static inline +String& operator << (String& out, Ptr fmtd) +{ + fmtd->reset(); + for(const char* str = fmtd->next(); str; str = fmtd->next()) + out += cv::String(str); + return out; +} + +static inline +String& operator << (String& out, const Mat& mtx) +{ + return out << Formatter::get()->format(mtx); +} + //////////////////////////////////////// Algorithm //////////////////////////////////// class CV_EXPORTS Algorithm; diff --git a/modules/core/src/mathfuncs.cpp b/modules/core/src/mathfuncs.cpp index 54c4e6d49a..b07c1eeaf6 100644 --- a/modules/core/src/mathfuncs.cpp +++ b/modules/core/src/mathfuncs.cpp @@ -1571,9 +1571,8 @@ template<> struct mat_type_assotiations static const type max_allowable = INT_MAX; }; -// inclusive maxVal !!! template -bool checkIntegerRange(cv::Mat src, Point& bad_pt, int minVal, int maxVal, double& bad_value) +static bool checkIntegerRange(cv::Mat src, Point& bad_pt, int minVal, int maxVal) { typedef mat_type_assotiations type_ass; @@ -1591,20 +1590,19 @@ bool checkIntegerRange(cv::Mat src, Point& bad_pt, int minVal, int maxVal, doubl for (int j = 0; j < as_one_channel.rows; ++j) for (int i = 0; i < as_one_channel.cols; ++i) { - if (as_one_channel.at(j ,i) < minVal || as_one_channel.at(j ,i) > maxVal) + typename type_ass::type v = as_one_channel.at(j ,i); + if (v < minVal || v > maxVal) { - bad_pt.y = j ; - bad_pt.x = i % src.channels(); - bad_value = as_one_channel.at(j ,i); + bad_pt.y = j; + bad_pt.x = i / src.channels(); return false; } } - bad_value = 0.0; return true; } -typedef bool (*check_range_function)(cv::Mat src, Point& bad_pt, int minVal, int maxVal, double& bad_value); +typedef bool (*check_range_function)(cv::Mat src, Point& bad_pt, int minVal, int maxVal); check_range_function check_range_functions[] = { @@ -1621,15 +1619,16 @@ bool checkRange(InputArray _src, bool quiet, Point* pt, double minVal, double ma if ( src.dims > 2 ) { + CV_Assert(pt == NULL); // no way to provide location info + const Mat* arrays[] = {&src, 0}; Mat planes[1]; NAryMatIterator it(arrays, planes); for ( size_t i = 0; i < it.nplanes; i++, ++it ) { - if (!checkRange( it.planes[0], quiet, pt, minVal, maxVal )) + if (!checkRange( it.planes[0], quiet, NULL, minVal, maxVal )) { - // todo: set index properly return false; } } @@ -1638,20 +1637,19 @@ bool checkRange(InputArray _src, bool quiet, Point* pt, double minVal, double ma int depth = src.depth(); Point badPt(-1, -1); - double badValue = 0; if (depth < CV_32F) { - // see "Bug #1784" - int minVali = minVal<(-INT_MAX - 1) ? (-INT_MAX - 1) : cvFloor(minVal); - int maxVali = maxVal>INT_MAX ? INT_MAX : cvCeil(maxVal) - 1; // checkIntegerRang() use inclusive maxVal + int minVali = minVal <= INT_MIN ? INT_MIN : cvFloor(minVal); + int maxVali = maxVal > INT_MAX ? INT_MAX : cvCeil(maxVal) - 1; - (check_range_functions[depth])(src, badPt, minVali, maxVali, badValue); + (check_range_functions[depth])(src, badPt, minVali, maxVali); } else { int i, loc = 0; - Size size = getContinuousSize( src, src.channels() ); + int cn = src.channels(); + Size size = getContinuousSize( src, cn ); if( depth == CV_32F ) { @@ -1675,8 +1673,8 @@ bool checkRange(InputArray _src, bool quiet, Point* pt, double minVal, double ma if( val < ia || val >= ib ) { - badPt = Point((loc + i) % src.cols, (loc + i) / src.cols); - badValue = ((const float*)isrc)[i]; + int pixelId = (loc + i) / cn; + badPt = Point(pixelId % src.cols, pixelId / src.cols); break; } } @@ -1704,8 +1702,8 @@ bool checkRange(InputArray _src, bool quiet, Point* pt, double minVal, double ma if( val < ia || val >= ib ) { - badPt = Point((loc + i) % src.cols, (loc + i) / src.cols); - badValue = ((const double*)isrc)[i]; + int pixelId = (loc + i) / cn; + badPt = Point(pixelId % src.cols, pixelId / src.cols); break; } } @@ -1718,10 +1716,15 @@ bool checkRange(InputArray _src, bool quiet, Point* pt, double minVal, double ma if( pt ) *pt = badPt; if( !quiet ) + { + cv::String value_str; + value_str << src(cv::Range(badPt.y, badPt.y + 1), cv::Range(badPt.x, badPt.x + 1)); CV_Error_( CV_StsOutOfRange, - ("the value at (%d, %d)=%g is out of range", badPt.x, badPt.y, badValue)); + ("the value at (%d, %d)=%s is out of range [%f, %f)", badPt.x, badPt.y, value_str.c_str(), minVal, maxVal)); + } + return false; } - return badPt.x < 0; + return true; } #ifdef HAVE_OPENCL diff --git a/modules/core/test/test_math.cpp b/modules/core/test/test_math.cpp index b7d6605414..65e3861ed8 100644 --- a/modules/core/test/test_math.cpp +++ b/modules/core/test/test_math.cpp @@ -2502,40 +2502,25 @@ protected: } }; -class Core_CheckRange_Empty : public cvtest::BaseTest -{ -public: - Core_CheckRange_Empty(){} - ~Core_CheckRange_Empty(){} -protected: - virtual void run( int start_from ); -}; - -void Core_CheckRange_Empty::run( int ) +TEST(Core_CheckRange_Empty, accuracy) { cv::Mat m; ASSERT_TRUE( cv::checkRange(m) ); } -TEST(Core_CheckRange_Empty, accuracy) { Core_CheckRange_Empty test; test.safe_run(); } - -class Core_CheckRange_INT_MAX : public cvtest::BaseTest -{ -public: - Core_CheckRange_INT_MAX(){} - ~Core_CheckRange_INT_MAX(){} -protected: - virtual void run( int start_from ); -}; - -void Core_CheckRange_INT_MAX::run( int ) +TEST(Core_CheckRange_INT_MAX, accuracy) { cv::Mat m(3, 3, CV_32SC1, cv::Scalar(INT_MAX)); ASSERT_FALSE( cv::checkRange(m, true, 0, 0, INT_MAX) ); ASSERT_TRUE( cv::checkRange(m) ); } -TEST(Core_CheckRange_INT_MAX, accuracy) { Core_CheckRange_INT_MAX test; test.safe_run(); } +TEST(Core_CheckRange_INT_MAX1, accuracy) +{ + cv::Mat m(3, 3, CV_32SC1, cv::Scalar(INT_MAX)); + ASSERT_TRUE( cv::checkRange(m, true, 0, 0, INT_MAX+1.0f) ); + ASSERT_TRUE( cv::checkRange(m) ); +} template class Core_CheckRange : public testing::Test {}; @@ -2546,13 +2531,30 @@ TYPED_TEST_P(Core_CheckRange, Negative) double min_bound = 4.5; double max_bound = 16.0; - TypeParam data[] = {5, 10, 15, 4, 10, 2, 8, 12, 14}; + TypeParam data[] = {5, 10, 15, 10, 10, 2, 8, 12, 14}; cv::Mat src = cv::Mat(3,3, cv::DataDepth::value, data); cv::Point bad_pt(0, 0); ASSERT_FALSE(checkRange(src, true, &bad_pt, min_bound, max_bound)); - ASSERT_EQ(bad_pt.x, 0); + ASSERT_EQ(bad_pt.x, 2); + ASSERT_EQ(bad_pt.y, 1); +} + +TYPED_TEST_P(Core_CheckRange, Negative3CN) +{ + double min_bound = 4.5; + double max_bound = 16.0; + + TypeParam data[] = { 5, 6, 7, 10, 11, 12, 13, 14, 15, + 10, 11, 12, 10, 11, 12, 2, 5, 6, + 8, 8, 8, 12, 12, 12, 14, 14, 14}; + cv::Mat src = cv::Mat(3,3, CV_MAKETYPE(cv::DataDepth::value, 3), data); + + cv::Point bad_pt(0, 0); + + ASSERT_FALSE(checkRange(src, true, &bad_pt, min_bound, max_bound)); + ASSERT_EQ(bad_pt.x, 2); ASSERT_EQ(bad_pt.y, 1); } @@ -2614,7 +2616,49 @@ TYPED_TEST_P(Core_CheckRange, One) ASSERT_TRUE( checkRange(src2, true, NULL, min_bound, max_bound) ); } -REGISTER_TYPED_TEST_CASE_P(Core_CheckRange, Negative, Positive, Bounds, Zero, One); +TEST(Core_CheckRange, NaN) +{ + float data[] = { 5, 6, 7, 10, 11, 12, 13, 14, 15, + 10, 11, 12, 10, 11, 12, 5, 5, std::numeric_limits::quiet_NaN(), + 8, 8, 8, 12, 12, 12, 14, 14, 14}; + cv::Mat src = cv::Mat(3,3, CV_32FC3, data); + + cv::Point bad_pt(0, 0); + + ASSERT_FALSE(checkRange(src, true, &bad_pt)); + ASSERT_EQ(bad_pt.x, 2); + ASSERT_EQ(bad_pt.y, 1); +} + +TEST(Core_CheckRange, Inf) +{ + float data[] = { 5, 6, 7, 10, 11, 12, 13, 14, 15, + 10, 11, 12, 10, 11, 12, 5, 5, std::numeric_limits::infinity(), + 8, 8, 8, 12, 12, 12, 14, 14, 14}; + cv::Mat src = cv::Mat(3,3, CV_32FC3, data); + + cv::Point bad_pt(0, 0); + + ASSERT_FALSE(checkRange(src, true, &bad_pt)); + ASSERT_EQ(bad_pt.x, 2); + ASSERT_EQ(bad_pt.y, 1); +} + +TEST(Core_CheckRange, Inf_Minus) +{ + float data[] = { 5, 6, 7, 10, 11, 12, 13, 14, 15, + 10, 11, 12, 10, 11, 12, 5, 5, -std::numeric_limits::infinity(), + 8, 8, 8, 12, 12, 12, 14, 14, 14}; + cv::Mat src = cv::Mat(3,3, CV_32FC3, data); + + cv::Point bad_pt(0, 0); + + ASSERT_FALSE(checkRange(src, true, &bad_pt)); + ASSERT_EQ(bad_pt.x, 2); + ASSERT_EQ(bad_pt.y, 1); +} + +REGISTER_TYPED_TEST_CASE_P(Core_CheckRange, Negative, Negative3CN, Positive, Bounds, Zero, One); typedef ::testing::Types mat_data_types; INSTANTIATE_TYPED_TEST_CASE_P(Negative_Test, Core_CheckRange, mat_data_types); diff --git a/modules/hal/include/opencv2/hal/defs.h b/modules/hal/include/opencv2/hal/defs.h index 117ec60469..c9566e3bcb 100644 --- a/modules/hal/include/opencv2/hal/defs.h +++ b/modules/hal/include/opencv2/hal/defs.h @@ -415,7 +415,7 @@ CV_INLINE int cvFloor( double value ) #endif } -/** @brief Rounds floating-point number to the nearest integer not larger than the original. +/** @brief Rounds floating-point number to the nearest integer not smaller than the original. The function computes an integer i such that: \f[i \le \texttt{value} < i+1\f]