From a35d4f90297107c96785c8bb5853512ab58b163f Mon Sep 17 00:00:00 2001 From: Liubov Batanina Date: Mon, 20 Jul 2020 12:04:20 +0300 Subject: [PATCH 1/9] Support Gather for variable inputs --- modules/dnn/src/onnx/onnx_importer.cpp | 58 +++++++++++++++++++------ modules/dnn/test/test_onnx_importer.cpp | 11 +++++ 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 682418bffa..220cae813e 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -1342,32 +1342,64 @@ void ONNXImporter::populateNet(Net dstNet) else if (layer_type == "Gather") { CV_Assert(node_proto.input_size() == 2); - Mat input = getBlob(node_proto, constBlobs, 0); Mat indexMat = getBlob(node_proto, constBlobs, 1); CV_Assert_N(indexMat.type() == CV_32S, indexMat.total() == 1); int index = indexMat.at(0); + int axis = layerParams.get("axis", 0); - Mat out; - if (layerParams.has("axis")) + if ((constBlobs.find(node_proto.input(0)) != constBlobs.end())) { - int axis = layerParams.get("axis"); - + Mat input = getBlob(node_proto, constBlobs, 0); + Mat out; std::vector ranges(input.dims, Range::all()); ranges[axis] = Range(index, index + 1); out = input(ranges); + MatShape outShape = shape(out); + if (outShape.size() > 1) + { + outShape.erase(outShape.begin() + axis); + out.reshape(0, outShape); + } + addConstant(layerParams.name, out, constBlobs, outShapes); + continue; } else { - CV_Assert(index < input.total()); - const int dims = input.dims; - input = input.reshape(1, 1); - input.dims = 2; - out = input.reshape(1, 1).colRange(index, index + 1); - out.dims = dims; + shapeIt = outShapes.find(node_proto.input(0)); + CV_Assert(shapeIt != outShapes.end()); + MatShape inpShape = shapeIt->second; + + LayerParams sliceLp; + sliceLp.type = "Slice"; + sliceLp.name = inpShape.size() > 1 ? layerParams.name + "/slice" : layerParams.name; + std::vector begin(inpShape.size(), 0); + std::vector end(inpShape.size(), -1); + begin[axis] = index; + end[axis] = index + 1; + + cv::dnn::DictValue paramBegin = cv::dnn::DictValue::arrayInt(begin.data(), begin.size()); + cv::dnn::DictValue paramEnd = cv::dnn::DictValue::arrayInt(end.data(), end.size()); + sliceLp.set("begin", paramBegin); + sliceLp.set("end", paramEnd); + + if (inpShape.size() > 1) + { + opencv_onnx::NodeProto proto; + proto.add_input(node_proto.input(0)); + proto.add_output(sliceLp.name); + addLayer(dstNet, sliceLp, proto, layer_id, outShapes); + + inpShape.erase(inpShape.begin() + axis); + layerParams.type = "Reshape"; + layerParams.set("dim", DictValue::arrayInt(&inpShape[0], inpShape.size())); + node_proto.set_input(0, sliceLp.name); + } + else + { + layerParams = sliceLp; + } } - addConstant(layerParams.name, out, constBlobs, outShapes); - continue; } else if (layer_type == "Concat") { diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index 4c8e66aae1..e932bc6919 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -111,6 +111,17 @@ TEST_P(Test_ONNX_layers, Convolution) testONNXModels("convolution"); } +TEST_P(Test_ONNX_layers, Gather) +{ + if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && target == DNN_TARGET_MYRIAD) + applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER); + testONNXModels("gather"); + // GPU plugin unsupported slice for constant + if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && (target == DNN_TARGET_OPENCL || target == DNN_TARGET_OPENCL_FP16)) + applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_OPENCL, CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH); + testONNXModels("gather_scalar", npy, 0, 0, false, false); +} + TEST_P(Test_ONNX_layers, Convolution3D) { #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_LT(2019010000) From 9411cd6c07687539cc4a7d68f54a8fa573215fc0 Mon Sep 17 00:00:00 2001 From: Namgoo Lee Date: Tue, 21 Jul 2020 10:27:43 +0900 Subject: [PATCH 2/9] Use in-place npp function for inplace arguments --- modules/cudaarithm/src/core.cpp | 44 ++++++++++++++++++++++++++- modules/cudaarithm/test/test_core.cpp | 13 ++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/modules/cudaarithm/src/core.cpp b/modules/cudaarithm/src/core.cpp index 6d97e15dbb..ac01afc7f0 100644 --- a/modules/cudaarithm/src/core.cpp +++ b/modules/cudaarithm/src/core.cpp @@ -102,6 +102,34 @@ namespace cudaSafeCall( cudaDeviceSynchronize() ); } }; + + template struct NppMirrorIFunc + { + typedef typename NppTypeTraits::npp_t npp_t; + + typedef NppStatus (*func_t)(npp_t* pSrcDst, int nSrcDstStep, NppiSize oROI, NppiAxis flip); + }; + + template ::func_t func> struct NppMirrorI + { + typedef typename NppMirrorIFunc::npp_t npp_t; + + static void call(GpuMat& srcDst, int flipCode, cudaStream_t stream) + { + NppStreamHandler h(stream); + + NppiSize sz; + sz.width = srcDst.cols; + sz.height = srcDst.rows; + + nppSafeCall( func(srcDst.ptr(), static_cast(srcDst.step), + sz, + (flipCode == 0 ? NPP_HORIZONTAL_AXIS : (flipCode > 0 ? NPP_VERTICAL_AXIS : NPP_BOTH_AXIS))) ); + + if (stream == 0) + cudaSafeCall( cudaDeviceSynchronize() ); + } + }; } void cv::cuda::flip(InputArray _src, OutputArray _dst, int flipCode, Stream& stream) @@ -117,6 +145,17 @@ void cv::cuda::flip(InputArray _src, OutputArray _dst, int flipCode, Stream& str {NppMirror::call, 0, NppMirror::call, NppMirror::call} }; + typedef void (*ifunc_t)(GpuMat& srcDst, int flipCode, cudaStream_t stream); + static const ifunc_t ifuncs[6][4] = + { + {NppMirrorI::call, 0, NppMirrorI::call, NppMirrorI::call}, + {0,0,0,0}, + {NppMirrorI::call, 0, NppMirrorI::call, NppMirrorI::call}, + {0,0,0,0}, + {NppMirrorI::call, 0, NppMirrorI::call, NppMirrorI::call}, + {NppMirrorI::call, 0, NppMirrorI::call, NppMirrorI::call} + }; + GpuMat src = getInputMat(_src, stream); CV_Assert(src.depth() == CV_8U || src.depth() == CV_16U || src.depth() == CV_32S || src.depth() == CV_32F); @@ -125,7 +164,10 @@ void cv::cuda::flip(InputArray _src, OutputArray _dst, int flipCode, Stream& str _dst.create(src.size(), src.type()); GpuMat dst = getOutputMat(_dst, src.size(), src.type(), stream); - funcs[src.depth()][src.channels() - 1](src, dst, flipCode, StreamAccessor::getStream(stream)); + if (src.refcount != dst.refcount) + funcs[src.depth()][src.channels() - 1](src, dst, flipCode, StreamAccessor::getStream(stream)); + else // in-place + ifuncs[src.depth()][src.channels() - 1](src, flipCode, StreamAccessor::getStream(stream)); syncOutput(dst, _dst, stream); } diff --git a/modules/cudaarithm/test/test_core.cpp b/modules/cudaarithm/test/test_core.cpp index 7e5762aa3f..bc8f3737e5 100644 --- a/modules/cudaarithm/test/test_core.cpp +++ b/modules/cudaarithm/test/test_core.cpp @@ -279,6 +279,19 @@ CUDA_TEST_P(Flip, Accuracy) EXPECT_MAT_NEAR(dst_gold, dst, 0.0); } +CUDA_TEST_P(Flip, AccuracyInplace) +{ + cv::Mat src = randomMat(size, type); + + cv::cuda::GpuMat srcDst = loadMat(src, useRoi); + cv::cuda::flip(srcDst, srcDst, flip_code); + + cv::Mat dst_gold; + cv::flip(src, dst_gold, flip_code); + + EXPECT_MAT_NEAR(dst_gold, srcDst, 0.0); +} + INSTANTIATE_TEST_CASE_P(CUDA_Arithm, Flip, testing::Combine( ALL_DEVICES, DIFFERENT_SIZES, From abceef74e0a5e7bbd98cf09d87912fec26cc8726 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 22 Jul 2020 10:05:55 +0300 Subject: [PATCH 3/9] Added reference to Original Wu's articte about SAUF connected components search method. --- doc/opencv.bib | 13 +++++++++++++ modules/imgproc/include/opencv2/imgproc.hpp | 6 +++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/doc/opencv.bib b/doc/opencv.bib index 73c4668ff8..bdfbc8cf1e 100644 --- a/doc/opencv.bib +++ b/doc/opencv.bib @@ -1215,3 +1215,16 @@ year = {1996}, publisher = {Elsevier} } +@Article{Wu2009, + author={Wu, Kesheng + and Otoo, Ekow + and Suzuki, Kenji}, + title={Optimizing two-pass connected-component labeling algorithms}, + journal={Pattern Analysis and Applications}, + year={2009}, + month={Jun}, + day={01}, + volume={12}, + number={2}, + pages={117-135}, +} diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 91c1ec52fc..962501b62d 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -403,7 +403,7 @@ enum ConnectedComponentsTypes { //! connected components algorithm enum ConnectedComponentsAlgorithmsTypes { - CCL_WU = 0, //!< SAUF algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity + CCL_WU = 0, //!< SAUF @cite Wu2009 algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity CCL_DEFAULT = -1, //!< BBDT algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity CCL_GRANA = 1 //!< BBDT algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity }; @@ -3842,7 +3842,7 @@ image with 4 or 8 way connectivity - returns N, the total number of labels [0, N represents the background label. ltype specifies the output label image type, an important consideration based on the total number of labels or alternatively the total number of pixels in the source image. ccltype specifies the connected components labeling algorithm to use, currently -Grana (BBDT) and Wu's (SAUF) algorithms are supported, see the #ConnectedComponentsAlgorithmsTypes +Grana (BBDT) and Wu's (SAUF) @cite Wu2009 algorithms are supported, see the #ConnectedComponentsAlgorithmsTypes for details. Note that SAUF algorithm forces a row major ordering of labels while BBDT does not. This function uses parallel version of both Grana and Wu's algorithms if at least one allowed parallel framework is enabled and if the rows of the image are at least twice the number returned by #getNumberOfCPUs. @@ -3874,7 +3874,7 @@ image with 4 or 8 way connectivity - returns N, the total number of labels [0, N represents the background label. ltype specifies the output label image type, an important consideration based on the total number of labels or alternatively the total number of pixels in the source image. ccltype specifies the connected components labeling algorithm to use, currently -Grana's (BBDT) and Wu's (SAUF) algorithms are supported, see the #ConnectedComponentsAlgorithmsTypes +Grana's (BBDT) and Wu's (SAUF) @cite Wu2009 algorithms are supported, see the #ConnectedComponentsAlgorithmsTypes for details. Note that SAUF algorithm forces a row major ordering of labels while BBDT does not. This function uses parallel version of both Grana and Wu's algorithms (statistics included) if at least one allowed parallel framework is enabled and if the rows of the image are at least twice the number returned by #getNumberOfCPUs. From 0ac2f0e04ce014caa7d7a7f0076e561ece90eacf Mon Sep 17 00:00:00 2001 From: Sinitsina Date: Wed, 22 Jul 2020 21:26:27 +0300 Subject: [PATCH 4/9] mish_functor_update --- modules/dnn/src/layers/elementwise_layers.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/dnn/src/layers/elementwise_layers.cpp b/modules/dnn/src/layers/elementwise_layers.cpp index 1bd8abef87..d53393c3e0 100644 --- a/modules/dnn/src/layers/elementwise_layers.cpp +++ b/modules/dnn/src/layers/elementwise_layers.cpp @@ -669,9 +669,14 @@ struct MishFunctor : public BaseFunctor { // Use fast approximation introduced in https://github.com/opencv/opencv/pull/17200 float x = srcptr[i]; - float eX = exp(std::min(x, 20.f)); - float n = (eX + 2) * eX; - dstptr[i] = (x * n) / (n + 2); + if (x >= 8.f) + dstptr[i] = x; + else + { + float eX = exp(x); + float n = (eX + 2) * eX; + dstptr[i] = (x * n) / (n + 2); + } } } } From f0271e54d90b3af62301f531f5f00995b00d7cd6 Mon Sep 17 00:00:00 2001 From: Alexey Lapshin Date: Tue, 2 Jun 2020 18:34:52 +0300 Subject: [PATCH 5/9] Autorotation for mp4 streams with metadata - Add VideoCapture camera orientation property for mp4 videos with camera orientation meta. - Add auto rotation for 90, 180, 270 degrees using cv::rotate --- modules/videoio/include/opencv2/videoio.hpp | 2 + modules/videoio/src/cap_ffmpeg.cpp | 11 ++-- modules/videoio/src/cap_ffmpeg_api.hpp | 10 +-- modules/videoio/src/cap_ffmpeg_impl.hpp | 73 +++++++++++++++------ 4 files changed, 67 insertions(+), 29 deletions(-) diff --git a/modules/videoio/include/opencv2/videoio.hpp b/modules/videoio/include/opencv2/videoio.hpp index 38a451b244..8b98ca9a17 100644 --- a/modules/videoio/include/opencv2/videoio.hpp +++ b/modules/videoio/include/opencv2/videoio.hpp @@ -177,6 +177,8 @@ enum VideoCaptureProperties { CAP_PROP_WB_TEMPERATURE=45, //!< white-balance color temperature CAP_PROP_CODEC_PIXEL_FORMAT =46, //!< (read-only) codec's pixel format. 4-character code - see VideoWriter::fourcc . Subset of [AV_PIX_FMT_*](https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/raw.c) or -1 if unknown CAP_PROP_BITRATE =47, //!< (read-only) Video bitrate in kbits/s + CAP_PROP_ORIENTATION_META=48, //!< (read-only) Frame rotation defined by stream meta (applicable for FFmpeg back-end only) + CAP_PROP_ORIENTATION_AUTO=49, //!< if true - rotates output frames of CvCapture considering video file's metadata (applicable for FFmpeg back-end only) (https://github.com/opencv/opencv/issues/15499) #ifndef CV_DOXYGEN CV__CAP_PROP_LATEST #endif diff --git a/modules/videoio/src/cap_ffmpeg.cpp b/modules/videoio/src/cap_ffmpeg.cpp index 67ee2e636d..a4aed6c741 100644 --- a/modules/videoio/src/cap_ffmpeg.cpp +++ b/modules/videoio/src/cap_ffmpeg.cpp @@ -229,13 +229,12 @@ public: } virtual bool retrieveFrame(int, cv::OutputArray frame) CV_OVERRIDE { - unsigned char* data = 0; - int step=0, width=0, height=0, cn=0; - + cv::Mat mat; if (!ffmpegCapture || - !icvRetrieveFrame_FFMPEG_p(ffmpegCapture, &data, &step, &width, &height, &cn)) + !icvRetrieveFrame_FFMPEG_p(ffmpegCapture, mat)) return false; - cv::Mat(height, width, CV_MAKETYPE(CV_8U, cn), data, step).copyTo(frame); + + mat.copyTo(frame); return true; } virtual bool open( const cv::String& filename ) @@ -262,6 +261,8 @@ public: protected: CvCapture_FFMPEG* ffmpegCapture; +private: + }; } // namespace diff --git a/modules/videoio/src/cap_ffmpeg_api.hpp b/modules/videoio/src/cap_ffmpeg_api.hpp index 004758099c..c132fa1d4f 100644 --- a/modules/videoio/src/cap_ffmpeg_api.hpp +++ b/modules/videoio/src/cap_ffmpeg_api.hpp @@ -28,7 +28,9 @@ enum CV_FFMPEG_CAP_PROP_SAR_NUM=40, CV_FFMPEG_CAP_PROP_SAR_DEN=41, CV_FFMPEG_CAP_PROP_CODEC_PIXEL_FORMAT=46, - CV_FFMPEG_CAP_PROP_BITRATE=47 + CV_FFMPEG_CAP_PROP_BITRATE=47, + CV_FFMPEG_CAP_PROP_ORIENTATION_META=48, + CV_FFMPEG_CAP_PROP_ORIENTATION_AUTO=49 }; typedef struct CvCapture_FFMPEG CvCapture_FFMPEG; @@ -39,8 +41,7 @@ OPENCV_FFMPEG_API int cvSetCaptureProperty_FFMPEG(struct CvCapture_FFMPEG* cap, int prop, double value); OPENCV_FFMPEG_API double cvGetCaptureProperty_FFMPEG(struct CvCapture_FFMPEG* cap, int prop); OPENCV_FFMPEG_API int cvGrabFrame_FFMPEG(struct CvCapture_FFMPEG* cap); -OPENCV_FFMPEG_API int cvRetrieveFrame_FFMPEG(struct CvCapture_FFMPEG* capture, unsigned char** data, - int* step, int* width, int* height, int* cn); +OPENCV_FFMPEG_API int cvRetrieveFrame_FFMPEG(struct CvCapture_FFMPEG* capture, cv::Mat &mat); OPENCV_FFMPEG_API void cvReleaseCapture_FFMPEG(struct CvCapture_FFMPEG** cap); OPENCV_FFMPEG_API struct CvVideoWriter_FFMPEG* cvCreateVideoWriter_FFMPEG(const char* filename, @@ -52,8 +53,7 @@ OPENCV_FFMPEG_API void cvReleaseVideoWriter_FFMPEG(struct CvVideoWriter_FFMPEG** typedef CvCapture_FFMPEG* (*CvCreateFileCapture_Plugin)( const char* filename ); typedef CvCapture_FFMPEG* (*CvCreateCameraCapture_Plugin)( int index ); typedef int (*CvGrabFrame_Plugin)( CvCapture_FFMPEG* capture_handle ); -typedef int (*CvRetrieveFrame_Plugin)( CvCapture_FFMPEG* capture_handle, unsigned char** data, int* step, - int* width, int* height, int* cn ); +typedef int (*CvRetrieveFrame_Plugin)( CvCapture_FFMPEG* capture_handle, cv::Mat &mat); typedef int (*CvSetCaptureProperty_Plugin)( CvCapture_FFMPEG* capture_handle, int prop_id, double value ); typedef double (*CvGetCaptureProperty_Plugin)( CvCapture_FFMPEG* capture_handle, int prop_id ); typedef void (*CvReleaseCapture_Plugin)( CvCapture_FFMPEG** capture_handle ); diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index fae85425b0..2b946a738e 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -481,7 +481,8 @@ struct CvCapture_FFMPEG double getProperty(int) const; bool setProperty(int, double); bool grabFrame(); - bool retrieveFrame(int, unsigned char** data, int* step, int* width, int* height, int* cn); + bool retrieveFrame(int, cv::Mat &mat); + void rotateFrame(cv::Mat &mat) const; void init(); @@ -497,6 +498,7 @@ struct CvCapture_FFMPEG double r2d(AVRational r) const; int64_t dts_to_frame_number(int64_t dts); double dts_to_sec(int64_t dts) const; + void get_rotation_angle(); AVFormatContext * ic; AVCodec * avcodec; @@ -512,6 +514,8 @@ struct CvCapture_FFMPEG int64_t frame_number, first_frame_number; + bool rotation_auto; + int rotation_angle; // valid 0, 90, 180, 270 double eps_zero; /* 'filename' contains the filename of the videosource, @@ -560,6 +564,9 @@ void CvCapture_FFMPEG::init() frame_number = 0; eps_zero = 0.000025; + rotation_auto = true; + rotation_angle = 0; + #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0) dict = NULL; #endif @@ -1032,6 +1039,7 @@ bool CvCapture_FFMPEG::open( const char* _filename ) frame.cn = 3; frame.step = 0; frame.data = NULL; + get_rotation_angle(); break; } } @@ -1279,8 +1287,7 @@ bool CvCapture_FFMPEG::grabFrame() return valid; } - -bool CvCapture_FFMPEG::retrieveFrame(int, unsigned char** data, int* step, int* width, int* height, int* cn) +bool CvCapture_FFMPEG::retrieveFrame(int, cv::Mat &mat) { if (!video_st) return false; @@ -1288,12 +1295,11 @@ bool CvCapture_FFMPEG::retrieveFrame(int, unsigned char** data, int* step, int* if (rawMode) { AVPacket& p = bsfc ? packet_filtered : packet; - *data = p.data; - *step = p.size; - *width = p.size; - *height = 1; - *cn = 1; - return p.data != NULL; + if (p.data == NULL) + return false; + + mat = cv::Mat(1, p.size, CV_MAKETYPE(CV_8U, 1), p.data, p.size); + return true; } if (!picture->data[0]) @@ -1356,15 +1362,29 @@ bool CvCapture_FFMPEG::retrieveFrame(int, unsigned char** data, int* step, int* rgb_picture.linesize ); - *data = frame.data; - *step = frame.step; - *width = frame.width; - *height = frame.height; - *cn = frame.cn; - + mat = cv::Mat(frame.height, frame.width, CV_MAKETYPE(CV_8U, frame.cn), frame.data, frame.step); + rotateFrame(mat); return true; } +void CvCapture_FFMPEG::rotateFrame(cv::Mat &mat) const { + if(!rotation_auto || rotation_angle%360 == 0) { + return; + } + + cv::RotateFlags flag; + if(rotation_angle == 90 || rotation_angle == -270) { // Rotate clockwise 90 degrees + flag = cv::ROTATE_90_CLOCKWISE; + } else if(rotation_angle == 270 || rotation_angle == -90) { // Rotate clockwise 270 degrees + flag = cv::ROTATE_90_COUNTERCLOCKWISE; + } else if(rotation_angle == 180 || rotation_angle == -180) { // Rotate clockwise 180 degrees + flag = cv::ROTATE_180; + } else { // Unsupported rotation + return; + } + + cv::rotate(mat, mat, flag); +} double CvCapture_FFMPEG::getProperty( int property_id ) const { @@ -1389,9 +1409,9 @@ double CvCapture_FFMPEG::getProperty( int property_id ) const case CV_FFMPEG_CAP_PROP_FRAME_COUNT: return (double)get_total_frames(); case CV_FFMPEG_CAP_PROP_FRAME_WIDTH: - return (double)frame.width; + return (double)((rotation_auto && rotation_angle%360) ? frame.height : frame.width); case CV_FFMPEG_CAP_PROP_FRAME_HEIGHT: - return (double)frame.height; + return (double)((rotation_auto && rotation_angle%360) ? frame.width : frame.height); case CV_FFMPEG_CAP_PROP_FPS: return get_fps(); case CV_FFMPEG_CAP_PROP_FOURCC: @@ -1435,6 +1455,10 @@ double CvCapture_FFMPEG::getProperty( int property_id ) const break; case CV_FFMPEG_CAP_PROP_BITRATE: return static_cast(get_bitrate()); + case CV_FFMPEG_CAP_PROP_ORIENTATION_META: + return static_cast(rotation_angle); + case CV_FFMPEG_CAP_PROP_ORIENTATION_AUTO: + return static_cast(rotation_auto); default: break; } @@ -1513,6 +1537,14 @@ double CvCapture_FFMPEG::dts_to_sec(int64_t dts) const r2d(ic->streams[video_stream]->time_base); } +void CvCapture_FFMPEG::get_rotation_angle() +{ + rotation_angle = 0; + AVDictionaryEntry *rotate_tag = av_dict_get(video_st->metadata, "rotate", NULL, 0); + if (rotate_tag != NULL) + rotation_angle = atoi(rotate_tag->value); +} + void CvCapture_FFMPEG::seek(int64_t _frame_number) { _frame_number = std::min(_frame_number, get_total_frames()); @@ -1608,6 +1640,9 @@ bool CvCapture_FFMPEG::setProperty( int property_id, double value ) if (value == -1) return setRaw(); return false; + case CV_FFMPEG_CAP_PROP_ORIENTATION_AUTO: + rotation_auto = static_cast(value); + break; default: return false; } @@ -2635,9 +2670,9 @@ int cvGrabFrame_FFMPEG(CvCapture_FFMPEG* capture) return capture->grabFrame(); } -int cvRetrieveFrame_FFMPEG(CvCapture_FFMPEG* capture, unsigned char** data, int* step, int* width, int* height, int* cn) +int cvRetrieveFrame_FFMPEG(CvCapture_FFMPEG* capture, cv::Mat &mat) { - return capture->retrieveFrame(0, data, step, width, height, cn); + return capture->retrieveFrame(0, mat); } CvVideoWriter_FFMPEG* cvCreateVideoWriter_FFMPEG( const char* filename, int fourcc, double fps, From 7ed37b3fa523fc0d3903617a954635f0b0d40e1a Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 8 Jun 2020 17:31:18 +0300 Subject: [PATCH 6/9] MP4 autorotation tests and various fixes for Windows - Added test for automated rotation for MP4 videos with metadata - Fix 180 degrees rotation bug - Moved rotation logic to cv::VideoCapture implementation for FFmpeg and restore binary compatibility with FFmpeg wrapper. --- modules/videoio/src/cap_ffmpeg.cpp | 35 ++++++++++-- modules/videoio/src/cap_ffmpeg_api.hpp | 6 +- modules/videoio/src/cap_ffmpeg_impl.hpp | 75 ++++++++++++++----------- modules/videoio/test/test_ffmpeg.cpp | 52 +++++++++++++++++ 4 files changed, 128 insertions(+), 40 deletions(-) diff --git a/modules/videoio/src/cap_ffmpeg.cpp b/modules/videoio/src/cap_ffmpeg.cpp index a4aed6c741..9d93637c06 100644 --- a/modules/videoio/src/cap_ffmpeg.cpp +++ b/modules/videoio/src/cap_ffmpeg.cpp @@ -229,12 +229,17 @@ public: } virtual bool retrieveFrame(int, cv::OutputArray frame) CV_OVERRIDE { - cv::Mat mat; + unsigned char* data = 0; + int step=0, width=0, height=0, cn=0; + if (!ffmpegCapture || - !icvRetrieveFrame_FFMPEG_p(ffmpegCapture, mat)) + !icvRetrieveFrame_FFMPEG_p(ffmpegCapture, &data, &step, &width, &height, &cn)) return false; - mat.copyTo(frame); + cv::Mat tmp(height, width, CV_MAKETYPE(CV_8U, cn), data, step); + this->rotateFrame(tmp); + tmp.copyTo(frame); + return true; } virtual bool open( const cv::String& filename ) @@ -261,8 +266,30 @@ public: protected: CvCapture_FFMPEG* ffmpegCapture; -private: + void rotateFrame(cv::Mat &mat) const + { + bool rotation_auto = 0 != getProperty(CV_FFMPEG_CAP_PROP_ORIENTATION_AUTO); + int rotation_angle = static_cast(getProperty(CV_FFMPEG_CAP_PROP_ORIENTATION_META)); + + if(!rotation_auto || rotation_angle%360 == 0) + { + return; + } + + cv::RotateFlags flag; + if(rotation_angle == 90 || rotation_angle == -270) { // Rotate clockwise 90 degrees + flag = cv::ROTATE_90_CLOCKWISE; + } else if(rotation_angle == 270 || rotation_angle == -90) { // Rotate clockwise 270 degrees + flag = cv::ROTATE_90_COUNTERCLOCKWISE; + } else if(rotation_angle == 180 || rotation_angle == -180) { // Rotate clockwise 180 degrees + flag = cv::ROTATE_180; + } else { // Unsupported rotation + return; + } + + cv::rotate(mat, mat, flag); + } }; } // namespace diff --git a/modules/videoio/src/cap_ffmpeg_api.hpp b/modules/videoio/src/cap_ffmpeg_api.hpp index c132fa1d4f..984d36f23c 100644 --- a/modules/videoio/src/cap_ffmpeg_api.hpp +++ b/modules/videoio/src/cap_ffmpeg_api.hpp @@ -41,7 +41,8 @@ OPENCV_FFMPEG_API int cvSetCaptureProperty_FFMPEG(struct CvCapture_FFMPEG* cap, int prop, double value); OPENCV_FFMPEG_API double cvGetCaptureProperty_FFMPEG(struct CvCapture_FFMPEG* cap, int prop); OPENCV_FFMPEG_API int cvGrabFrame_FFMPEG(struct CvCapture_FFMPEG* cap); -OPENCV_FFMPEG_API int cvRetrieveFrame_FFMPEG(struct CvCapture_FFMPEG* capture, cv::Mat &mat); +OPENCV_FFMPEG_API int cvRetrieveFrame_FFMPEG(struct CvCapture_FFMPEG* capture, unsigned char** data, + int* step, int* width, int* height, int* cn); OPENCV_FFMPEG_API void cvReleaseCapture_FFMPEG(struct CvCapture_FFMPEG** cap); OPENCV_FFMPEG_API struct CvVideoWriter_FFMPEG* cvCreateVideoWriter_FFMPEG(const char* filename, @@ -53,7 +54,8 @@ OPENCV_FFMPEG_API void cvReleaseVideoWriter_FFMPEG(struct CvVideoWriter_FFMPEG** typedef CvCapture_FFMPEG* (*CvCreateFileCapture_Plugin)( const char* filename ); typedef CvCapture_FFMPEG* (*CvCreateCameraCapture_Plugin)( int index ); typedef int (*CvGrabFrame_Plugin)( CvCapture_FFMPEG* capture_handle ); -typedef int (*CvRetrieveFrame_Plugin)( CvCapture_FFMPEG* capture_handle, cv::Mat &mat); +typedef int (*CvRetrieveFrame_Plugin)( CvCapture_FFMPEG* capture_handle, unsigned char** data, int* step, + int* width, int* height, int* cn ); typedef int (*CvSetCaptureProperty_Plugin)( CvCapture_FFMPEG* capture_handle, int prop_id, double value ); typedef double (*CvGetCaptureProperty_Plugin)( CvCapture_FFMPEG* capture_handle, int prop_id ); typedef void (*CvReleaseCapture_Plugin)( CvCapture_FFMPEG** capture_handle ); diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index 2b946a738e..b3cd2e5a9c 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -481,7 +481,7 @@ struct CvCapture_FFMPEG double getProperty(int) const; bool setProperty(int, double); bool grabFrame(); - bool retrieveFrame(int, cv::Mat &mat); + bool retrieveFrame(int, unsigned char** data, int* step, int* width, int* height, int* cn); void rotateFrame(cv::Mat &mat) const; void init(); @@ -564,11 +564,17 @@ void CvCapture_FFMPEG::init() frame_number = 0; eps_zero = 0.000025; - rotation_auto = true; rotation_angle = 0; -#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0) +#if (LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)) +#if (LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 92, 100)) + rotation_auto = true; +#else + rotation_auto = false; +#endif dict = NULL; +#else + rotation_auto = false; #endif rawMode = false; @@ -1287,7 +1293,7 @@ bool CvCapture_FFMPEG::grabFrame() return valid; } -bool CvCapture_FFMPEG::retrieveFrame(int, cv::Mat &mat) +bool CvCapture_FFMPEG::retrieveFrame(int, unsigned char** data, int* step, int* width, int* height, int* cn) { if (!video_st) return false; @@ -1295,11 +1301,12 @@ bool CvCapture_FFMPEG::retrieveFrame(int, cv::Mat &mat) if (rawMode) { AVPacket& p = bsfc ? packet_filtered : packet; - if (p.data == NULL) - return false; - - mat = cv::Mat(1, p.size, CV_MAKETYPE(CV_8U, 1), p.data, p.size); - return true; + *data = p.data; + *step = p.size; + *width = p.size; + *height = 1; + *cn = 1; + return p.data != NULL; } if (!picture->data[0]) @@ -1362,30 +1369,15 @@ bool CvCapture_FFMPEG::retrieveFrame(int, cv::Mat &mat) rgb_picture.linesize ); - mat = cv::Mat(frame.height, frame.width, CV_MAKETYPE(CV_8U, frame.cn), frame.data, frame.step); - rotateFrame(mat); + *data = frame.data; + *step = frame.step; + *width = frame.width; + *height = frame.height; + *cn = frame.cn; + return true; } -void CvCapture_FFMPEG::rotateFrame(cv::Mat &mat) const { - if(!rotation_auto || rotation_angle%360 == 0) { - return; - } - - cv::RotateFlags flag; - if(rotation_angle == 90 || rotation_angle == -270) { // Rotate clockwise 90 degrees - flag = cv::ROTATE_90_CLOCKWISE; - } else if(rotation_angle == 270 || rotation_angle == -90) { // Rotate clockwise 270 degrees - flag = cv::ROTATE_90_COUNTERCLOCKWISE; - } else if(rotation_angle == 180 || rotation_angle == -180) { // Rotate clockwise 180 degrees - flag = cv::ROTATE_180; - } else { // Unsupported rotation - return; - } - - cv::rotate(mat, mat, flag); -} - double CvCapture_FFMPEG::getProperty( int property_id ) const { if( !video_st ) return 0; @@ -1409,9 +1401,9 @@ double CvCapture_FFMPEG::getProperty( int property_id ) const case CV_FFMPEG_CAP_PROP_FRAME_COUNT: return (double)get_total_frames(); case CV_FFMPEG_CAP_PROP_FRAME_WIDTH: - return (double)((rotation_auto && rotation_angle%360) ? frame.height : frame.width); + return (double)((rotation_auto && rotation_angle%180) ? frame.height : frame.width); case CV_FFMPEG_CAP_PROP_FRAME_HEIGHT: - return (double)((rotation_auto && rotation_angle%360) ? frame.width : frame.height); + return (double)((rotation_auto && rotation_angle%180) ? frame.width : frame.height); case CV_FFMPEG_CAP_PROP_FPS: return get_fps(); case CV_FFMPEG_CAP_PROP_FOURCC: @@ -1458,7 +1450,12 @@ double CvCapture_FFMPEG::getProperty( int property_id ) const case CV_FFMPEG_CAP_PROP_ORIENTATION_META: return static_cast(rotation_angle); case CV_FFMPEG_CAP_PROP_ORIENTATION_AUTO: +#if ((LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)) && \ + (LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100))) return static_cast(rotation_auto); +#else + return 0; +#endif default: break; } @@ -1540,9 +1537,12 @@ double CvCapture_FFMPEG::dts_to_sec(int64_t dts) const void CvCapture_FFMPEG::get_rotation_angle() { rotation_angle = 0; +#if ((LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)) && \ + (LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100))) AVDictionaryEntry *rotate_tag = av_dict_get(video_st->metadata, "rotate", NULL, 0); if (rotate_tag != NULL) rotation_angle = atoi(rotate_tag->value); +#endif } void CvCapture_FFMPEG::seek(int64_t _frame_number) @@ -1641,7 +1641,14 @@ bool CvCapture_FFMPEG::setProperty( int property_id, double value ) return setRaw(); return false; case CV_FFMPEG_CAP_PROP_ORIENTATION_AUTO: +#if ((LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)) && \ + (LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100))) rotation_auto = static_cast(value); + return true; +#else + rotation_auto = 0; + return false; +#endif break; default: return false; @@ -2670,9 +2677,9 @@ int cvGrabFrame_FFMPEG(CvCapture_FFMPEG* capture) return capture->grabFrame(); } -int cvRetrieveFrame_FFMPEG(CvCapture_FFMPEG* capture, cv::Mat &mat) +int cvRetrieveFrame_FFMPEG(CvCapture_FFMPEG* capture, unsigned char** data, int* step, int* width, int* height, int* cn) { - return capture->retrieveFrame(0, mat); + return capture->retrieveFrame(0, data, step, width, height, cn); } CvVideoWriter_FFMPEG* cvCreateVideoWriter_FFMPEG( const char* filename, int fourcc, double fps, diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index 0251261b81..6acc65f177 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -636,5 +636,57 @@ const ffmpeg_cap_properties_param_t videoio_ffmpeg_properties[] = { INSTANTIATE_TEST_CASE_P(videoio, ffmpeg_cap_properties, testing::ValuesIn(videoio_ffmpeg_properties)); +// related issue: https://github.com/opencv/opencv/issues/15499 +TEST(videoio, mp4_orientation_meta_auto) +{ + string video_file = string(cvtest::TS::ptr()->get_data_path()) + "video/big_buck_bunny_rotated.mp4"; + + VideoCapture cap; + EXPECT_NO_THROW(cap.open(video_file, CAP_FFMPEG)); + ASSERT_TRUE(cap.isOpened()) << "Can't open the video: " << video_file << " with backend " << CAP_FFMPEG << std::endl; + + cap.set(CAP_PROP_ORIENTATION_AUTO, true); + if (cap.get(CAP_PROP_ORIENTATION_AUTO) == 0) + throw SkipTestException("FFmpeg frame rotation metadata is not supported"); + + Size actual; + EXPECT_NO_THROW(actual = Size((int)cap.get(CAP_PROP_FRAME_WIDTH), + (int)cap.get(CAP_PROP_FRAME_HEIGHT))); + EXPECT_EQ(384, actual.width); + EXPECT_EQ(672, actual.height); + + Mat frame; + + cap >> frame; + + ASSERT_EQ(384, frame.cols); + ASSERT_EQ(672, frame.rows); +} + +// related issue: https://github.com/opencv/opencv/issues/15499 +TEST(videoio, mp4_orientation_no_rotation) +{ + string video_file = string(cvtest::TS::ptr()->get_data_path()) + "video/big_buck_bunny_rotated.mp4"; + + VideoCapture cap; + EXPECT_NO_THROW(cap.open(video_file, CAP_FFMPEG)); + cap.set(CAP_PROP_ORIENTATION_AUTO, 0); + ASSERT_TRUE(cap.isOpened()) << "Can't open the video: " << video_file << " with backend " << CAP_FFMPEG << std::endl; + ASSERT_FALSE(cap.get(CAP_PROP_ORIENTATION_AUTO)); + + Size actual; + EXPECT_NO_THROW(actual = Size((int)cap.get(CAP_PROP_FRAME_WIDTH), + (int)cap.get(CAP_PROP_FRAME_HEIGHT))); + EXPECT_EQ(672, actual.width); + EXPECT_EQ(384, actual.height); + + Mat frame; + + cap >> frame; + + ASSERT_EQ(672, frame.cols); + ASSERT_EQ(384, frame.rows); +} + #endif }} // namespace From 88dbee35894497052c77b505071fa20597be353e Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Wed, 22 Jul 2020 07:29:56 +0300 Subject: [PATCH 7/9] Update samples --- samples/cpp/create_mask.cpp | 4 +--- samples/cpp/intersectExample.cpp | 4 +--- samples/cpp/squares.cpp | 22 ++++------------------ 3 files changed, 6 insertions(+), 24 deletions(-) diff --git a/samples/cpp/create_mask.cpp b/samples/cpp/create_mask.cpp index dc54953678..67b0ec9bbd 100644 --- a/samples/cpp/create_mask.cpp +++ b/samples/cpp/create_mask.cpp @@ -74,9 +74,7 @@ void mouseHandler(int event, int x, int y, int, void*) final = Mat::zeros(src.size(), CV_8UC3); mask = Mat::zeros(src.size(), CV_8UC1); - vector > vpts; - vpts.push_back(pts); - fillPoly(mask, vpts, Scalar(255, 255, 255), 8, 0); + fillPoly(mask, pts, Scalar(255, 255, 255), 8, 0); bitwise_and(src, src, final, mask); imshow("Mask", mask); imshow("Result", final); diff --git a/samples/cpp/intersectExample.cpp b/samples/cpp/intersectExample.cpp index a8a897241f..187aebbea9 100644 --- a/samples/cpp/intersectExample.cpp +++ b/samples/cpp/intersectExample.cpp @@ -50,9 +50,7 @@ static float drawIntersection(Mat &image, vector polygon1, vector { fillColor = Scalar(0, 0, 255); } - vector > pp; - pp.push_back(intersectionPolygon); - fillPoly(image, pp, fillColor); + fillPoly(image, intersectionPolygon, fillColor); } polylines(image, polygons, true, Scalar(0, 0, 0)); diff --git a/samples/cpp/squares.cpp b/samples/cpp/squares.cpp index 36535d1ee0..042a716f90 100644 --- a/samples/cpp/squares.cpp +++ b/samples/cpp/squares.cpp @@ -121,21 +121,6 @@ static void findSquares( const Mat& image, vector >& squares ) } } - -// the function draws all the squares in the image -static void drawSquares( Mat& image, const vector >& squares ) -{ - for( size_t i = 0; i < squares.size(); i++ ) - { - const Point* p = &squares[i][0]; - int n = (int)squares[i].size(); - polylines(image, &p, &n, 1, true, Scalar(0,255,0), 3, LINE_AA); - } - - imshow(wndname, image); -} - - int main(int argc, char** argv) { static const char* names[] = { "pic1.png", "pic2.png", "pic3.png", @@ -148,8 +133,6 @@ int main(int argc, char** argv) names[1] = "0"; } - vector > squares; - for( int i = 0; names[i] != 0; i++ ) { string filename = samples::findFile(names[i]); @@ -160,8 +143,11 @@ int main(int argc, char** argv) continue; } + vector > squares; findSquares(image, squares); - drawSquares(image, squares); + + polylines(image, squares, true, Scalar(0, 255, 0), 3, LINE_AA); + imshow(wndname, image); int c = waitKey(); if( c == 27 ) From 7e943808b6f136f9d736a0f2cedbcb6077f9c366 Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Thu, 23 Jul 2020 14:39:40 +0300 Subject: [PATCH 8/9] Update train_HOG.cpp --- samples/cpp/train_HOG.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/samples/cpp/train_HOG.cpp b/samples/cpp/train_HOG.cpp index 356ff0ec3f..4a160fe4eb 100644 --- a/samples/cpp/train_HOG.cpp +++ b/samples/cpp/train_HOG.cpp @@ -74,9 +74,9 @@ void load_images( const String & dirname, vector< Mat > & img_lst, bool showImag for ( size_t i = 0; i < files.size(); ++i ) { Mat img = imread( files[i] ); // load the image - if ( img.empty() ) // invalid image, skip it. + if ( img.empty() ) { - cout << files[i] << " is invalid!" << endl; + cout << files[i] << " is invalid!" << endl; // invalid image, skip it. continue; } @@ -95,16 +95,13 @@ void sample_neg( const vector< Mat > & full_neg_lst, vector< Mat > & neg_lst, co box.width = size.width; box.height = size.height; - const int size_x = box.width; - const int size_y = box.height; - srand( (unsigned int)time( NULL ) ); for ( size_t i = 0; i < full_neg_lst.size(); i++ ) if ( full_neg_lst[i].cols > box.width && full_neg_lst[i].rows > box.height ) { - box.x = rand() % ( full_neg_lst[i].cols - size_x ); - box.y = rand() % ( full_neg_lst[i].rows - size_y ); + box.x = rand() % ( full_neg_lst[i].cols - box.width ); + box.y = rand() % ( full_neg_lst[i].rows - box.height ); Mat roi = full_neg_lst[i]( box ); neg_lst.push_back( roi.clone() ); } @@ -259,7 +256,7 @@ int main( int argc, char** argv ) load_images( pos_dir, pos_lst, visualization ); if ( pos_lst.size() > 0 ) { - clog << "...[done]" << endl; + clog << "...[done] " << pos_lst.size() << " files." << endl; } else { @@ -287,22 +284,25 @@ int main( int argc, char** argv ) } clog << "Negative images are being loaded..."; - load_images( neg_dir, full_neg_lst, false ); + load_images( neg_dir, full_neg_lst, visualization ); + clog << "...[done] " << full_neg_lst.size() << " files." << endl; + + clog << "Negative images are being processed..."; sample_neg( full_neg_lst, neg_lst, pos_image_size ); - clog << "...[done]" << endl; + clog << "...[done] " << neg_lst.size() << " files." << endl; clog << "Histogram of Gradients are being calculated for positive images..."; computeHOGs( pos_image_size, pos_lst, gradient_lst, flip_samples ); size_t positive_count = gradient_lst.size(); labels.assign( positive_count, +1 ); - clog << "...[done] ( positive count : " << positive_count << " )" << endl; + clog << "...[done] ( positive images count : " << positive_count << " )" << endl; clog << "Histogram of Gradients are being calculated for negative images..."; computeHOGs( pos_image_size, neg_lst, gradient_lst, flip_samples ); size_t negative_count = gradient_lst.size() - positive_count; labels.insert( labels.end(), negative_count, -1 ); CV_Assert( positive_count < labels.size() ); - clog << "...[done] ( negative count : " << negative_count << " )" << endl; + clog << "...[done] ( negative images count : " << negative_count << " )" << endl; Mat train_data; convert_to_ml( gradient_lst, train_data ); @@ -324,7 +324,7 @@ int main( int argc, char** argv ) if ( train_twice ) { - clog << "Testing trained detector on negative images. This may take a few minutes..."; + clog << "Testing trained detector on negative images. This might take a few minutes..."; HOGDescriptor my_hog; my_hog.winSize = pos_image_size; From 19646ad04947c2d2a563765374127ee86fed9d86 Mon Sep 17 00:00:00 2001 From: Tomoaki Teshima Date: Sat, 25 Jul 2020 23:45:51 +0900 Subject: [PATCH 9/9] let the test pass on Jetson --- modules/cudaarithm/test/test_element_operations.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cudaarithm/test/test_element_operations.cpp b/modules/cudaarithm/test/test_element_operations.cpp index 9d20046df9..848ab5ce74 100644 --- a/modules/cudaarithm/test/test_element_operations.cpp +++ b/modules/cudaarithm/test/test_element_operations.cpp @@ -2778,7 +2778,7 @@ CUDA_TEST_P(PolarToCart, Accuracy) { cv::Mat magnitude = randomMat(size, type); cv::Mat angle = randomMat(size, type); - const double tol = (type == CV_32FC1 ? 1.6e-4 : 1e-4) * (angleInDegrees ? 1.0 : 19.0); + const double tol = (type == CV_32FC1 ? 1.6e-4 : 1e-4) * (angleInDegrees ? 1.0 : 19.47); cv::cuda::GpuMat x = createMat(size, type, useRoi); cv::cuda::GpuMat y = createMat(size, type, useRoi);