diff --git a/3rdparty/protobuf/src/google/protobuf/text_format.cc b/3rdparty/protobuf/src/google/protobuf/text_format.cc index eed2a768c0..78f1acd7fe 100644 --- a/3rdparty/protobuf/src/google/protobuf/text_format.cc +++ b/3rdparty/protobuf/src/google/protobuf/text_format.cc @@ -469,8 +469,9 @@ class TextFormat::Parser::ParserImpl { "\" has no field named \"" + field_name + "\"."); return false; } else { - ReportWarning("Message type \"" + descriptor->full_name() + - "\" has no field named \"" + field_name + "\"."); + // No warnings to let user define custom layers (see https://github.com/opencv/opencv/pull/11129) + // ReportWarning("Message type \"" + descriptor->full_name() + + // "\" has no field named \"" + field_name + "\"."); } } } @@ -485,10 +486,13 @@ class TextFormat::Parser::ParserImpl { // start with "{" or "<" which indicates the beginning of a message body. // If there is no ":" or there is a "{" or "<" after ":", this field has // to be a message or the input is ill-formed. + UnknownFieldSet* unknown_fields = reflection->MutableUnknownFields(message); if (TryConsume(":") && !LookingAt("{") && !LookingAt("<")) { - return SkipFieldValue(); + UnknownFieldSet* unknown_field = unknown_fields->AddGroup(unknown_fields->field_count()); + unknown_field->AddLengthDelimited(0, field_name); // Add a field's name. + return SkipFieldValue(unknown_field); } else { - return SkipFieldMessage(); + return SkipFieldMessage(unknown_fields); } } @@ -571,7 +575,7 @@ label_skip_parsing: } // Skips the next field including the field's name and value. - bool SkipField() { + bool SkipField(UnknownFieldSet* unknown_fields) { string field_name; if (TryConsume("[")) { // Extension name. @@ -588,9 +592,11 @@ label_skip_parsing: // If there is no ":" or there is a "{" or "<" after ":", this field has // to be a message or the input is ill-formed. if (TryConsume(":") && !LookingAt("{") && !LookingAt("<")) { - DO(SkipFieldValue()); + UnknownFieldSet* unknown_field = unknown_fields->AddGroup(unknown_fields->field_count()); + unknown_field->AddLengthDelimited(0, field_name); // Add a field's name. + DO(SkipFieldValue(unknown_field)); } else { - DO(SkipFieldMessage()); + DO(SkipFieldMessage(unknown_fields)); } // For historical reasons, fields may optionally be separated by commas or // semicolons. @@ -625,11 +631,11 @@ label_skip_parsing: // Skips the whole body of a message including the beginning delimiter and // the ending delimiter. - bool SkipFieldMessage() { + bool SkipFieldMessage(UnknownFieldSet* unknown_fields) { string delimiter; DO(ConsumeMessageDelimiter(&delimiter)); while (!LookingAt(">") && !LookingAt("}")) { - DO(SkipField()); + DO(SkipField(unknown_fields)); } DO(Consume(delimiter)); return true; @@ -769,7 +775,7 @@ label_skip_parsing: return true; } - bool SkipFieldValue() { + bool SkipFieldValue(UnknownFieldSet* unknown_field) { if (LookingAtType(io::Tokenizer::TYPE_STRING)) { while (LookingAtType(io::Tokenizer::TYPE_STRING)) { tokenizer_.Next(); @@ -779,9 +785,9 @@ label_skip_parsing: if (TryConsume("[")) { while (true) { if (!LookingAt("{") && !LookingAt("<")) { - DO(SkipFieldValue()); + DO(SkipFieldValue(unknown_field)); } else { - DO(SkipFieldMessage()); + DO(SkipFieldMessage(unknown_field)); } if (TryConsume("]")) { break; @@ -833,6 +839,8 @@ label_skip_parsing: return false; } } + // Use a tag 1 because tag 0 is used for field's name. + unknown_field->AddLengthDelimited(1, tokenizer_.current().text); tokenizer_.Next(); return true; } @@ -1298,13 +1306,13 @@ class TextFormat::Printer::TextGenerator TextFormat::Finder::~Finder() { } -TextFormat::Parser::Parser() +TextFormat::Parser::Parser(bool allow_unknown_field) : error_collector_(NULL), finder_(NULL), parse_info_tree_(NULL), allow_partial_(false), allow_case_insensitive_field_(false), - allow_unknown_field_(false), + allow_unknown_field_(allow_unknown_field), allow_unknown_enum_(false), allow_field_number_(false), allow_relaxed_whitespace_(false), diff --git a/3rdparty/protobuf/src/google/protobuf/text_format.h b/3rdparty/protobuf/src/google/protobuf/text_format.h index a2670d6bb4..74d89a5f3e 100644 --- a/3rdparty/protobuf/src/google/protobuf/text_format.h +++ b/3rdparty/protobuf/src/google/protobuf/text_format.h @@ -457,7 +457,7 @@ class LIBPROTOBUF_EXPORT TextFormat { // For more control over parsing, use this class. class LIBPROTOBUF_EXPORT Parser { public: - Parser(); + Parser(bool allow_unknown_field = false); ~Parser(); // Like TextFormat::Parse(). diff --git a/cmake/OpenCVCompilerOptimizations.cmake b/cmake/OpenCVCompilerOptimizations.cmake index 98b6f5a090..9190222afc 100644 --- a/cmake/OpenCVCompilerOptimizations.cmake +++ b/cmake/OpenCVCompilerOptimizations.cmake @@ -258,7 +258,10 @@ if(X86 OR X86_64) endif() if(NOT DEFINED CPU_BASELINE) - if(X86_64) + if(APPLE) + # MacOS X has limited set of possible supported H/W, so compiler is configured well + set(CPU_BASELINE "DETECT" CACHE STRING "${HELP_CPU_BASELINE}") + elseif(X86_64) set(CPU_BASELINE "SSE3" CACHE STRING "${HELP_CPU_BASELINE}") else() set(CPU_BASELINE "SSE2" CACHE STRING "${HELP_CPU_BASELINE}") diff --git a/cmake/OpenCVCompilerOptions.cmake b/cmake/OpenCVCompilerOptions.cmake index b9293df1b6..774103af3a 100644 --- a/cmake/OpenCVCompilerOptions.cmake +++ b/cmake/OpenCVCompilerOptions.cmake @@ -120,7 +120,9 @@ if(CV_GCC OR CV_CLANG) add_extra_compiler_option(-Wno-unnamed-type-template-args) add_extra_compiler_option(-Wno-comment) if(NOT OPENCV_SKIP_IMPLICIT_FALLTHROUGH - AND NOT " ${CMAKE_CXX_FLAGS} ${OPENCV_EXTRA_FLAGS} ${OPENCV_EXTRA_CXX_FLAGS}" MATCHES "implicit-fallthrough") + AND NOT " ${CMAKE_CXX_FLAGS} ${OPENCV_EXTRA_FLAGS} ${OPENCV_EXTRA_CXX_FLAGS}" MATCHES "implicit-fallthrough" + AND (CV_GCC AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0.0) + ) add_extra_compiler_option(-Wimplicit-fallthrough=3) endif() if(CV_GCC AND CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 7.2.0) diff --git a/cmake/checks/cpu_popcnt.cpp b/cmake/checks/cpu_popcnt.cpp index 16e5575108..44c7deda05 100644 --- a/cmake/checks/cpu_popcnt.cpp +++ b/cmake/checks/cpu_popcnt.cpp @@ -4,12 +4,14 @@ # define CV_POPCNT_U64 _mm_popcnt_u64 # endif # define CV_POPCNT_U32 _mm_popcnt_u32 -#else +#elif defined(__POPCNT__) # include # if defined(__x86_64__) # define CV_POPCNT_U64 __builtin_popcountll # endif # define CV_POPCNT_U32 __builtin_popcount +#else +# error "__POPCNT__ is not defined by compiler" #endif int main() diff --git a/doc/tutorials/dnn/dnn_custom_layers/dnn_custom_layers.md b/doc/tutorials/dnn/dnn_custom_layers/dnn_custom_layers.md new file mode 100644 index 0000000000..f0e419753a --- /dev/null +++ b/doc/tutorials/dnn/dnn_custom_layers/dnn_custom_layers.md @@ -0,0 +1,192 @@ +# Custom deep learning layers support {#tutorial_dnn_custom_layers} + +## Introduction +Deep learning is a fast growing area. The new approaches to build neural networks +usually introduce new types of layers. They could be modifications of existing +ones or implement outstanding researching ideas. + +OpenCV gives an opportunity to import and run networks from different deep learning +frameworks. There are a number of the most popular layers. However you can face +a problem that your network cannot be imported using OpenCV because of unimplemented layers. + +The first solution is to create a feature request at https://github.com/opencv/opencv/issues +mentioning details such a source of model and type of new layer. A new layer could +be implemented if OpenCV community shares this need. + +The second way is to define a **custom layer** so OpenCV's deep learning engine +will know how to use it. This tutorial is dedicated to show you a process of deep +learning models import customization. + +## Define a custom layer in C++ +Deep learning layer is a building block of network's pipeline. +It has connections to **input blobs** and produces results to **output blobs**. +There are trained **weights** and **hyper-parameters**. +Layers' names, types, weights and hyper-parameters are stored in files are generated by +native frameworks during training. If OpenCV mets unknown layer type it throws an +exception trying to read a model: + +``` +Unspecified error: Can't create layer "layer_name" of type "MyType" in function getLayerInstance +``` + +To import the model correctly you have to derive a class from cv::dnn::Layer with +the following methods: + +@snippet dnn/custom_layers.cpp A custom layer interface + +And register it before the import: + +@snippet dnn/custom_layers.cpp Register a custom layer + +@note `MyType` is a type of unimplemented layer from the thrown exception. + +Let's see what all the methods do: + +- Constructor + +@snippet dnn/custom_layers.cpp MyLayer::MyLayer + +Retrieves hyper-parameters from cv::dnn::LayerParams. If your layer has trainable +weights they will be already stored in the Layer's member cv::dnn::Layer::blobs. + +- A static method `create` + +@snippet dnn/custom_layers.cpp MyLayer::create + +This method should create an instance of you layer and return cv::Ptr with it. + +- Output blobs' shape computation + +@snippet dnn/custom_layers.cpp MyLayer::getMemoryShapes + +Returns layer's output shapes depends on input shapes. You may request an extra +memory using `internals`. + +- Run a layer + +@snippet dnn/custom_layers.cpp MyLayer::forward + +Implement a layer's logic here. Compute outputs for given inputs. + +@note OpenCV manages memory allocated for layers. In the most cases the same memory +can be reused between layers. So your `forward` implementation should not rely that +the second invocation of `forward` will has the same data at `outputs` and `internals`. + +- Optional `finalize` method + +@snippet dnn/custom_layers.cpp MyLayer::finalize + +The chain of methods are the following: OpenCV deep learning engine calls `create` +method once then it calls `getMemoryShapes` for an every created layer then you +can make some preparations depends on known input dimensions at cv::dnn::Layer::finalize. +After network was initialized only `forward` method is called for an every network's input. + +@note Varying input blobs' sizes such height or width or batch size you make OpenCV +reallocate all the internal memory. That leads efficiency gaps. Try to initialize +and deploy models using a fixed batch size and image's dimensions. + +## Example: custom layer from Caffe +Let's create a custom layer `Interp` from https://github.com/cdmh/deeplab-public. +It's just a simple resize that takes an input blob of size `N x C x Hi x Wi` and returns +an output blob of size `N x C x Ho x Wo` where `N` is a batch size, `C` is a number of channels, +`Hi x Wi` and `Ho x Wo` are input and output `height x width` correspondingly. +This layer has no trainable weights but it has hyper-parameters to specify an output size. + +In example, +~~~~~~~~~~~~~ +layer { + name: "output" + type: "Interp" + bottom: "input" + top: "output" + interp_param { + height: 9 + width: 8 + } +} +~~~~~~~~~~~~~ + +This way our implementation can look like: + +@snippet dnn/custom_layers.cpp InterpLayer + +Next we need to register a new layer type and try to import the model. + +@snippet dnn/custom_layers.cpp Register InterpLayer + +## Example: custom layer from TensorFlow +This is an example of how to import a network with [tf.image.resize_bilinear](https://www.tensorflow.org/versions/master/api_docs/python/tf/image/resize_bilinear) +operation. This is also a resize but with an implementation different from OpenCV's or `Interp` above. + +Let's create a single layer network: +~~~~~~~~~~~~~{.py} +inp = tf.placeholder(tf.float32, [2, 3, 4, 5], 'input') +resized = tf.image.resize_bilinear(inp, size=[9, 8], name='resize_bilinear') +~~~~~~~~~~~~~ +OpenCV sees that TensorFlow's graph in the following way: + +``` +node { + name: "input" + op: "Placeholder" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } +} +node { + name: "resize_bilinear/size" + op: "Const" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT32 + tensor_shape { + dim { + size: 2 + } + } + tensor_content: "\t\000\000\000\010\000\000\000" + } + } + } +} +node { + name: "resize_bilinear" + op: "ResizeBilinear" + input: "input:0" + input: "resize_bilinear/size" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "align_corners" + value { + b: false + } + } +} +library { +} +``` +Custom layers import from TensorFlow is designed to put all layer's `attr` into +cv::dnn::LayerParams but input `Const` blobs into cv::dnn::Layer::blobs. +In our case resize's output shape will be stored in layer's `blobs[0]`. + +@snippet dnn/custom_layers.cpp ResizeBilinearLayer + +Next we register a layer and try to import the model. + +@snippet dnn/custom_layers.cpp Register ResizeBilinearLayer diff --git a/doc/tutorials/dnn/table_of_content_dnn.markdown b/doc/tutorials/dnn/table_of_content_dnn.markdown index 94b818c41a..9a52f100db 100644 --- a/doc/tutorials/dnn/table_of_content_dnn.markdown +++ b/doc/tutorials/dnn/table_of_content_dnn.markdown @@ -48,3 +48,11 @@ Deep Neural Networks (dnn module) {#tutorial_table_of_content_dnn} *Author:* Dmitry Kurtaev In this tutorial we'll run deep learning models in browser using OpenCV.js. + +- @subpage tutorial_dnn_custom_layers + + *Compatibility:* \> OpenCV 3.4.1 + + *Author:* Dmitry Kurtaev + + How to define custom layers to import networks. diff --git a/modules/calib3d/src/circlesgrid.cpp b/modules/calib3d/src/circlesgrid.cpp index aecafa2fee..18ff876efc 100644 --- a/modules/calib3d/src/circlesgrid.cpp +++ b/modules/calib3d/src/circlesgrid.cpp @@ -751,12 +751,8 @@ bool CirclesGridFinder::isDetectionCorrect() } return (vertices.size() == largeHeight * largeWidth + smallHeight * smallWidth); } - - default: - CV_Error(0, "Unknown pattern type"); } - - return false; + CV_Error(Error::StsBadArg, "Unknown pattern type"); } void CirclesGridFinder::findMCS(const std::vector &basis, std::vector &basisGraphs) diff --git a/modules/core/include/opencv2/core/base.hpp b/modules/core/include/opencv2/core/base.hpp index 164f4439fd..f6bc601492 100644 --- a/modules/core/include/opencv2/core/base.hpp +++ b/modules/core/include/opencv2/core/base.hpp @@ -371,7 +371,7 @@ It is possible to alternate error processing by using redirectError(). @param _func - function name. Available only when the compiler supports getting it @param _file - source file name where the error has occurred @param _line - line number in the source file where the error has occurred -@see CV_Error, CV_Error_, CV_ErrorNoReturn, CV_ErrorNoReturn_, CV_Assert, CV_DbgAssert +@see CV_Error, CV_Error_, CV_Assert, CV_DbgAssert */ CV_EXPORTS void error(int _code, const String& _err, const char* _func, const char* _file, int _line); @@ -414,8 +414,6 @@ CV_INLINE CV_NORETURN void errorNoReturn(int _code, const String& _err, const ch // We need to use simplified definition for them. #define CV_Error(...) do { abort(); } while (0) #define CV_Error_( code, args ) do { cv::format args; abort(); } while (0) -#define CV_ErrorNoReturn(...) do { abort(); } while (0) -#define CV_ErrorNoReturn_(...) do { abort(); } while (0) #define CV_Assert_1( expr ) do { if (!(expr)) abort(); } while (0) #else // CV_STATIC_ANALYSIS @@ -446,22 +444,22 @@ for example: */ #define CV_Error_( code, args ) cv::error( code, cv::format args, CV_Func, __FILE__, __LINE__ ) -/** same as CV_Error(code,msg), but does not return */ -#define CV_ErrorNoReturn( code, msg ) cv::errorNoReturn( code, msg, CV_Func, __FILE__, __LINE__ ) - -/** same as CV_Error_(code,args), but does not return */ -#define CV_ErrorNoReturn_( code, args ) cv::errorNoReturn( code, cv::format args, CV_Func, __FILE__, __LINE__ ) - #define CV_Assert_1( expr ) if(!!(expr)) ; else cv::error( cv::Error::StsAssert, #expr, CV_Func, __FILE__, __LINE__ ) //! @cond IGNORED +#define CV__ErrorNoReturn( code, msg ) cv::errorNoReturn( code, msg, CV_Func, __FILE__, __LINE__ ) +#define CV__ErrorNoReturn_( code, args ) cv::errorNoReturn( code, cv::format args, CV_Func, __FILE__, __LINE__ ) #ifdef __OPENCV_BUILD #undef CV_Error -#define CV_Error CV_ErrorNoReturn +#define CV_Error CV__ErrorNoReturn #undef CV_Error_ -#define CV_Error_ CV_ErrorNoReturn_ +#define CV_Error_ CV__ErrorNoReturn_ #undef CV_Assert_1 #define CV_Assert_1( expr ) if(!!(expr)) ; else cv::errorNoReturn( cv::Error::StsAssert, #expr, CV_Func, __FILE__, __LINE__ ) +#else +// backward compatibility +#define CV_ErrorNoReturn CV__ErrorNoReturn +#define CV_ErrorNoReturn_ CV__ErrorNoReturn_ #endif //! @endcond diff --git a/modules/core/include/opencv2/core/hal/intrin_cpp.hpp b/modules/core/include/opencv2/core/hal/intrin_cpp.hpp index b619f1a0c2..c7cbb578db 100644 --- a/modules/core/include/opencv2/core/hal/intrin_cpp.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_cpp.hpp @@ -1042,13 +1042,16 @@ template inline bool v_check_any(const v_reg<_Tp, n>& a) return false; } -/** @brief Bitwise select +/** @brief Per-element select (blend operation) -Return value will be built by combining values a and b using the following scheme: -If the i-th bit in _mask_ is 1 - select i-th bit from _a_ -else - select i-th bit from _b_ */ +Return value will be built by combining values _a_ and _b_ using the following scheme: + result[i] = mask[i] ? a[i] : b[i]; + +@note: _mask_ element values are restricted to these values: +- 0: select element from _b_ +- 0xff/0xffff/etc: select element from _a_ +(fully compatible with bitwise-based operator) +*/ template inline v_reg<_Tp, n> v_select(const v_reg<_Tp, n>& mask, const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) { @@ -1058,8 +1061,8 @@ template inline v_reg<_Tp, n> v_select(const v_reg<_Tp, n>& for( int i = 0; i < n; i++ ) { int_type m = Traits::reinterpret_int(mask.s[i]); - c.s[i] = Traits::reinterpret_from_int((Traits::reinterpret_int(a.s[i]) & m) - | (Traits::reinterpret_int(b.s[i]) & ~m)); + CV_DbgAssert(m == 0 || m == (~(int_type)0)); // restrict mask values: 0 or 0xff/0xffff/etc + c.s[i] = m ? a.s[i] : b.s[i]; } return c; } diff --git a/modules/core/include/opencv2/core/hal/intrin_sse.hpp b/modules/core/include/opencv2/core/hal/intrin_sse.hpp index c91b05de93..22c0668642 100644 --- a/modules/core/include/opencv2/core/hal/intrin_sse.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_sse.hpp @@ -438,10 +438,14 @@ void v_rshr_pack_store(schar* ptr, const v_int16x8& a) } -// bit-wise "mask ? a : b" +// byte-wise "mask ? a : b" inline __m128i v_select_si128(__m128i mask, __m128i a, __m128i b) { +#if CV_SSE4_1 + return _mm_blendv_epi8(b, a, mask); +#else return _mm_xor_si128(b, _mm_and_si128(_mm_xor_si128(a, b), mask)); +#endif } inline v_uint16x8 v_pack(const v_uint32x4& a, const v_uint32x4& b) @@ -1403,6 +1407,26 @@ OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_int32x4, epi8, v_packq_epi32, OPENCV_HAL_AND, OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_float32x4, ps, OPENCV_HAL_NOP, OPENCV_HAL_1ST, 15, 15) OPENCV_HAL_IMPL_SSE_CHECK_SIGNS(v_float64x2, pd, OPENCV_HAL_NOP, OPENCV_HAL_1ST, 3, 3) +#if CV_SSE4_1 +#define OPENCV_HAL_IMPL_SSE_SELECT(_Tpvec, cast_ret, cast, suffix) \ +inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ +{ \ + return _Tpvec(cast_ret(_mm_blendv_##suffix(cast(b.val), cast(a.val), cast(mask.val)))); \ +} + +OPENCV_HAL_IMPL_SSE_SELECT(v_uint8x16, OPENCV_HAL_NOP, OPENCV_HAL_NOP, epi8) +OPENCV_HAL_IMPL_SSE_SELECT(v_int8x16, OPENCV_HAL_NOP, OPENCV_HAL_NOP, epi8) +OPENCV_HAL_IMPL_SSE_SELECT(v_uint16x8, OPENCV_HAL_NOP, OPENCV_HAL_NOP, epi8) +OPENCV_HAL_IMPL_SSE_SELECT(v_int16x8, OPENCV_HAL_NOP, OPENCV_HAL_NOP, epi8) +OPENCV_HAL_IMPL_SSE_SELECT(v_uint32x4, _mm_castps_si128, _mm_castsi128_ps, ps) +OPENCV_HAL_IMPL_SSE_SELECT(v_int32x4, _mm_castps_si128, _mm_castsi128_ps, ps) +// OPENCV_HAL_IMPL_SSE_SELECT(v_uint64x2, TBD, TBD, pd) +// OPENCV_HAL_IMPL_SSE_SELECT(v_int64x2, TBD, TBD, ps) +OPENCV_HAL_IMPL_SSE_SELECT(v_float32x4, OPENCV_HAL_NOP, OPENCV_HAL_NOP, ps) +OPENCV_HAL_IMPL_SSE_SELECT(v_float64x2, OPENCV_HAL_NOP, OPENCV_HAL_NOP, pd) + +#else // CV_SSE4_1 + #define OPENCV_HAL_IMPL_SSE_SELECT(_Tpvec, suffix) \ inline _Tpvec v_select(const _Tpvec& mask, const _Tpvec& a, const _Tpvec& b) \ { \ @@ -1419,6 +1443,7 @@ OPENCV_HAL_IMPL_SSE_SELECT(v_int32x4, si128) // OPENCV_HAL_IMPL_SSE_SELECT(v_int64x2, si128) OPENCV_HAL_IMPL_SSE_SELECT(v_float32x4, ps) OPENCV_HAL_IMPL_SSE_SELECT(v_float64x2, pd) +#endif #define OPENCV_HAL_IMPL_SSE_EXPAND(_Tpuvec, _Tpwuvec, _Tpu, _Tpsvec, _Tpwsvec, _Tps, suffix, wsuffix, shift) \ inline void v_expand(const _Tpuvec& a, _Tpwuvec& b0, _Tpwuvec& b1) \ @@ -1607,6 +1632,28 @@ inline void v_load_deinterleave(const uchar* ptr, v_uint8x16& a, v_uint8x16& b) inline void v_load_deinterleave(const uchar* ptr, v_uint8x16& a, v_uint8x16& b, v_uint8x16& c) { +#if CV_SSSE3 + static const __m128i m0 = _mm_setr_epi8(0, 3, 6, 9, 12, 15, 1, 4, 7, 10, 13, 2, 5, 8, 11, 14); + static const __m128i m1 = _mm_alignr_epi8(m0, m0, 11); + static const __m128i m2 = _mm_alignr_epi8(m0, m0, 6); + + __m128i t0 = _mm_loadu_si128((const __m128i*)ptr); + __m128i t1 = _mm_loadu_si128((const __m128i*)(ptr + 16)); + __m128i t2 = _mm_loadu_si128((const __m128i*)(ptr + 32)); + + __m128i s0 = _mm_shuffle_epi8(t0, m0); + __m128i s1 = _mm_shuffle_epi8(t1, m1); + __m128i s2 = _mm_shuffle_epi8(t2, m2); + + t0 = _mm_alignr_epi8(s1, _mm_slli_si128(s0, 10), 5); + a.val = _mm_alignr_epi8(s2, t0, 5); + + t1 = _mm_alignr_epi8(_mm_srli_si128(s1, 5), _mm_slli_si128(s0, 5), 6); + b.val = _mm_alignr_epi8(_mm_srli_si128(s2, 5), t1, 5); + + t2 = _mm_alignr_epi8(_mm_srli_si128(s2, 10), s1, 11); + c.val = _mm_alignr_epi8(t2, s0, 11); +#else __m128i t00 = _mm_loadu_si128((const __m128i*)ptr); __m128i t01 = _mm_loadu_si128((const __m128i*)(ptr + 16)); __m128i t02 = _mm_loadu_si128((const __m128i*)(ptr + 32)); @@ -1626,6 +1673,7 @@ inline void v_load_deinterleave(const uchar* ptr, v_uint8x16& a, v_uint8x16& b, a.val = _mm_unpacklo_epi8(t30, _mm_unpackhi_epi64(t31, t31)); b.val = _mm_unpacklo_epi8(_mm_unpackhi_epi64(t30, t30), t32); c.val = _mm_unpacklo_epi8(t31, _mm_unpackhi_epi64(t32, t32)); +#endif } inline void v_load_deinterleave(const uchar* ptr, v_uint8x16& a, v_uint8x16& b, v_uint8x16& c, v_uint8x16& d) @@ -1840,6 +1888,27 @@ inline void v_store_interleave( uchar* ptr, const v_uint8x16& a, const v_uint8x1 inline void v_store_interleave( uchar* ptr, const v_uint8x16& a, const v_uint8x16& b, const v_uint8x16& c ) { +#if CV_SSSE3 + static const __m128i m0 = _mm_setr_epi8(0, 6, 11, 1, 7, 12, 2, 8, 13, 3, 9, 14, 4, 10, 15, 5); + static const __m128i m1 = _mm_setr_epi8(5, 11, 0, 6, 12, 1, 7, 13, 2, 8, 14, 3, 9, 15, 4, 10); + static const __m128i m2 = _mm_setr_epi8(10, 0, 5, 11, 1, 6, 12, 2, 7, 13, 3, 8, 14, 4, 9, 15); + + __m128i t0 = _mm_alignr_epi8(b.val, _mm_slli_si128(a.val, 10), 5); + t0 = _mm_alignr_epi8(c.val, t0, 5); + __m128i s0 = _mm_shuffle_epi8(t0, m0); + + __m128i t1 = _mm_alignr_epi8(_mm_srli_si128(b.val, 5), _mm_slli_si128(a.val, 5), 6); + t1 = _mm_alignr_epi8(_mm_srli_si128(c.val, 5), t1, 5); + __m128i s1 = _mm_shuffle_epi8(t1, m1); + + __m128i t2 = _mm_alignr_epi8(_mm_srli_si128(c.val, 10), b.val, 11); + t2 = _mm_alignr_epi8(t2, a.val, 11); + __m128i s2 = _mm_shuffle_epi8(t2, m2); + + _mm_storeu_si128((__m128i*)ptr, s0); + _mm_storeu_si128((__m128i*)(ptr + 16), s1); + _mm_storeu_si128((__m128i*)(ptr + 32), s2); +#else __m128i z = _mm_setzero_si128(); __m128i ab0 = _mm_unpacklo_epi8(a.val, b.val); __m128i ab1 = _mm_unpackhi_epi8(a.val, b.val); @@ -1881,6 +1950,7 @@ inline void v_store_interleave( uchar* ptr, const v_uint8x16& a, const v_uint8x1 _mm_storeu_si128((__m128i*)(ptr), v0); _mm_storeu_si128((__m128i*)(ptr + 16), v1); _mm_storeu_si128((__m128i*)(ptr + 32), v2); +#endif } inline void v_store_interleave( uchar* ptr, const v_uint8x16& a, const v_uint8x16& b, diff --git a/modules/core/include/opencv2/core/opencl/opencl_info.hpp b/modules/core/include/opencv2/core/opencl/opencl_info.hpp index fdd27039e2..b5d37394a7 100644 --- a/modules/core/include/opencv2/core/opencl/opencl_info.hpp +++ b/modules/core/include/opencv2/core/opencl/opencl_info.hpp @@ -93,7 +93,7 @@ static void dumpOpenCLInformation() const Device& device = Device::getDefault(); if (!device.available()) - CV_ErrorNoReturn(Error::OpenCLInitError, "OpenCL device is not available"); + CV_Error(Error::OpenCLInitError, "OpenCL device is not available"); DUMP_MESSAGE_STDOUT("Current OpenCL device: "); diff --git a/modules/core/include/opencv2/core/opencl/runtime/opencl_core.hpp b/modules/core/include/opencv2/core/opencl/runtime/opencl_core.hpp index ea9a7ffa1e..0404b3177a 100644 --- a/modules/core/include/opencv2/core/opencl/runtime/opencl_core.hpp +++ b/modules/core/include/opencv2/core/opencl/runtime/opencl_core.hpp @@ -76,7 +76,7 @@ #endif #ifndef CL_VERSION_1_2 -#define CV_REQUIRE_OPENCL_1_2_ERROR CV_ErrorNoReturn(cv::Error::OpenCLApiCallError, "OpenCV compiled without OpenCL v1.2 support, so we can't use functionality from OpenCL v1.2") +#define CV_REQUIRE_OPENCL_1_2_ERROR CV_Error(cv::Error::OpenCLApiCallError, "OpenCV compiled without OpenCL v1.2 support, so we can't use functionality from OpenCL v1.2") #endif #endif // HAVE_OPENCL diff --git a/modules/core/perf/perf_mat.cpp b/modules/core/perf/perf_mat.cpp index 83f10e1add..325ef5fb7c 100644 --- a/modules/core/perf/perf_mat.cpp +++ b/modules/core/perf/perf_mat.cpp @@ -96,8 +96,8 @@ PERF_TEST_P(Size_MatType, Mat_Clone_Roi, } PERF_TEST_P(Size_MatType, Mat_CopyToWithMask, - testing::Combine(testing::Values(TYPICAL_MAT_SIZES), - testing::Values(CV_8UC1, CV_8UC2)) + testing::Combine(testing::Values(::perf::sz1080p, ::perf::szODD), + testing::Values(CV_8UC1, CV_8UC2, CV_8UC3, CV_16UC1, CV_32SC1, CV_32FC4)) ) { const Size_MatType_t params = GetParam(); diff --git a/modules/core/src/alloc.cpp b/modules/core/src/alloc.cpp index 8be725459d..531af254ff 100644 --- a/modules/core/src/alloc.cpp +++ b/modules/core/src/alloc.cpp @@ -53,7 +53,6 @@ namespace cv { static void* OutOfMemoryError(size_t size) { CV_Error_(CV_StsNoMem, ("Failed to allocate %llu bytes", (unsigned long long)size)); - return 0; } diff --git a/modules/core/src/command_line_parser.cpp b/modules/core/src/command_line_parser.cpp index 7d5c6cc03a..766835fe1d 100644 --- a/modules/core/src/command_line_parser.cpp +++ b/modules/core/src/command_line_parser.cpp @@ -1,3 +1,6 @@ +// 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 "precomp.hpp" #include @@ -364,7 +367,6 @@ bool CommandLineParser::has(const String& name) const } CV_Error_(Error::StsBadArg, ("undeclared key '%s' requested", name.c_str())); - return false; } bool CommandLineParser::check() const diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index 9531270903..f4f18cb740 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -91,11 +91,7 @@ copyMask_(const uchar* _src, size_t sstep, const uchar* mask, size_t mste uchar* dst = (uchar*)_dst; int x = 0; #if CV_SIMD128 - if( hasSIMD128() - #if CV_SSE4_2 - && USE_SSE4_2 - #endif - ) { + { v_uint8x16 v_zero = v_setzero_u8(); for( ; x <= size.width - 16; x += 16 ) @@ -104,11 +100,7 @@ copyMask_(const uchar* _src, size_t sstep, const uchar* mask, size_t mste v_dst = v_load(dst + x), v_nmask = v_load(mask + x) == v_zero; - #if CV_SSE4_2 - v_dst = v_uint8x16(_mm_blendv_epi8(v_src.val, v_dst.val, v_nmask.val)); - #else v_dst = v_select(v_nmask, v_dst, v_src); - #endif v_store(dst + x, v_dst); } } @@ -130,11 +122,7 @@ copyMask_(const uchar* _src, size_t sstep, const uchar* mask, size_t mst ushort* dst = (ushort*)_dst; int x = 0; #if CV_SIMD128 - if( hasSIMD128() - #if CV_SSE4_2 - && USE_SSE4_2 - #endif - ) { + { v_uint8x16 v_zero = v_setzero_u8(); for( ; x <= size.width - 16; x += 16 ) @@ -146,13 +134,8 @@ copyMask_(const uchar* _src, size_t sstep, const uchar* mask, size_t mst v_uint8x16 v_nmask = v_load(mask + x) == v_zero; v_zip(v_nmask, v_nmask, v_nmask1, v_nmask2); - #if CV_SSE4_2 - v_dst1 = v_uint16x8(_mm_blendv_epi8(v_src1.val, v_dst1.val, v_nmask1.val)); - v_dst2 = v_uint16x8(_mm_blendv_epi8(v_src2.val, v_dst2.val, v_nmask2.val)); - #else v_dst1 = v_select(v_reinterpret_as_u16(v_nmask1), v_dst1, v_src1); v_dst2 = v_select(v_reinterpret_as_u16(v_nmask2), v_dst2, v_src2); - #endif v_store(dst + x, v_dst1); v_store(dst + x + 8, v_dst2); } diff --git a/modules/core/src/directx.cpp b/modules/core/src/directx.cpp index 2ea168705d..97e56259d5 100644 --- a/modules/core/src/directx.cpp +++ b/modules/core/src/directx.cpp @@ -50,11 +50,11 @@ #include # include "directx.inc.hpp" #else // HAVE_DIRECTX -#define NO_DIRECTX_SUPPORT_ERROR CV_ErrorNoReturn(cv::Error::StsBadFunc, "OpenCV was build without DirectX support") +#define NO_DIRECTX_SUPPORT_ERROR CV_Error(cv::Error::StsBadFunc, "OpenCV was build without DirectX support") #endif #ifndef HAVE_OPENCL -# define NO_OPENCL_SUPPORT_ERROR CV_ErrorNoReturn(cv::Error::StsBadFunc, "OpenCV was build without OpenCL support") +# define NO_OPENCL_SUPPORT_ERROR CV_Error(cv::Error::StsBadFunc, "OpenCV was build without OpenCL support") #endif // HAVE_OPENCL namespace cv { namespace directx { diff --git a/modules/core/src/gl_core_3_1.cpp b/modules/core/src/gl_core_3_1.cpp index 60d7f6f88a..eabb36ff3e 100644 --- a/modules/core/src/gl_core_3_1.cpp +++ b/modules/core/src/gl_core_3_1.cpp @@ -143,6 +143,9 @@ return func; } #else +#if defined(_MSC_VER) + #pragma warning(disable : 4702) // unreachable code +#endif static void* IntGetProcAddress(const char*) { CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support"); diff --git a/modules/core/src/matrix.cpp b/modules/core/src/matrix.cpp index f9c4044614..d9d7ebef97 100644 --- a/modules/core/src/matrix.cpp +++ b/modules/core/src/matrix.cpp @@ -912,7 +912,6 @@ Mat Mat::reshape(int _cn, int _newndims, const int* _newsz) const CV_Error(CV_StsNotImplemented, "Reshaping of n-dimensional non-continuous matrices is not supported yet"); // TBD - return Mat(); } Mat Mat::reshape(int _cn, const std::vector& _newshape) const diff --git a/modules/core/src/matrix_c.cpp b/modules/core/src/matrix_c.cpp index 8b9d29eeee..28583cd05c 100644 --- a/modules/core/src/matrix_c.cpp +++ b/modules/core/src/matrix_c.cpp @@ -179,7 +179,6 @@ Mat cvarrToMat(const CvArr* arr, bool copyData, return buf; } CV_Error(CV_StsBadArg, "Unknown array type"); - return Mat(); } void extractImageCOI(const CvArr* arr, OutputArray _ch, int coi) diff --git a/modules/core/src/matrix_wrap.cpp b/modules/core/src/matrix_wrap.cpp index 3fead37cbb..bea5c670b6 100644 --- a/modules/core/src/matrix_wrap.cpp +++ b/modules/core/src/matrix_wrap.cpp @@ -110,14 +110,12 @@ Mat _InputArray::getMat_(int i) const { CV_Assert( i < 0 ); CV_Error(cv::Error::StsNotImplemented, "You should explicitly call mapHost/unmapHost methods for ogl::Buffer object"); - return Mat(); } if( k == CUDA_GPU_MAT ) { CV_Assert( i < 0 ); CV_Error(cv::Error::StsNotImplemented, "You should explicitly call download method for cuda::GpuMat object"); - return Mat(); } if( k == CUDA_HOST_MEM ) @@ -130,7 +128,6 @@ Mat _InputArray::getMat_(int i) const } CV_Error(Error::StsNotImplemented, "Unknown/unsupported array type"); - return Mat(); } UMat _InputArray::getUMat(int i) const @@ -354,14 +351,12 @@ cuda::GpuMat _InputArray::getGpuMat() const if (k == OPENGL_BUFFER) { CV_Error(cv::Error::StsNotImplemented, "You should explicitly call mapDevice/unmapDevice methods for ogl::Buffer object"); - return cuda::GpuMat(); } if (k == NONE) return cuda::GpuMat(); CV_Error(cv::Error::StsNotImplemented, "getGpuMat is available only for cuda::GpuMat and cuda::HostMem"); - return cuda::GpuMat(); } void _InputArray::getGpuMatVector(std::vector& gpumv) const { @@ -516,7 +511,6 @@ Size _InputArray::size(int i) const } CV_Error(Error::StsNotImplemented, "Unknown/unsupported array type"); - return Size(); } int _InputArray::sizend(int* arrsz, int i) const @@ -716,7 +710,6 @@ int _InputArray::dims(int i) const } CV_Error(Error::StsNotImplemented, "Unknown/unsupported array type"); - return 0; } size_t _InputArray::total(int i) const @@ -845,7 +838,6 @@ int _InputArray::type(int i) const return ((const cuda::HostMem*)obj)->type(); CV_Error(Error::StsNotImplemented, "Unknown/unsupported array type"); - return 0; } int _InputArray::depth(int i) const @@ -928,7 +920,6 @@ bool _InputArray::empty() const return ((const cuda::HostMem*)obj)->empty(); CV_Error(Error::StsNotImplemented, "Unknown/unsupported array type"); - return true; } bool _InputArray::isContinuous(int i) const @@ -970,7 +961,6 @@ bool _InputArray::isContinuous(int i) const return i < 0 ? ((const cuda::GpuMat*)obj)->isContinuous() : true; CV_Error(CV_StsNotImplemented, "Unknown/unsupported array type"); - return false; } bool _InputArray::isSubmatrix(int i) const @@ -1009,7 +999,6 @@ bool _InputArray::isSubmatrix(int i) const } CV_Error(CV_StsNotImplemented, ""); - return false; } size_t _InputArray::offset(int i) const @@ -1074,7 +1063,6 @@ size_t _InputArray::offset(int i) const } CV_Error(Error::StsNotImplemented, ""); - return 0; } size_t _InputArray::step(int i) const @@ -1135,7 +1123,6 @@ size_t _InputArray::step(int i) const } CV_Error(Error::StsNotImplemented, ""); - return 0; } void _InputArray::copyTo(const _OutputArray& arr) const @@ -1459,7 +1446,6 @@ void _OutputArray::create(int d, const int* sizes, int mtype, int i, if( k == NONE ) { CV_Error(CV_StsNullPtr, "create() called for the missing output array" ); - return; } if( k == STD_VECTOR_MAT ) diff --git a/modules/core/src/ocl.cpp b/modules/core/src/ocl.cpp index 7d34580e6c..142f90aeec 100644 --- a/modules/core/src/ocl.cpp +++ b/modules/core/src/ocl.cpp @@ -133,7 +133,7 @@ namespace cv { namespace ocl { int refcount #ifndef HAVE_OPENCL -#define CV_OPENCL_NO_SUPPORT() CV_ErrorNoReturn(cv::Error::OpenCLApiCallError, "OpenCV build without OpenCL support") +#define CV_OPENCL_NO_SUPPORT() CV_Error(cv::Error::OpenCLApiCallError, "OpenCV build without OpenCL support") namespace { struct DummyImpl { @@ -2177,7 +2177,7 @@ struct Context::Impl if (!ptr) { CV_OPENCL_SVM_TRACE_ERROR_P("clSVMAlloc returned NULL...\n"); - CV_ErrorNoReturn(Error::StsBadArg, "clSVMAlloc returned NULL"); + CV_Error(Error::StsBadArg, "clSVMAlloc returned NULL"); } try { @@ -2186,7 +2186,7 @@ struct Context::Impl if (CL_SUCCESS != clEnqueueSVMMap(q, CL_TRUE, CL_MAP_WRITE, ptr, 100, 0, NULL, NULL)) { CV_OPENCL_SVM_TRACE_ERROR_P("clEnqueueSVMMap failed...\n"); - CV_ErrorNoReturn(Error::StsBadArg, "clEnqueueSVMMap FAILED"); + CV_Error(Error::StsBadArg, "clEnqueueSVMMap FAILED"); } clFinish(q); try @@ -2201,12 +2201,12 @@ struct Context::Impl if (CL_SUCCESS != clEnqueueSVMUnmap(q, ptr, 0, NULL, NULL)) { CV_OPENCL_SVM_TRACE_ERROR_P("clEnqueueSVMUnmap failed...\n"); - CV_ErrorNoReturn(Error::StsBadArg, "clEnqueueSVMUnmap FAILED"); + CV_Error(Error::StsBadArg, "clEnqueueSVMUnmap FAILED"); } clFinish(q); if (error) { - CV_ErrorNoReturn(Error::StsBadArg, "OpenCL SVM buffer access test was FAILED"); + CV_Error(Error::StsBadArg, "OpenCL SVM buffer access test was FAILED"); } } catch (...) @@ -2412,7 +2412,7 @@ void Context::setUseSVM(bool enabled) i->svmInit(); if (enabled && !i->svmAvailable) { - CV_ErrorNoReturn(Error::StsError, "OpenCL Shared Virtual Memory (SVM) is not supported by OpenCL device"); + CV_Error(Error::StsError, "OpenCL Shared Virtual Memory (SVM) is not supported by OpenCL device"); } i->svmEnabled = enabled; } @@ -2483,7 +2483,7 @@ void attachContext(const String& platformName, void* platformID, void* context, CV_OCL_CHECK(clGetPlatformIDs(0, 0, &cnt)); if (cnt == 0) - CV_ErrorNoReturn(cv::Error::OpenCLApiCallError, "no OpenCL platform available!"); + CV_Error(cv::Error::OpenCLApiCallError, "no OpenCL platform available!"); std::vector platforms(cnt); @@ -2505,13 +2505,13 @@ void attachContext(const String& platformName, void* platformID, void* context, } if (!platformAvailable) - CV_ErrorNoReturn(cv::Error::OpenCLApiCallError, "No matched platforms available!"); + CV_Error(cv::Error::OpenCLApiCallError, "No matched platforms available!"); // check if platformID corresponds to platformName String actualPlatformName; get_platform_name((cl_platform_id)platformID, actualPlatformName); if (platformName != actualPlatformName) - CV_ErrorNoReturn(cv::Error::OpenCLApiCallError, "No matched platforms available!"); + CV_Error(cv::Error::OpenCLApiCallError, "No matched platforms available!"); // do not initialize OpenCL context Context ctx = Context::getDefault(false); @@ -3305,7 +3305,7 @@ struct ProgramSource::Impl hash = crc64(sourceAddr_, sourceSize_); break; default: - CV_ErrorNoReturn(Error::StsInternal, "Internal error"); + CV_Error(Error::StsInternal, "Internal error"); } sourceHash_ = cv::format("%08llx", hash); isHashUpdated = true; @@ -3427,7 +3427,7 @@ const String& ProgramSource::source() const ProgramSource::hash_t ProgramSource::hash() const { - CV_ErrorNoReturn(Error::StsNotImplemented, "Removed method: ProgramSource::hash()"); + CV_Error(Error::StsNotImplemented, "Removed method: ProgramSource::hash()"); } ProgramSource ProgramSource::fromBinary(const String& module, const String& name, @@ -3597,11 +3597,11 @@ struct Program::Impl } else if (src_->kind_ == ProgramSource::Impl::PROGRAM_SPIRV) { - CV_ErrorNoReturn(Error::StsNotImplemented, "OpenCL: SPIR-V is not supported"); + CV_Error(Error::StsNotImplemented, "OpenCL: SPIR-V is not supported"); } else { - CV_ErrorNoReturn(Error::StsInternal, "Internal error"); + CV_Error(Error::StsInternal, "Internal error"); } CV_Assert(handle != NULL); #if OPENCV_HAVE_FILESYSTEM_SUPPORT @@ -3948,19 +3948,19 @@ void* Program::ptr() const #ifndef OPENCV_REMOVE_DEPRECATED_API const ProgramSource& Program::source() const { - CV_ErrorNoReturn(Error::StsNotImplemented, "Removed API"); + CV_Error(Error::StsNotImplemented, "Removed API"); } bool Program::read(const String& bin, const String& buildflags) { CV_UNUSED(bin); CV_UNUSED(buildflags); - CV_ErrorNoReturn(Error::StsNotImplemented, "Removed API"); + CV_Error(Error::StsNotImplemented, "Removed API"); } bool Program::write(String& bin) const { CV_UNUSED(bin); - CV_ErrorNoReturn(Error::StsNotImplemented, "Removed API"); + CV_Error(Error::StsNotImplemented, "Removed API"); } String Program::getPrefix() const @@ -5627,7 +5627,7 @@ public: } if (id != NULL && strcmp(id, "OCL") != 0) { - CV_ErrorNoReturn(cv::Error::StsBadArg, "getBufferPoolController(): unknown BufferPool ID\n"); + CV_Error(cv::Error::StsBadArg, "getBufferPoolController(): unknown BufferPool ID\n"); } return &bufferPool; } diff --git a/modules/core/src/opencl/runtime/opencl_core.cpp b/modules/core/src/opencl/runtime/opencl_core.cpp index 46b8c14ca4..1d761390cc 100644 --- a/modules/core/src/opencl/runtime/opencl_core.cpp +++ b/modules/core/src/opencl/runtime/opencl_core.cpp @@ -316,7 +316,7 @@ static void* opencl_check_fn(int ID) #endif else { - CV_ErrorNoReturn(cv::Error::StsBadArg, "Invalid function ID"); + CV_Error(cv::Error::StsBadArg, "Invalid function ID"); } void* func = CV_CL_GET_PROC_ADDRESS(e->fnName); if (!func) diff --git a/modules/core/src/opengl.cpp b/modules/core/src/opengl.cpp index 4146e6ab8f..08cfb4ed90 100644 --- a/modules/core/src/opengl.cpp +++ b/modules/core/src/opengl.cpp @@ -48,7 +48,7 @@ # include # endif #else // HAVE_OPENGL -# define NO_OPENGL_SUPPORT_ERROR CV_ErrorNoReturn(cv::Error::StsBadFunc, "OpenCV was build without OpenGL support") +# define NO_OPENGL_SUPPORT_ERROR CV_Error(cv::Error::StsBadFunc, "OpenCV was build without OpenGL support") #endif // HAVE_OPENGL using namespace cv; @@ -1304,10 +1304,15 @@ void cv::ogl::Arrays::release() void cv::ogl::Arrays::setAutoRelease(bool flag) { +#ifndef HAVE_OPENGL + CV_UNUSED(flag); + throw_no_ogl(); +#else vertex_.setAutoRelease(flag); color_.setAutoRelease(flag); normal_.setAutoRelease(flag); texCoord_.setAutoRelease(flag); +#endif } void cv::ogl::Arrays::bind() const @@ -1563,10 +1568,10 @@ void cv::ogl::render(const ogl::Arrays& arr, InputArray indices, int mode, Scala # ifdef cl_khr_gl_sharing # define HAVE_OPENCL_OPENGL_SHARING # else -# define NO_OPENCL_SHARING_ERROR CV_ErrorNoReturn(cv::Error::StsBadFunc, "OpenCV was build without OpenCL/OpenGL sharing support") +# define NO_OPENCL_SHARING_ERROR CV_Error(cv::Error::StsBadFunc, "OpenCV was build without OpenCL/OpenGL sharing support") # endif #else // HAVE_OPENCL -# define NO_OPENCL_SUPPORT_ERROR CV_ErrorNoReturn(cv::Error::StsBadFunc, "OpenCV was build without OpenCL support") +# define NO_OPENCL_SUPPORT_ERROR CV_Error(cv::Error::StsBadFunc, "OpenCV was build without OpenCL support") #endif // HAVE_OPENCL #if defined(HAVE_OPENGL) diff --git a/modules/core/src/parallel.cpp b/modules/core/src/parallel.cpp index 829ccf4585..9fe49d6c75 100644 --- a/modules/core/src/parallel.cpp +++ b/modules/core/src/parallel.cpp @@ -233,7 +233,7 @@ namespace #if CV__EXCEPTION_PTR std::rethrow_exception(pException); #else - CV_ErrorNoReturn(Error::StsError, "Exception in parallel_for() body: " + exception_message); + CV_Error(Error::StsError, "Exception in parallel_for() body: " + exception_message); #endif } } diff --git a/modules/core/src/persistence.cpp b/modules/core/src/persistence.cpp index a163c28dd2..9b67a97e80 100644 --- a/modules/core/src/persistence.cpp +++ b/modules/core/src/persistence.cpp @@ -85,7 +85,7 @@ char* icvGets( CvFileStorage* fs, char* str, int maxCount ) return ptr; } #endif - CV_ErrorNoReturn(CV_StsError, "The storage is not opened"); + CV_Error(CV_StsError, "The storage is not opened"); } int icvEof( CvFileStorage* fs ) diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index 09d4f39247..0569924aee 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -532,7 +532,7 @@ struct HWFeatures "******************************************************************\n"); fprintf(stderr, "\nRequired baseline features:\n"); checkFeatures(baseline_features, sizeof(baseline_features) / sizeof(baseline_features[0]), true); - CV_ErrorNoReturn(cv::Error::StsAssert, "Missing support for required CPU baseline features. Check OpenCV build configuration and required CPU/HW setup."); + CV_Error(cv::Error::StsAssert, "Missing support for required CPU baseline features. Check OpenCV build configuration and required CPU/HW setup."); } readSettings(baseline_features, sizeof(baseline_features) / sizeof(baseline_features[0])); @@ -1567,7 +1567,7 @@ bool utils::getConfigurationParameterBool(const char* name, bool defaultValue) { return false; } - CV_ErrorNoReturn(cv::Error::StsBadArg, cv::format("Invalid value for %s parameter: %s", name, value.c_str())); + CV_Error(cv::Error::StsBadArg, cv::format("Invalid value for %s parameter: %s", name, value.c_str())); } @@ -1598,7 +1598,7 @@ size_t utils::getConfigurationParameterSizeT(const char* name, size_t defaultVal return v * 1024 * 1024; else if (suffixStr == "KB" || suffixStr == "Kb" || suffixStr == "kb") return v * 1024; - CV_ErrorNoReturn(cv::Error::StsBadArg, cv::format("Invalid value for %s parameter: %s", name, value.c_str())); + CV_Error(cv::Error::StsBadArg, cv::format("Invalid value for %s parameter: %s", name, value.c_str())); } cv::String utils::getConfigurationParameterString(const char* name, const char* defaultValue) diff --git a/modules/core/src/umatrix.cpp b/modules/core/src/umatrix.cpp index 2fd0ab96a5..303b6bec12 100644 --- a/modules/core/src/umatrix.cpp +++ b/modules/core/src/umatrix.cpp @@ -837,8 +837,6 @@ UMat UMat::reshape(int _cn, int _newndims, const int* _newsz) const } CV_Error(CV_StsNotImplemented, "Reshaping of n-dimensional non-continuous matrices is not supported yet"); - // TBD - return UMat(); } Mat UMat::getMat(int accessFlags) const diff --git a/modules/core/src/utils/filesystem.cpp b/modules/core/src/utils/filesystem.cpp index d9d72ecedc..d7eeffce23 100644 --- a/modules/core/src/utils/filesystem.cpp +++ b/modules/core/src/utils/filesystem.cpp @@ -265,7 +265,7 @@ struct FileLock::Impl } else { - CV_ErrorNoReturn_(Error::StsAssert, ("Can't open lock file: %s", fname)); + CV_Error_(Error::StsAssert, ("Can't open lock file: %s", fname)); } } break; @@ -517,7 +517,7 @@ cv::String getCacheDirectory(const char* sub_directory_name, const char* configu } #else -#define NOT_IMPLEMENTED CV_ErrorNoReturn(Error::StsNotImplemented, ""); +#define NOT_IMPLEMENTED CV_Error(Error::StsNotImplemented, ""); CV_EXPORTS bool exists(const cv::String& /*path*/) { NOT_IMPLEMENTED } CV_EXPORTS void remove_all(const cv::String& /*path*/) { NOT_IMPLEMENTED } CV_EXPORTS bool createDirectory(const cv::String& /*path*/) { NOT_IMPLEMENTED } diff --git a/modules/core/src/va_intel.cpp b/modules/core/src/va_intel.cpp index 5cdd6e8f3d..0a2bfd96a3 100644 --- a/modules/core/src/va_intel.cpp +++ b/modules/core/src/va_intel.cpp @@ -10,7 +10,7 @@ #ifdef HAVE_VA # include #else // HAVE_VA -# define NO_VA_SUPPORT_ERROR CV_ErrorNoReturn(cv::Error::StsBadFunc, "OpenCV was build without VA support (libva)") +# define NO_VA_SUPPORT_ERROR CV_Error(cv::Error::StsBadFunc, "OpenCV was build without VA support (libva)") #endif // HAVE_VA using namespace cv; diff --git a/modules/core/test/test_eigen.cpp b/modules/core/test/test_eigen.cpp index 8a90c6e325..b2f09ea48c 100644 --- a/modules/core/test/test_eigen.cpp +++ b/modules/core/test/test_eigen.cpp @@ -174,7 +174,6 @@ bool Core_EigenTest::check_pair_count(const cv::Mat& src, const cv::Mat& evalues std::cout << "Number of rows: " << evalues.rows << " Number of cols: " << evalues.cols << endl; std::cout << "Size of src symmetric matrix: " << src.rows << " * " << src.cols << endl; std::cout << endl; CV_Error(CORE_EIGEN_ERROR_COUNT, MESSAGE_ERROR_COUNT); - return false; } return true; } @@ -190,7 +189,6 @@ bool Core_EigenTest::check_pair_count(const cv::Mat& src, const cv::Mat& evalues std::cout << "Number of rows: " << evectors.rows << " Number of cols: " << evectors.cols << endl; std:: cout << "Size of src symmetric matrix: " << src.rows << " * " << src.cols << endl; std::cout << endl; CV_Error (CORE_EIGEN_ERROR_SIZE, MESSAGE_ERROR_SIZE); - return false; } if (!(evalues.rows == right_eigen_pair_count && evalues.cols == 1)) @@ -199,7 +197,6 @@ bool Core_EigenTest::check_pair_count(const cv::Mat& src, const cv::Mat& evalues std::cout << "Number of rows: " << evalues.rows << " Number of cols: " << evalues.cols << endl; std:: cout << "Size of src symmetric matrix: " << src.rows << " * " << src.cols << endl; std::cout << endl; CV_Error (CORE_EIGEN_ERROR_COUNT, MESSAGE_ERROR_COUNT); - return false; } return true; @@ -237,7 +234,6 @@ bool Core_EigenTest::check_orthogonality(const cv::Mat& U) std::cout << endl; std::cout << "Checking orthogonality of matrix " << U << ": "; print_information(i, U, diff, eps_vec); CV_Error(CORE_EIGEN_ERROR_ORTHO, MESSAGE_ERROR_ORTHO); - return false; } } @@ -257,7 +253,6 @@ bool Core_EigenTest::check_pairs_order(const cv::Mat& eigen_values) std::cout << "Pair of indexes with non descending of eigen values: (" << i << ", " << i+1 << ")." << endl; std::cout << endl; CV_Error(CORE_EIGEN_ERROR_ORDER, MESSAGE_ERROR_ORDER); - return false; } break; @@ -272,7 +267,6 @@ bool Core_EigenTest::check_pairs_order(const cv::Mat& eigen_values) std::cout << "Pair of indexes with non descending of eigen values: (" << i << ", " << i+1 << ")." << endl; std::cout << endl; CV_Error(CORE_EIGEN_ERROR_ORDER, "Eigen values are not sorted in descending order."); - return false; } break; @@ -331,7 +325,6 @@ bool Core_EigenTest::test_pairs(const cv::Mat& src) std::cout << endl; std::cout << "Checking accuracy of eigen vectors computing for matrix " << src << ": "; print_information(i, src, diff, eps_vec); CV_Error(CORE_EIGEN_ERROR_DIFF, MESSAGE_ERROR_DIFF_2); - return false; } } @@ -360,7 +353,6 @@ bool Core_EigenTest::test_values(const cv::Mat& src) std::cout << endl; std::cout << "Checking accuracy of eigen values computing for matrix " << src << ": "; print_information(i, src, diff, eps_val); CV_Error(CORE_EIGEN_ERROR_DIFF, MESSAGE_ERROR_DIFF_1); - return false; } } diff --git a/modules/core/test/test_intrin_utils.hpp b/modules/core/test/test_intrin_utils.hpp index 43d8aaff4d..7a21c9eb56 100644 --- a/modules/core/test/test_intrin_utils.hpp +++ b/modules/core/test/test_intrin_utils.hpp @@ -657,8 +657,15 @@ template struct TheTest TheTest & test_mask() { - Data dataA, dataB, dataC, dataD(1), dataE(2); + typedef V_TypeTraits Traits; + typedef typename Traits::int_type int_type; + + Data dataA, dataB(0), dataC, dataD(1), dataE(2); dataA[1] *= (LaneType)-1; + const LaneType mask_one = Traits::reinterpret_from_int(~(typename Traits::uint_type)(0)); + dataB[1] = mask_one; + dataB[R::nlanes / 2] = mask_one; + dataB[R::nlanes - 1] = mask_one; dataC *= (LaneType)-1; R a = dataA, b = dataB, c = dataC, d = dataD, e = dataE; @@ -670,12 +677,9 @@ template struct TheTest EXPECT_EQ(true, v_check_all(c)); EXPECT_EQ(true, v_check_any(a)); - EXPECT_EQ(false, v_check_any(b)); + EXPECT_EQ(true, v_check_any(b)); EXPECT_EQ(true, v_check_any(c)); - typedef V_TypeTraits Traits; - typedef typename Traits::int_type int_type; - R f = v_select(b, d, e); Data resF = f; for (int i = 0; i < R::nlanes; ++i) diff --git a/modules/dnn/include/opencv2/dnn/all_layers.hpp b/modules/dnn/include/opencv2/dnn/all_layers.hpp index 90538424a6..f4e93b7f75 100644 --- a/modules/dnn/include/opencv2/dnn/all_layers.hpp +++ b/modules/dnn/include/opencv2/dnn/all_layers.hpp @@ -555,7 +555,7 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN * An every sample in the batch is normalized separately. Optionally, * output is scaled by the trained parameters. */ - class NormalizeBBoxLayer : public Layer + class CV_EXPORTS NormalizeBBoxLayer : public Layer { public: float pnorm, epsilon; diff --git a/modules/dnn/include/opencv2/dnn/dict.hpp b/modules/dnn/include/opencv2/dnn/dict.hpp index 43cb58a685..69287dc1cc 100644 --- a/modules/dnn/include/opencv2/dnn/dict.hpp +++ b/modules/dnn/include/opencv2/dnn/dict.hpp @@ -142,6 +142,10 @@ public: const T &set(const String &key, const T &value); friend std::ostream &operator<<(std::ostream &stream, const Dict &dict); + + std::map::const_iterator begin() const; + + std::map::const_iterator end() const; }; //! @} diff --git a/modules/dnn/include/opencv2/dnn/dnn.inl.hpp b/modules/dnn/include/opencv2/dnn/dnn.inl.hpp index c30185b27b..4231896187 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.inl.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.inl.hpp @@ -102,9 +102,13 @@ inline int64 DictValue::get(int idx) const return (int64)doubleValue; } + else if (type == Param::STRING) + { + return std::atoi((*ps)[idx].c_str()); + } else { - CV_Assert(isInt() || isReal()); + CV_Assert(isInt() || isReal() || isString()); return 0; } } @@ -146,9 +150,13 @@ inline double DictValue::get(int idx) const { return (double)(*pi)[idx]; } + else if (type == Param::STRING) + { + return std::atof((*ps)[idx].c_str()); + } else { - CV_Assert(isReal() || isInt()); + CV_Assert(isReal() || isInt() || isString()); return 0; } } @@ -261,17 +269,16 @@ inline int DictValue::size() const { case Param::INT: return (int)pi->size(); - break; case Param::STRING: return (int)ps->size(); - break; case Param::REAL: return (int)pd->size(); - break; - default: - CV_Error(Error::StsInternal, ""); - return -1; } +#ifdef __OPENCV_BUILD + CV_Error(Error::StsInternal, ""); +#else + CV_ErrorNoReturn(Error::StsInternal, ""); +#endif } inline std::ostream &operator<<(std::ostream &stream, const DictValue &dictv) @@ -366,6 +373,16 @@ inline std::ostream &operator<<(std::ostream &stream, const Dict &dict) return stream; } +inline std::map::const_iterator Dict::begin() const +{ + return dict.begin(); +} + +inline std::map::const_iterator Dict::end() const +{ + return dict.end(); +} + CV__DNN_EXPERIMENTAL_NS_END } } diff --git a/modules/dnn/include/opencv2/dnn/layer.details.hpp b/modules/dnn/include/opencv2/dnn/layer.details.hpp index 82bd3b11f2..619514e827 100644 --- a/modules/dnn/include/opencv2/dnn/layer.details.hpp +++ b/modules/dnn/include/opencv2/dnn/layer.details.hpp @@ -13,11 +13,11 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN /** @brief Registers layer constructor in runtime. * @param type string, containing type name of the layer. -* @param constuctorFunc pointer to the function of type LayerRegister::Constuctor, which creates the layer. +* @param constructorFunc pointer to the function of type LayerRegister::Constructor, which creates the layer. * @details This macros must be placed inside the function code. */ -#define CV_DNN_REGISTER_LAYER_FUNC(type, constuctorFunc) \ - cv::dnn::LayerFactory::registerLayer(#type, constuctorFunc); +#define CV_DNN_REGISTER_LAYER_FUNC(type, constructorFunc) \ + cv::dnn::LayerFactory::registerLayer(#type, constructorFunc); /** @brief Registers layer class in runtime. * @param type string, containing type name of the layer. @@ -29,11 +29,11 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN /** @brief Registers layer constructor on module load time. * @param type string, containing type name of the layer. -* @param constuctorFunc pointer to the function of type LayerRegister::Constuctor, which creates the layer. +* @param constructorFunc pointer to the function of type LayerRegister::Constructor, which creates the layer. * @details This macros must be placed outside the function code. */ -#define CV_DNN_REGISTER_LAYER_FUNC_STATIC(type, constuctorFunc) \ -static cv::dnn::details::_LayerStaticRegisterer __LayerStaticRegisterer_##type(#type, constuctorFunc); +#define CV_DNN_REGISTER_LAYER_FUNC_STATIC(type, constructorFunc) \ +static cv::dnn::details::_LayerStaticRegisterer __LayerStaticRegisterer_##type(#type, constructorFunc); /** @brief Registers layer class on module load time. * @param type string, containing type name of the layer. @@ -59,10 +59,10 @@ class _LayerStaticRegisterer String type; public: - _LayerStaticRegisterer(const String &layerType, LayerFactory::Constuctor layerConstuctor) + _LayerStaticRegisterer(const String &layerType, LayerFactory::Constructor layerConstructor) { this->type = layerType; - LayerFactory::registerLayer(layerType, layerConstuctor); + LayerFactory::registerLayer(layerType, layerConstructor); } ~_LayerStaticRegisterer() diff --git a/modules/dnn/include/opencv2/dnn/layer.hpp b/modules/dnn/include/opencv2/dnn/layer.hpp index 3fb81f326a..c4712b8e7a 100644 --- a/modules/dnn/include/opencv2/dnn/layer.hpp +++ b/modules/dnn/include/opencv2/dnn/layer.hpp @@ -58,10 +58,10 @@ class CV_EXPORTS LayerFactory public: //! Each Layer class must provide this function to the factory - typedef Ptr(*Constuctor)(LayerParams ¶ms); + typedef Ptr(*Constructor)(LayerParams ¶ms); //! Registers the layer class with typename @p type and specified @p constructor. Thread-safe. - static void registerLayer(const String &type, Constuctor constructor); + static void registerLayer(const String &type, Constructor constructor); //! Unregisters registered layer with specified type name. Thread-safe. static void unregisterLayer(const String &type); diff --git a/modules/dnn/src/caffe/caffe_importer.cpp b/modules/dnn/src/caffe/caffe_importer.cpp index c13d3a55cc..1cc6d285c0 100644 --- a/modules/dnn/src/caffe/caffe_importer.cpp +++ b/modules/dnn/src/caffe/caffe_importer.cpp @@ -103,6 +103,19 @@ public: ReadNetParamsFromBinaryBufferOrDie(dataModel, lenModel, &netBinary); } + void extractCustomParams(const google::protobuf::UnknownFieldSet& unknownFields, cv::dnn::LayerParams ¶ms) + { + const int numFields = unknownFields.field_count(); + for (int i = 0; i < numFields; ++i) + { + const google::protobuf::UnknownField& field = unknownFields.field(i); + CV_Assert(field.type() == google::protobuf::UnknownField::TYPE_GROUP); + std::string fieldName = field.group().field(0).length_delimited(); + std::string fieldValue = field.group().field(1).length_delimited(); + params.set(fieldName, fieldValue); + } + } + void addParam(const Message &msg, const FieldDescriptor *field, cv::dnn::LayerParams ¶ms) { const Reflection *refl = msg.GetReflection(); @@ -187,12 +200,15 @@ public: if (!isInternal && !ends_with_param(fd->name())) continue; + const google::protobuf::UnknownFieldSet& unknownFields = msgRefl->GetUnknownFields(msg); bool hasData = fd->is_required() || (fd->is_optional() && msgRefl->HasField(msg, fd)) || - (fd->is_repeated() && msgRefl->FieldSize(msg, fd) > 0); + (fd->is_repeated() && msgRefl->FieldSize(msg, fd) > 0) || + !unknownFields.empty(); if (!hasData) continue; + extractCustomParams(unknownFields, params); if (fd->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { if (fd->is_repeated()) //Extract only first item! @@ -258,7 +274,7 @@ public: } } - void extractBinaryLayerParms(const caffe::LayerParameter& layer, LayerParams& layerParams) + void extractBinaryLayerParams(const caffe::LayerParameter& layer, LayerParams& layerParams) { const std::string &name = layer.name(); @@ -319,7 +335,7 @@ public: LayerParams layerParams; extractLayerParams(layer, layerParams); - extractBinaryLayerParms(layer, layerParams); + extractBinaryLayerParams(layer, layerParams); int repetitions = layerCounter[name]++; if (repetitions) diff --git a/modules/dnn/src/caffe/caffe_io.cpp b/modules/dnn/src/caffe/caffe_io.cpp index f6b4eb373a..7fc5fea37c 100644 --- a/modules/dnn/src/caffe/caffe_io.cpp +++ b/modules/dnn/src/caffe/caffe_io.cpp @@ -1120,7 +1120,7 @@ bool ReadProtoFromTextFile(const char* filename, Message* proto) { std::ifstream fs(filename, std::ifstream::in); CHECK(fs.is_open()) << "Can't open \"" << filename << "\""; IstreamInputStream input(&fs); - return google::protobuf::TextFormat::Parse(&input, proto); + return google::protobuf::TextFormat::Parser(true).Parse(&input, proto); } bool ReadProtoFromBinaryFile(const char* filename, Message* proto) { diff --git a/modules/dnn/src/dnn.cpp b/modules/dnn/src/dnn.cpp index 86da4c0856..3f753ecec6 100644 --- a/modules/dnn/src/dnn.cpp +++ b/modules/dnn/src/dnn.cpp @@ -1941,7 +1941,7 @@ Net::Net() : impl(new Net::Impl) Net Net::readFromModelOptimizer(const String& xml, const String& bin) { #ifndef HAVE_INF_ENGINE - CV_ErrorNoReturn(Error::StsError, "Build OpenCV with Inference Engine to enable loading models from Model Optimizer."); + CV_Error(Error::StsError, "Build OpenCV with Inference Engine to enable loading models from Model Optimizer."); #else InferenceEngine::CNNNetReader reader; reader.ReadNetwork(xml); @@ -2790,7 +2790,7 @@ static Mutex& getLayerFactoryMutex() return *instance; } -typedef std::map LayerFactory_Impl; +typedef std::map > LayerFactory_Impl; static LayerFactory_Impl& getLayerFactoryImpl_() { @@ -2813,21 +2813,22 @@ static LayerFactory_Impl& getLayerFactoryImpl() return *instance; } -void LayerFactory::registerLayer(const String &type, Constuctor constructor) +void LayerFactory::registerLayer(const String &type, Constructor constructor) { CV_TRACE_FUNCTION(); CV_TRACE_ARG_VALUE(type, "type", type.c_str()); cv::AutoLock lock(getLayerFactoryMutex()); String type_ = type.toLowerCase(); - LayerFactory_Impl::const_iterator it = getLayerFactoryImpl().find(type_); + LayerFactory_Impl::iterator it = getLayerFactoryImpl().find(type_); - if (it != getLayerFactoryImpl().end() && it->second != constructor) + if (it != getLayerFactoryImpl().end()) { - CV_Error(cv::Error::StsBadArg, "Layer \"" + type_ + "\" already was registered"); + if (it->second.back() == constructor) + CV_Error(cv::Error::StsBadArg, "Layer \"" + type_ + "\" already was registered"); + it->second.push_back(constructor); } - - getLayerFactoryImpl().insert(std::make_pair(type_, constructor)); + getLayerFactoryImpl().insert(std::make_pair(type_, std::vector(1, constructor))); } void LayerFactory::unregisterLayer(const String &type) @@ -2837,7 +2838,15 @@ void LayerFactory::unregisterLayer(const String &type) cv::AutoLock lock(getLayerFactoryMutex()); String type_ = type.toLowerCase(); - getLayerFactoryImpl().erase(type_); + + LayerFactory_Impl::iterator it = getLayerFactoryImpl().find(type_); + if (it != getLayerFactoryImpl().end()) + { + if (it->second.size() > 1) + it->second.pop_back(); + else + getLayerFactoryImpl().erase(it); + } } Ptr LayerFactory::createLayerInstance(const String &type, LayerParams& params) @@ -2851,7 +2860,8 @@ Ptr LayerFactory::createLayerInstance(const String &type, LayerParams& pa if (it != getLayerFactoryImpl().end()) { - return it->second(params); + CV_Assert(!it->second.empty()); + return it->second.back()(params); } else { @@ -2920,7 +2930,7 @@ Net readNet(const String& _model, const String& _config, const String& _framewor std::swap(model, config); return readNetFromModelOptimizer(config, model); } - CV_ErrorNoReturn(Error::StsError, "Cannot determine an origin framework of files: " + + CV_Error(Error::StsError, "Cannot determine an origin framework of files: " + model + (config.empty() ? "" : ", " + config)); } diff --git a/modules/dnn/src/layers/detection_output_layer.cpp b/modules/dnn/src/layers/detection_output_layer.cpp index c6b711f841..5c4be180a1 100644 --- a/modules/dnn/src/layers/detection_output_layer.cpp +++ b/modules/dnn/src/layers/detection_output_layer.cpp @@ -151,7 +151,7 @@ public: message += " layer parameter does not contain "; message += parameterName; message += " parameter."; - CV_ErrorNoReturn(Error::StsBadArg, message); + CV_Error(Error::StsBadArg, message); } else { @@ -471,12 +471,12 @@ public: { int label = it->first; if (confidenceScores.rows <= label) - CV_ErrorNoReturn_(cv::Error::StsError, ("Could not find confidence predictions for label %d", label)); + CV_Error_(cv::Error::StsError, ("Could not find confidence predictions for label %d", label)); const std::vector& scores = confidenceScores.row(label); int locLabel = _shareLocation ? -1 : label; LabelBBox::const_iterator label_bboxes = decodeBBoxes.find(locLabel); if (label_bboxes == decodeBBoxes.end()) - CV_ErrorNoReturn_(cv::Error::StsError, ("Could not find location predictions for label %d", locLabel)); + CV_Error_(cv::Error::StsError, ("Could not find location predictions for label %d", locLabel)); const std::vector& indices = it->second; for (size_t j = 0; j < indices.size(); ++j, ++count) @@ -507,14 +507,14 @@ public: if (c == _backgroundLabelId) continue; // Ignore background class. if (c >= confidenceScores.rows) - CV_ErrorNoReturn_(cv::Error::StsError, ("Could not find confidence predictions for label %d", c)); + CV_Error_(cv::Error::StsError, ("Could not find confidence predictions for label %d", c)); const std::vector scores = confidenceScores.row(c); int label = _shareLocation ? -1 : c; LabelBBox::const_iterator label_bboxes = decodeBBoxes.find(label); if (label_bboxes == decodeBBoxes.end()) - CV_ErrorNoReturn_(cv::Error::StsError, ("Could not find location predictions for label %d", label)); + CV_Error_(cv::Error::StsError, ("Could not find location predictions for label %d", label)); if (_bboxesNormalized) NMSFast_(label_bboxes->second, scores, _confidenceThreshold, _nmsThreshold, 1.0, _topK, indices[c], util::caffe_norm_box_overlap); @@ -532,7 +532,7 @@ public: int label = it->first; const std::vector& labelIndices = it->second; if (label >= confidenceScores.rows) - CV_ErrorNoReturn_(cv::Error::StsError, ("Could not find location predictions for label %d", label)); + CV_Error_(cv::Error::StsError, ("Could not find location predictions for label %d", label)); const std::vector& scores = confidenceScores.row(label); for (size_t j = 0; j < labelIndices.size(); ++j) { @@ -645,7 +645,7 @@ public: decode_bbox.ymax = decode_bbox_center_y + decode_bbox_height * .5; } else - CV_ErrorNoReturn(Error::StsBadArg, "Unknown type."); + CV_Error(Error::StsBadArg, "Unknown type."); if (clip_bbox) { @@ -714,7 +714,7 @@ public: continue; // Ignore background class. LabelBBox::const_iterator label_loc_preds = loc_preds.find(label); if (label_loc_preds == loc_preds.end()) - CV_ErrorNoReturn_(cv::Error::StsError, ("Could not find location predictions for label %d", label)); + CV_Error_(cv::Error::StsError, ("Could not find location predictions for label %d", label)); DecodeBBoxes(prior_bboxes, prior_variances, code_type, variance_encoded_in_target, clip, clip_bounds, normalized_bbox, label_loc_preds->second, decode_bboxes[label]); diff --git a/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp b/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp index 80bb6b5367..8dfa0cdb62 100644 --- a/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp +++ b/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp @@ -89,7 +89,7 @@ public: if (net.node(i).name() == name) return net.node(i); } - CV_ErrorNoReturn(Error::StsParseError, "Input node with name " + name + " not found"); + CV_Error(Error::StsParseError, "Input node with name " + name + " not found"); } // Match TensorFlow subgraph starting from with a set of nodes to be fused. diff --git a/modules/dnn/src/tensorflow/tf_importer.cpp b/modules/dnn/src/tensorflow/tf_importer.cpp index a401f715be..ea5d1e7957 100644 --- a/modules/dnn/src/tensorflow/tf_importer.cpp +++ b/modules/dnn/src/tensorflow/tf_importer.cpp @@ -1564,8 +1564,44 @@ void TFImporter::populateNet(Net dstNet) } else { - printLayerAttr(layer); - CV_Error_(Error::StsError, ("Unknown layer type %s in op %s", type.c_str(), name.c_str())); + // Importer does not know how to map this TensorFlow's operation onto OpenCV's layer. + // However we create a layer with the same type and rely that user defined a custom layer. + + // All the attributes are added to LayerParams. + google::protobuf::Map attr = layer.attr(); + for (google::protobuf::Map::const_iterator ai = attr.begin(); + ai != attr.end(); ++ai) + { + if (ai->second.value_case() == tensorflow::AttrValue::kS) // string + layerParams.set(ai->first, ai->second.s()); + if (ai->second.value_case() == tensorflow::AttrValue::kI) // int64 + layerParams.set(ai->first, ai->second.i()); + if (ai->second.value_case() == tensorflow::AttrValue::kF) // float + layerParams.set(ai->first, ai->second.f()); + if (ai->second.value_case() == tensorflow::AttrValue::kB) // bool + layerParams.set(ai->first, ai->second.b()); + } + + // All the Const input nodes are added to layer's blobs. + std::vector inputsNames; + for (int i = 0; i < layer.input_size(); ++i) + { + // Check if input is a Const node. + if (value_id.find(layer.input(i)) != value_id.end()) + { + Mat blob = getTensorContent(getConstBlob(layer, value_id, i)); + layerParams.blobs.push_back(blob); + } + else + inputsNames.push_back(layer.input(i)); + } + int id = dstNet.addLayer(name, type, layerParams); + layer_id[name] = id; + + for (int i = 0; i < inputsNames.size(); ++i) + { + connect(layer_id, dstNet, parsePin(inputsNames[i]), id, i); + } } } } diff --git a/modules/dnn/src/torch/torch_importer.cpp b/modules/dnn/src/torch/torch_importer.cpp index 83e4a48be7..813ee085cb 100644 --- a/modules/dnn/src/torch/torch_importer.cpp +++ b/modules/dnn/src/torch/torch_importer.cpp @@ -940,7 +940,21 @@ struct TorchImporter } else { - CV_Error(Error::StsNotImplemented, "Unknown nn class \"" + className + "\""); + // Importer does not know how to map Torch's layer type to an OpenCV's one. + // However we parse all the parameters to let user create a custom layer. + readTorchTable(scalarParams, tensorParams); + for (std::map::const_iterator it = scalarParams.begin(); + it != scalarParams.end(); ++it) + { + layerParams.set(it->first, it->second); + } + for (std::map >::iterator it = tensorParams.begin(); + it != tensorParams.end(); ++it) + { + layerParams.blobs.push_back(it->second.second); + } + newModule->apiType = nnName; + curModule->modules.push_back(newModule); } } else diff --git a/modules/dnn/test/test_layers.cpp b/modules/dnn/test/test_layers.cpp index 413e5275c9..89c6ed8915 100644 --- a/modules/dnn/test/test_layers.cpp +++ b/modules/dnn/test/test_layers.cpp @@ -44,7 +44,7 @@ #include "npy_blob.hpp" #include #include -#include +#include // CV_DNN_REGISTER_LAYER_CLASS namespace opencv_test { namespace { @@ -117,94 +117,50 @@ void testLayerUsingCaffeModels(String basename, int targetId = DNN_TARGET_CPU, normAssert(ref, out); } -TEST(Layer_Test_Softmax, Accuracy) +typedef testing::TestWithParam Test_Caffe_layers; +TEST_P(Test_Caffe_layers, Softmax) { - testLayerUsingCaffeModels("layer_softmax"); + testLayerUsingCaffeModels("layer_softmax", GetParam()); } -OCL_TEST(Layer_Test_Softmax, Accuracy) +TEST_P(Test_Caffe_layers, LRN_spatial) { - testLayerUsingCaffeModels("layer_softmax", DNN_TARGET_OPENCL); + testLayerUsingCaffeModels("layer_lrn_spatial", GetParam()); } -TEST(Layer_Test_LRN_spatial, Accuracy) +TEST_P(Test_Caffe_layers, LRN_channels) { - testLayerUsingCaffeModels("layer_lrn_spatial"); + testLayerUsingCaffeModels("layer_lrn_channels", GetParam()); } -OCL_TEST(Layer_Test_LRN_spatial, Accuracy) +TEST_P(Test_Caffe_layers, Convolution) { - testLayerUsingCaffeModels("layer_lrn_spatial", DNN_TARGET_OPENCL); + testLayerUsingCaffeModels("layer_convolution", GetParam(), true); } -TEST(Layer_Test_LRN_channels, Accuracy) +TEST_P(Test_Caffe_layers, DeConvolution) { - testLayerUsingCaffeModels("layer_lrn_channels"); + testLayerUsingCaffeModels("layer_deconvolution", GetParam(), true, false); } -OCL_TEST(Layer_Test_LRN_channels, Accuracy) +TEST_P(Test_Caffe_layers, InnerProduct) { - testLayerUsingCaffeModels("layer_lrn_channels", DNN_TARGET_OPENCL); + testLayerUsingCaffeModels("layer_inner_product", GetParam(), true); } -TEST(Layer_Test_Convolution, Accuracy) +TEST_P(Test_Caffe_layers, Pooling_max) { - testLayerUsingCaffeModels("layer_convolution", DNN_TARGET_CPU, true); + testLayerUsingCaffeModels("layer_pooling_max", GetParam()); } -OCL_TEST(Layer_Test_Convolution, Accuracy) +TEST_P(Test_Caffe_layers, Pooling_ave) { - testLayerUsingCaffeModels("layer_convolution", DNN_TARGET_OPENCL, true); + testLayerUsingCaffeModels("layer_pooling_ave", GetParam()); } -TEST(Layer_Test_DeConvolution, Accuracy) +TEST_P(Test_Caffe_layers, MVN) { - testLayerUsingCaffeModels("layer_deconvolution", DNN_TARGET_CPU, true, false); -} - -OCL_TEST(Layer_Test_DeConvolution, Accuracy) -{ - testLayerUsingCaffeModels("layer_deconvolution", DNN_TARGET_OPENCL, true, false); -} - -TEST(Layer_Test_InnerProduct, Accuracy) -{ - testLayerUsingCaffeModels("layer_inner_product", DNN_TARGET_CPU, true); -} - -OCL_TEST(Layer_Test_InnerProduct, Accuracy) -{ - testLayerUsingCaffeModels("layer_inner_product", DNN_TARGET_OPENCL, true); -} - -TEST(Layer_Test_Pooling_max, Accuracy) -{ - testLayerUsingCaffeModels("layer_pooling_max"); -} - -OCL_TEST(Layer_Test_Pooling_max, Accuracy) -{ - testLayerUsingCaffeModels("layer_pooling_max", DNN_TARGET_OPENCL); -} - -TEST(Layer_Test_Pooling_ave, Accuracy) -{ - testLayerUsingCaffeModels("layer_pooling_ave"); -} - -OCL_TEST(Layer_Test_Pooling_ave, Accuracy) -{ - testLayerUsingCaffeModels("layer_pooling_ave", DNN_TARGET_OPENCL); -} - -TEST(Layer_Test_MVN, Accuracy) -{ - testLayerUsingCaffeModels("layer_mvn"); -} - -OCL_TEST(Layer_Test_MVN, Accuracy) -{ - testLayerUsingCaffeModels("layer_mvn", DNN_TARGET_OPENCL); + testLayerUsingCaffeModels("layer_mvn", GetParam()); } void testReshape(const MatShape& inputShape, const MatShape& targetShape, @@ -257,14 +213,9 @@ TEST(Layer_Test_BatchNorm, local_stats) testLayerUsingCaffeModels("layer_batch_norm_local_stats", DNN_TARGET_CPU, true, false); } -TEST(Layer_Test_ReLU, Accuracy) +TEST_P(Test_Caffe_layers, ReLU) { - testLayerUsingCaffeModels("layer_relu"); -} - -OCL_TEST(Layer_Test_ReLU, Accuracy) -{ - testLayerUsingCaffeModels("layer_relu", DNN_TARGET_OPENCL); + testLayerUsingCaffeModels("layer_relu", GetParam()); } TEST(Layer_Test_Dropout, Accuracy) @@ -272,14 +223,9 @@ TEST(Layer_Test_Dropout, Accuracy) testLayerUsingCaffeModels("layer_dropout"); } -TEST(Layer_Test_Concat, Accuracy) +TEST_P(Test_Caffe_layers, Concat) { - testLayerUsingCaffeModels("layer_concat"); -} - -OCL_TEST(Layer_Test_Concat, Accuracy) -{ - testLayerUsingCaffeModels("layer_concat", DNN_TARGET_OPENCL); + testLayerUsingCaffeModels("layer_concat", GetParam()); } TEST(Layer_Test_Fused_Concat, Accuracy) @@ -325,26 +271,16 @@ TEST(Layer_Test_Fused_Concat, Accuracy) testLayerUsingCaffeModels("layer_concat_shared_input", DNN_TARGET_CPU, true, false); } -TEST(Layer_Test_Eltwise, Accuracy) +TEST_P(Test_Caffe_layers, Eltwise) { - testLayerUsingCaffeModels("layer_eltwise"); + testLayerUsingCaffeModels("layer_eltwise", GetParam()); } -OCL_TEST(Layer_Test_Eltwise, Accuracy) +TEST_P(Test_Caffe_layers, PReLU) { - testLayerUsingCaffeModels("layer_eltwise", DNN_TARGET_OPENCL); -} - -TEST(Layer_Test_PReLU, Accuracy) -{ - testLayerUsingCaffeModels("layer_prelu", DNN_TARGET_CPU, true); - testLayerUsingCaffeModels("layer_prelu_fc", DNN_TARGET_CPU, true, false); -} - -OCL_TEST(Layer_Test_PReLU, Accuracy) -{ - testLayerUsingCaffeModels("layer_prelu", DNN_TARGET_OPENCL, true); - testLayerUsingCaffeModels("layer_prelu_fc", DNN_TARGET_OPENCL, true, false); + int targetId = GetParam(); + testLayerUsingCaffeModels("layer_prelu", targetId, true); + testLayerUsingCaffeModels("layer_prelu_fc", targetId, true, false); } //template @@ -385,14 +321,9 @@ static void test_Reshape_Split_Slice_layers(int targetId) normAssert(input, output); } -TEST(Layer_Test_Reshape_Split_Slice, Accuracy) +TEST_P(Test_Caffe_layers, Reshape_Split_Slice) { - test_Reshape_Split_Slice_layers(DNN_TARGET_CPU); -} - -OCL_TEST(Layer_Test_Reshape_Split_Slice, Accuracy) -{ - test_Reshape_Split_Slice_layers(DNN_TARGET_OPENCL); + test_Reshape_Split_Slice_layers(GetParam()); } TEST(Layer_Conv_Elu, Accuracy) @@ -602,7 +533,6 @@ TEST(Layer_Test_ROIPooling, Accuracy) normAssert(out, ref); } -typedef testing::TestWithParam Test_Caffe_layers; TEST_P(Test_Caffe_layers, FasterRCNN_Proposal) { Net net = readNetFromCaffe(_tf("net_faster_rcnn_proposal.prototxt")); @@ -906,4 +836,104 @@ TEST(Test_DLDT, two_inputs) } #endif // HAVE_INF_ENGINE +// Test a custom layer. +class InterpLayer CV_FINAL : public Layer +{ +public: + InterpLayer(const LayerParams ¶ms) : Layer(params) + { + zoomFactor = params.get("zoom_factor", 0); + outWidth = params.get("width", 0); + outHeight = params.get("height", 0); + } + + static Ptr create(LayerParams& params) + { + return Ptr(new InterpLayer(params)); + } + + virtual bool getMemoryShapes(const std::vector > &inputs, + const int requiredOutputs, + std::vector > &outputs, + std::vector > &internals) const CV_OVERRIDE + { + const int batchSize = inputs[0][0]; + const int numChannels = inputs[0][1]; + const int inpHeight = inputs[0][2]; + const int inpWidth = inputs[0][3]; + + std::vector outShape(4); + outShape[0] = batchSize; + outShape[1] = numChannels; + outShape[2] = outHeight != 0 ? outHeight : (inpHeight + (inpHeight - 1) * (zoomFactor - 1)); + outShape[3] = outWidth != 0 ? outWidth : (inpWidth + (inpWidth - 1) * (zoomFactor - 1)); + outputs.assign(1, outShape); + return false; + } + + virtual void finalize(const std::vector& inputs, std::vector &outputs) CV_OVERRIDE + { + if (!outWidth && !outHeight) + { + outHeight = outputs[0].size[2]; + outWidth = outputs[0].size[3]; + } + } + + // Implementation of this custom layer is based on https://github.com/cdmh/deeplab-public/blob/master/src/caffe/layers/interp_layer.cpp + virtual void forward(std::vector &inputs, std::vector &outputs, std::vector& internals) CV_OVERRIDE + { + Mat& inp = *inputs[0]; + Mat& out = outputs[0]; + const float* inpData = (float*)inp.data; + float* outData = (float*)out.data; + + const int batchSize = inp.size[0]; + const int numChannels = inp.size[1]; + const int inpHeight = inp.size[2]; + const int inpWidth = inp.size[3]; + + const float rheight = (outHeight > 1) ? static_cast(inpHeight - 1) / (outHeight - 1) : 0.f; + const float rwidth = (outWidth > 1) ? static_cast(inpWidth - 1) / (outWidth - 1) : 0.f; + for (int h2 = 0; h2 < outHeight; ++h2) + { + const float h1r = rheight * h2; + const int h1 = h1r; + const int h1p = (h1 < inpHeight - 1) ? 1 : 0; + const float h1lambda = h1r - h1; + const float h0lambda = 1.f - h1lambda; + for (int w2 = 0; w2 < outWidth; ++w2) + { + const float w1r = rwidth * w2; + const int w1 = w1r; + const int w1p = (w1 < inpWidth - 1) ? 1 : 0; + const float w1lambda = w1r - w1; + const float w0lambda = 1.f - w1lambda; + const float* pos1 = inpData + h1 * inpWidth + w1; + float* pos2 = outData + h2 * outWidth + w2; + for (int c = 0; c < batchSize * numChannels; ++c) + { + pos2[0] = + h0lambda * (w0lambda * pos1[0] + w1lambda * pos1[w1p]) + + h1lambda * (w0lambda * pos1[h1p * inpWidth] + w1lambda * pos1[h1p * inpWidth + w1p]); + pos1 += inpWidth * inpHeight; + pos2 += outWidth * outHeight; + } + } + } + } + + virtual void forward(InputArrayOfArrays, OutputArrayOfArrays, OutputArrayOfArrays) CV_OVERRIDE {} + +private: + int outWidth, outHeight, zoomFactor; +}; + +TEST(Layer_Test_Interp, Accuracy) +{ + CV_DNN_REGISTER_LAYER_CLASS(Interp, InterpLayer); + testLayerUsingCaffeModels("layer_interp", DNN_TARGET_CPU, false, false); + LayerFactory::unregisterLayer("Interp"); +} + }} // namespace diff --git a/modules/dnn/test/test_misc.cpp b/modules/dnn/test/test_misc.cpp index 2e925049df..b2fb71bcaf 100644 --- a/modules/dnn/test/test_misc.cpp +++ b/modules/dnn/test/test_misc.cpp @@ -7,6 +7,8 @@ #include "test_precomp.hpp" +#include // CV_DNN_REGISTER_LAYER_CLASS + namespace opencv_test { namespace { TEST(blobFromImage_4ch, Regression) @@ -75,4 +77,64 @@ TEST(readNet, Regression) EXPECT_FALSE(net.empty()); } +class FirstCustomLayer CV_FINAL : public Layer +{ +public: + FirstCustomLayer(const LayerParams ¶ms) : Layer(params) {} + + static Ptr create(LayerParams& params) + { + return Ptr(new FirstCustomLayer(params)); + } + + virtual void forward(InputArrayOfArrays, OutputArrayOfArrays, OutputArrayOfArrays) CV_OVERRIDE {} + virtual void forward(std::vector &inputs, std::vector &outputs, std::vector& internals) CV_OVERRIDE + { + outputs[0].setTo(1); + } +}; + +class SecondCustomLayer CV_FINAL : public Layer +{ +public: + SecondCustomLayer(const LayerParams ¶ms) : Layer(params) {} + + static Ptr create(LayerParams& params) + { + return Ptr(new SecondCustomLayer(params)); + } + + virtual void forward(InputArrayOfArrays, OutputArrayOfArrays, OutputArrayOfArrays) CV_OVERRIDE {} + virtual void forward(std::vector &inputs, std::vector &outputs, std::vector& internals) CV_OVERRIDE + { + outputs[0].setTo(2); + } +}; + +TEST(LayerFactory, custom_layers) +{ + LayerParams lp; + lp.name = "name"; + lp.type = "CustomType"; + + Mat inp(1, 1, CV_32FC1); + for (int i = 0; i < 3; ++i) + { + if (i == 0) { CV_DNN_REGISTER_LAYER_CLASS(CustomType, FirstCustomLayer); } + else if (i == 1) { CV_DNN_REGISTER_LAYER_CLASS(CustomType, SecondCustomLayer); } + else if (i == 2) { LayerFactory::unregisterLayer("CustomType"); } + + Net net; + net.addLayerToPrev(lp.name, lp.type, lp); + + net.setInput(inp); + Mat output = net.forward(); + + if (i == 0) EXPECT_EQ(output.at(0), 1); + else if (i == 1) EXPECT_EQ(output.at(0), 2); + else if (i == 2) EXPECT_EQ(output.at(0), 1); + } + LayerFactory::unregisterLayer("CustomType"); +} + }} // namespace diff --git a/modules/dnn/test/test_tf_importer.cpp b/modules/dnn/test/test_tf_importer.cpp index b7f3c5c55a..397aadfa08 100644 --- a/modules/dnn/test/test_tf_importer.cpp +++ b/modules/dnn/test/test_tf_importer.cpp @@ -12,6 +12,8 @@ Test for Tensorflow models loading #include "test_precomp.hpp" #include "npy_blob.hpp" +#include // CV_DNN_REGISTER_LAYER_CLASS + namespace opencv_test { @@ -364,4 +366,95 @@ TEST(Test_TensorFlow, memory_read) runTensorFlowNet("batch_norm_text", DNN_TARGET_CPU, true, l1, lInf, true); } +// Test a custom layer. +class ResizeBilinearLayer CV_FINAL : public Layer +{ +public: + ResizeBilinearLayer(const LayerParams ¶ms) : Layer(params) + { + CV_Assert(!params.get("align_corners", false)); + CV_Assert(blobs.size() == 1, blobs[0].type() == CV_32SC1); + outHeight = blobs[0].at(0, 0); + outWidth = blobs[0].at(0, 1); + } + + static Ptr create(LayerParams& params) + { + return Ptr(new ResizeBilinearLayer(params)); + } + + virtual bool getMemoryShapes(const std::vector > &inputs, + const int requiredOutputs, + std::vector > &outputs, + std::vector > &internals) const CV_OVERRIDE + { + std::vector outShape(4); + outShape[0] = inputs[0][0]; // batch size + outShape[1] = inputs[0][1]; // number of channels + outShape[2] = outHeight; + outShape[3] = outWidth; + outputs.assign(1, outShape); + return false; + } + + // This implementation is based on a reference implementation from + // https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/lite/kernels/internal/reference/reference_ops.h + virtual void forward(std::vector &inputs, std::vector &outputs, std::vector &internals) CV_OVERRIDE + { + Mat& inp = *inputs[0]; + Mat& out = outputs[0]; + const float* inpData = (float*)inp.data; + float* outData = (float*)out.data; + + const int batchSize = inp.size[0]; + const int numChannels = inp.size[1]; + const int inpHeight = inp.size[2]; + const int inpWidth = inp.size[3]; + + float heightScale = static_cast(inpHeight) / outHeight; + float widthScale = static_cast(inpWidth) / outWidth; + for (int b = 0; b < batchSize; ++b) + { + for (int y = 0; y < outHeight; ++y) + { + float input_y = y * heightScale; + int y0 = static_cast(std::floor(input_y)); + int y1 = std::min(y0 + 1, inpHeight - 1); + for (int x = 0; x < outWidth; ++x) + { + float input_x = x * widthScale; + int x0 = static_cast(std::floor(input_x)); + int x1 = std::min(x0 + 1, inpWidth - 1); + for (int c = 0; c < numChannels; ++c) + { + float interpolation = + inpData[offset(inp.size, c, x0, y0, b)] * (1 - (input_y - y0)) * (1 - (input_x - x0)) + + inpData[offset(inp.size, c, x0, y1, b)] * (input_y - y0) * (1 - (input_x - x0)) + + inpData[offset(inp.size, c, x1, y0, b)] * (1 - (input_y - y0)) * (input_x - x0) + + inpData[offset(inp.size, c, x1, y1, b)] * (input_y - y0) * (input_x - x0); + outData[offset(out.size, c, x, y, b)] = interpolation; + } + } + } + } + } + + virtual void forward(InputArrayOfArrays, OutputArrayOfArrays, OutputArrayOfArrays) CV_OVERRIDE {} + +private: + static inline int offset(const MatSize& size, int c, int x, int y, int b) + { + return x + size[3] * (y + size[2] * (c + size[1] * b)); + } + + int outWidth, outHeight; +}; + +TEST(Test_TensorFlow, resize_bilinear) +{ + CV_DNN_REGISTER_LAYER_CLASS(ResizeBilinear, ResizeBilinearLayer); + runTensorFlowNet("resize_bilinear"); + LayerFactory::unregisterLayer("ResizeBilinear"); +} + } diff --git a/modules/dnn/test/test_torch_importer.cpp b/modules/dnn/test/test_torch_importer.cpp index f748758dc1..33e0e94801 100644 --- a/modules/dnn/test/test_torch_importer.cpp +++ b/modules/dnn/test/test_torch_importer.cpp @@ -42,6 +42,7 @@ #include "test_precomp.hpp" #include "npy_blob.hpp" #include +#include // CV_DNN_REGISTER_LAYER_CLASS namespace opencv_test { @@ -325,4 +326,62 @@ TEST(Torch_Importer, net_residual) runTorchNet("net_residual", DNN_TARGET_CPU, "", false, true); } +// Test a custom layer +// https://github.com/torch/nn/blob/master/doc/convolution.md#nn.SpatialUpSamplingNearest +class SpatialUpSamplingNearestLayer CV_FINAL : public Layer +{ +public: + SpatialUpSamplingNearestLayer(const LayerParams ¶ms) : Layer(params) + { + scale = params.get("scale_factor"); + } + + static Ptr create(LayerParams& params) + { + return Ptr(new SpatialUpSamplingNearestLayer(params)); + } + + virtual bool getMemoryShapes(const std::vector > &inputs, + const int requiredOutputs, + std::vector > &outputs, + std::vector > &internals) const CV_OVERRIDE + { + std::vector outShape(4); + outShape[0] = inputs[0][0]; // batch size + outShape[1] = inputs[0][1]; // number of channels + outShape[2] = scale * inputs[0][2]; + outShape[3] = scale * inputs[0][3]; + outputs.assign(1, outShape); + return false; + } + + virtual void forward(std::vector &inputs, std::vector &outputs, std::vector &internals) CV_OVERRIDE + { + Mat& inp = *inputs[0]; + Mat& out = outputs[0]; + const int outHeight = out.size[2]; + const int outWidth = out.size[3]; + for (size_t n = 0; n < inputs[0]->size[0]; ++n) + { + for (size_t ch = 0; ch < inputs[0]->size[1]; ++ch) + { + resize(getPlane(inp, n, ch), getPlane(out, n, ch), + Size(outWidth, outHeight), 0, 0, INTER_NEAREST); + } + } + } + + virtual void forward(InputArrayOfArrays, OutputArrayOfArrays, OutputArrayOfArrays) CV_OVERRIDE {} + +private: + int scale; +}; + +TEST(Torch_Importer, upsampling_nearest) +{ + CV_DNN_REGISTER_LAYER_CLASS(SpatialUpSamplingNearest, SpatialUpSamplingNearestLayer); + runTorchNet("net_spatial_upsampling_nearest", DNN_TARGET_CPU, "", false, true); + LayerFactory::unregisterLayer("SpatialUpSamplingNearest"); +} + } diff --git a/modules/features2d/src/matchers.cpp b/modules/features2d/src/matchers.cpp index 6cb7670c69..486b1b916a 100644 --- a/modules/features2d/src/matchers.cpp +++ b/modules/features2d/src/matchers.cpp @@ -1360,11 +1360,13 @@ Ptr FlannBasedMatcher::clone( bool emptyTrainData ) const { CV_Error( Error::StsNotImplemented, "deep clone functionality is not implemented, because " "Flann::Index has not copy constructor or clone method "); +#if 0 //matcher->flannIndex; matcher->addedDescCount = addedDescCount; matcher->mergedDescriptors = DescriptorCollection( mergedDescriptors ); std::transform( trainDescCollection.begin(), trainDescCollection.end(), matcher->trainDescCollection.begin(), clone_op ); +#endif } return matcher; } diff --git a/modules/highgui/src/window.cpp b/modules/highgui/src/window.cpp index 251d1d31da..663b4458fc 100644 --- a/modules/highgui/src/window.cpp +++ b/modules/highgui/src/window.cpp @@ -530,7 +530,6 @@ static const char* NO_QT_ERR_MSG = "The library is compiled without QT support"; cv::QtFont cv::fontQt(const String&, int, Scalar, int, int, int) { CV_Error(CV_StsNotImplemented, NO_QT_ERR_MSG); - return QtFont(); } void cv::addText( const Mat&, const String&, Point, const QtFont&) @@ -556,7 +555,6 @@ void cv::displayOverlay(const String&, const String&, int ) int cv::startLoop(int (*)(int argc, char *argv[]), int , char**) { CV_Error(CV_StsNotImplemented, NO_QT_ERR_MSG); - return 0; } void cv::stopLoop() @@ -577,7 +575,6 @@ void cv::loadWindowParameters(const String&) int cv::createButton(const String&, ButtonCallback, void*, int , bool ) { CV_Error(CV_StsNotImplemented, NO_QT_ERR_MSG); - return 0; } #endif @@ -606,17 +603,16 @@ void cv::setWindowTitle(const String&, const String&) } #define CV_NO_GUI_ERROR(funcname) \ - cvError( CV_StsError, funcname, \ + cv::errorNoReturn(cv::Error::StsError, \ "The function is not implemented. " \ "Rebuild the library with Windows, GTK+ 2.x or Carbon support. "\ "If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script", \ - __FILE__, __LINE__ ) + funcname, __FILE__, __LINE__) CV_IMPL int cvNamedWindow( const char*, int ) { CV_NO_GUI_ERROR("cvNamedWindow"); - return -1; } CV_IMPL void cvDestroyWindow( const char* ) @@ -651,7 +647,6 @@ cvCreateTrackbar( const char*, const char*, int*, int, CvTrackbarCallback ) { CV_NO_GUI_ERROR( "cvCreateTrackbar" ); - return -1; } CV_IMPL int @@ -660,7 +655,6 @@ cvCreateTrackbar2( const char* /*trackbar_name*/, const char* /*window_name*/, void* /*userdata*/ ) { CV_NO_GUI_ERROR( "cvCreateTrackbar2" ); - return -1; } CV_IMPL void @@ -672,7 +666,6 @@ cvSetMouseCallback( const char*, CvMouseCallback, void* ) CV_IMPL int cvGetTrackbarPos( const char*, const char* ) { CV_NO_GUI_ERROR( "cvGetTrackbarPos" ); - return -1; } CV_IMPL void cvSetTrackbarPos( const char*, const char*, int ) @@ -693,33 +686,28 @@ CV_IMPL void cvSetTrackbarMin(const char*, const char*, int) CV_IMPL void* cvGetWindowHandle( const char* ) { CV_NO_GUI_ERROR( "cvGetWindowHandle" ); - return 0; } CV_IMPL const char* cvGetWindowName( void* ) { CV_NO_GUI_ERROR( "cvGetWindowName" ); - return 0; } CV_IMPL int cvWaitKey( int ) { CV_NO_GUI_ERROR( "cvWaitKey" ); - return -1; } CV_IMPL int cvInitSystem( int , char** ) { CV_NO_GUI_ERROR( "cvInitSystem" ); - return -1; } CV_IMPL int cvStartWindowThread() { CV_NO_GUI_ERROR( "cvStartWindowThread" ); - return -1; } //-------- Qt --------- @@ -742,7 +730,6 @@ CV_IMPL int cvStartLoop(int (*)(int argc, char *argv[]), int , char* argv[]) { (void)argv; CV_NO_GUI_ERROR("cvStartLoop"); - return -1; } CV_IMPL void cvStopLoop() @@ -763,7 +750,6 @@ CV_IMPL void cvSaveWindowParameters(const char* ) CV_IMPL int cvCreateButton(const char*, void (*)(int, void*), void*, int, int) { CV_NO_GUI_ERROR("cvCreateButton"); - return -1; } diff --git a/modules/imgcodecs/src/grfmt_bmp.cpp b/modules/imgcodecs/src/grfmt_bmp.cpp index 25a386eaeb..3a6dbfe622 100644 --- a/modules/imgcodecs/src/grfmt_bmp.cpp +++ b/modules/imgcodecs/src/grfmt_bmp.cpp @@ -490,7 +490,7 @@ decode_rle8_bad: ; result = true; break; default: - CV_ErrorNoReturn(cv::Error::StsError, "Invalid/unsupported mode"); + CV_Error(cv::Error::StsError, "Invalid/unsupported mode"); } } CV_CATCH_ALL diff --git a/modules/imgcodecs/src/grfmt_gdal.cpp b/modules/imgcodecs/src/grfmt_gdal.cpp index cd0be3e9dd..17581b576a 100644 --- a/modules/imgcodecs/src/grfmt_gdal.cpp +++ b/modules/imgcodecs/src/grfmt_gdal.cpp @@ -409,7 +409,7 @@ bool GdalDecoder::readData( Mat& img ){ color = 3; break; default: - CV_ErrorNoReturn(cv::Error::StsError, "Invalid/unsupported mode"); + CV_Error(cv::Error::StsError, "Invalid/unsupported mode"); } // make sure the image band has the same dimensions as the image diff --git a/modules/imgcodecs/src/grfmt_pxm.cpp b/modules/imgcodecs/src/grfmt_pxm.cpp index a7da43902f..49ffc9ef59 100644 --- a/modules/imgcodecs/src/grfmt_pxm.cpp +++ b/modules/imgcodecs/src/grfmt_pxm.cpp @@ -77,7 +77,7 @@ static int ReadNumber(RLByteStream& strm, int maxdigits = 0) else { #if 1 - CV_ErrorNoReturn_(Error::StsError, ("PXM: Unexpected code in ReadNumber(): 0x%x (%d)", code, code)); + CV_Error_(Error::StsError, ("PXM: Unexpected code in ReadNumber(): 0x%x (%d)", code, code)); #else code = strm.getByte(); #endif @@ -354,7 +354,7 @@ bool PxMDecoder::readData( Mat& img ) break; } default: - CV_ErrorNoReturn(Error::StsError, "m_bpp is not supported"); + CV_Error(Error::StsError, "m_bpp is not supported"); } } CV_CATCH (cv::Exception, e) diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index 12852d1c48..e6782dcbec 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -722,7 +722,7 @@ bool imwrite( const String& filename, InputArray _img, else if (_img.isMatVector() || _img.isUMatVector()) _img.getMatVector(img_vec); else - CV_ErrorNoReturn(Error::StsBadArg, "Unknown/unsupported input encountered"); + CV_Error(Error::StsBadArg, "Unknown/unsupported input encountered"); CV_Assert(!img_vec.empty()); return imwrite_(filename, img_vec, params, false); diff --git a/modules/imgcodecs/src/rgbe.cpp b/modules/imgcodecs/src/rgbe.cpp index fd957321de..5152795d19 100644 --- a/modules/imgcodecs/src/rgbe.cpp +++ b/modules/imgcodecs/src/rgbe.cpp @@ -99,7 +99,6 @@ static int rgbe_error(int rgbe_error_code, const char *msg) CV_Error(cv::Error::StsError, cv::String("RGBE error: \n") + cv::String(msg)); } - return RGBE_RETURN_FAILURE; } /* standard conversion from float pixels to rgbe pixels */ diff --git a/modules/imgproc/src/colormap.cpp b/modules/imgproc/src/colormap.cpp index 3e5068d488..83e4878e5f 100644 --- a/modules/imgproc/src/colormap.cpp +++ b/modules/imgproc/src/colormap.cpp @@ -126,9 +126,8 @@ static Mat interp1(InputArray _x, InputArray _Y, InputArray _xi) case CV_32SC1: return interp1_(x,Y,xi); break; case CV_32FC1: return interp1_(x,Y,xi); break; case CV_64FC1: return interp1_(x,Y,xi); break; - default: CV_Error(Error::StsUnsupportedFormat, ""); break; } - return Mat(); + CV_Error(Error::StsUnsupportedFormat, ""); } namespace colormap diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp index f428cf3f87..bbc27dca7d 100644 --- a/modules/imgproc/src/connectedcomponents.cpp +++ b/modules/imgproc/src/connectedcomponents.cpp @@ -3980,7 +3980,6 @@ namespace cv{ } CV_Error(CV_StsUnsupportedFormat, "unsupported label/image type"); - return -1; } } @@ -4003,7 +4002,6 @@ int cv::connectedComponents(InputArray img_, OutputArray _labels, int connectivi } else{ CV_Error(CV_StsUnsupportedFormat, "the type of labels must be 16u or 32s"); - return 0; } } diff --git a/modules/imgproc/src/filter.cpp b/modules/imgproc/src/filter.cpp index 5cbaf3f357..194040ffec 100644 --- a/modules/imgproc/src/filter.cpp +++ b/modules/imgproc/src/filter.cpp @@ -3642,8 +3642,6 @@ cv::Ptr cv::getLinearRowFilter( int srcType, int bufType, CV_Error_( CV_StsNotImplemented, ("Unsupported combination of source format (=%d), and buffer format (=%d)", srcType, bufType)); - - return Ptr(); } @@ -3739,8 +3737,6 @@ cv::Ptr cv::getLinearColumnFilter( int bufType, int dstTyp CV_Error_( CV_StsNotImplemented, ("Unsupported combination of buffer format (=%d), and destination format (=%d)", bufType, dstType)); - - return Ptr(); } @@ -4491,8 +4487,6 @@ cv::Ptr cv::getLinearFilter(int srcType, int dstType, CV_Error_( CV_StsNotImplemented, ("Unsupported combination of source format (=%d), and destination format (=%d)", srcType, dstType)); - - return Ptr(); } diff --git a/modules/imgproc/src/morph.cpp b/modules/imgproc/src/morph.cpp index ca92da3244..0c3a1e4088 100644 --- a/modules/imgproc/src/morph.cpp +++ b/modules/imgproc/src/morph.cpp @@ -888,7 +888,6 @@ cv::Ptr cv::getMorphologyRowFilter(int op, int type, int ksiz } CV_Error_( CV_StsNotImplemented, ("Unsupported data type (=%d)", type)); - return Ptr(); } cv::Ptr cv::getMorphologyColumnFilter(int op, int type, int ksize, int anchor) @@ -935,7 +934,6 @@ cv::Ptr cv::getMorphologyColumnFilter(int op, int type, in } CV_Error_( CV_StsNotImplemented, ("Unsupported data type (=%d)", type)); - return Ptr(); } @@ -973,7 +971,6 @@ cv::Ptr cv::getMorphologyFilter(int op, int type, InputArray _ke } CV_Error_( CV_StsNotImplemented, ("Unsupported data type (=%d)", type)); - return Ptr(); } diff --git a/modules/imgproc/src/precomp.hpp b/modules/imgproc/src/precomp.hpp index d0d02e164d..405a799b1e 100644 --- a/modules/imgproc/src/precomp.hpp +++ b/modules/imgproc/src/precomp.hpp @@ -113,9 +113,7 @@ inline bool isStorageOrMat(void * arr) return true; else if (CV_IS_MAT( arr )) return false; - else - CV_Error( CV_StsBadArg, "Destination is not CvMemStorage* nor CvMat*" ); - return false; + CV_Error( CV_StsBadArg, "Destination is not CvMemStorage* nor CvMat*" ); } #endif /*__OPENCV_CV_INTERNAL_H_*/ diff --git a/modules/imgproc/src/smooth.cpp b/modules/imgproc/src/smooth.cpp index 17c1f85156..8cef3824ff 100644 --- a/modules/imgproc/src/smooth.cpp +++ b/modules/imgproc/src/smooth.cpp @@ -1332,8 +1332,6 @@ cv::Ptr cv::getRowSumFilter(int srcType, int sumType, int ksi CV_Error_( CV_StsNotImplemented, ("Unsupported combination of source format (=%d), and buffer format (=%d)", srcType, sumType)); - - return Ptr(); } @@ -1374,8 +1372,6 @@ cv::Ptr cv::getColumnSumFilter(int sumType, int dstType, i CV_Error_( CV_StsNotImplemented, ("Unsupported combination of sum format (=%d), and destination format (=%d)", sumType, dstType)); - - return Ptr(); } @@ -1656,8 +1652,6 @@ static Ptr getSqrRowSumFilter(int srcType, int sumType, int ksize CV_Error_( CV_StsNotImplemented, ("Unsupported combination of source format (=%d), and buffer format (=%d)", srcType, sumType)); - - return Ptr(); } } diff --git a/modules/imgproc/src/undistort.cpp b/modules/imgproc/src/undistort.cpp index a10561d102..d083ceb77b 100644 --- a/modules/imgproc/src/undistort.cpp +++ b/modules/imgproc/src/undistort.cpp @@ -613,7 +613,6 @@ static Point2f mapPointSpherical(const Point2f& p, float alpha, Vec4d* J, int pr return Point2f((float)asin(x1), (float)asin(y1)); } CV_Error(CV_StsBadArg, "Unknown projection type"); - return Point2f(); } diff --git a/modules/imgproc/test/test_boundingrect.cpp b/modules/imgproc/test/test_boundingrect.cpp index 332c593a9a..52a84e5cfe 100644 --- a/modules/imgproc/test/test_boundingrect.cpp +++ b/modules/imgproc/test/test_boundingrect.cpp @@ -125,7 +125,6 @@ template bool CV_BoundingRectTest::checking_function_work(vector

ExposureCompensator::createDefault(int type) if (type == GAIN_BLOCKS) return makePtr(); CV_Error(Error::StsBadArg, "unsupported exposure compensation method"); - return Ptr(); } diff --git a/modules/ts/src/ts.cpp b/modules/ts/src/ts.cpp index 7264a34557..7dd05fbf55 100644 --- a/modules/ts/src/ts.cpp +++ b/modules/ts/src/ts.cpp @@ -836,7 +836,7 @@ std::string findDataFile(const std::string& relative_path, bool required) #endif #endif if (required) - CV_ErrorNoReturn(cv::Error::StsError, cv::format("OpenCV tests: Can't find required data file: %s", relative_path.c_str())); + CV_Error(cv::Error::StsError, cv::format("OpenCV tests: Can't find required data file: %s", relative_path.c_str())); throw SkipTestException(cv::format("OpenCV tests: Can't find data file: %s", relative_path.c_str())); } diff --git a/modules/video/src/ecc.cpp b/modules/video/src/ecc.cpp index 377b775072..aabb317b65 100644 --- a/modules/video/src/ecc.cpp +++ b/modules/video/src/ecc.cpp @@ -404,7 +404,7 @@ double cv::findTransformECC(InputArray templateImage, Mat templateFloat = Mat(hs, ws, CV_32F);// to store the (smoothed) template Mat imageFloat = Mat(hd, wd, CV_32F);// to store the (smoothed) input image Mat imageWarped = Mat(hs, ws, CV_32F);// to store the warped zero-mean input image - Mat imageMask = Mat(hs, ws, CV_8U); //to store the final mask + Mat imageMask = Mat(hs, ws, CV_8U); // to store the final mask Mat inputMaskMat = inputMask.getMat(); //to use it for mask warping diff --git a/modules/videoio/include/opencv2/videoio/container_avi.private.hpp b/modules/videoio/include/opencv2/videoio/container_avi.private.hpp index 212a892699..61ea74dece 100644 --- a/modules/videoio/include/opencv2/videoio/container_avi.private.hpp +++ b/modules/videoio/include/opencv2/videoio/container_avi.private.hpp @@ -106,7 +106,8 @@ protected: bool parseMovi(frame_list& in_frame_list) { //not implemented - in_frame_list.empty(); + CV_UNUSED(in_frame_list); + // FIXIT: in_frame_list.empty(); return true; } bool parseStrl(char stream_id, Codecs codec_); diff --git a/modules/videoio/src/cap.cpp b/modules/videoio/src/cap.cpp index 841db162a9..67e1befca2 100644 --- a/modules/videoio/src/cap.cpp +++ b/modules/videoio/src/cap.cpp @@ -40,6 +40,8 @@ //M*/ #include "precomp.hpp" +#include +using namespace std; #include "cap_intelperc.hpp" #include "cap_dshow.hpp" @@ -63,7 +65,7 @@ #if defined(__clang__) #pragma clang diagnostic ignored "-Wimplicit-fallthrough" #endif -#if defined(__GNUC__) +#if defined(__GNUC__) && __GNUC__ >= 7 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" #endif @@ -200,12 +202,6 @@ CV_IMPL CvCapture * cvCreateCameraCapture (int index) TRY_OPEN(capture, cvCreateCameraCapture_V4L(index)) #endif -#ifdef HAVE_GSTREAMER - TRY_OPEN(capture, cvCreateCapture_GStreamer(CV_CAP_GSTREAMER_V4L2, reinterpret_cast(index))) - - TRY_OPEN(capture, cvCreateCapture_GStreamer(CV_CAP_GSTREAMER_V4L, reinterpret_cast(index))) -#endif - if (pref) break; // CAP_VFW or CAP_V4L or CAP_V4L2 case CAP_FIREWIRE: @@ -221,11 +217,6 @@ CV_IMPL CvCapture * cvCreateCameraCapture (int index) TRY_OPEN(capture, cvCreateCameraCapture_CMU(index)) #endif -#if defined(HAVE_GSTREAMER) && 0 - // Re-enable again when gstreamer 1394 support will land in the backend code - TRY_OPEN(capture, cvCreateCapture_GStreamer(CV_CAP_GSTREAMER_1394, 0)) -#endif - if (pref) break; // CAP_FIREWIRE #ifdef HAVE_MIL @@ -330,12 +321,6 @@ CV_IMPL CvCapture * cvCreateFileCaptureWithPreference (const char * filename, in if (apiPreference) break; #endif -#ifdef HAVE_GSTREAMER - case CAP_GSTREAMER: - TRY_OPEN(result, cvCreateCapture_GStreamer (CV_CAP_GSTREAMER_FILE, filename)) - if (apiPreference) break; -#endif - #if defined(HAVE_QUICKTIME) || defined(HAVE_QTKIT) case CAP_QT: TRY_OPEN(result, cvCreateFileCapture_QT (filename)) @@ -463,6 +448,9 @@ static Ptr IVideoCapture_create(int index) { int domains[] = { +#ifdef HAVE_GSTREAMER + CAP_GSTREAMER, +#endif #ifdef HAVE_DSHOW CAP_DSHOW, #endif @@ -490,7 +478,8 @@ static Ptr IVideoCapture_create(int index) // try every possibly installed camera API for (int i = 0; domains[i] >= 0; i++) { -#if defined(HAVE_DSHOW) || \ +#if defined(HAVE_GSTREAMER) || \ + defined(HAVE_DSHOW) || \ defined(HAVE_INTELPERC) || \ defined(WINRT_VIDEO) || \ defined(HAVE_GPHOTO2) || \ @@ -499,6 +488,11 @@ static Ptr IVideoCapture_create(int index) switch (domains[i]) { +#ifdef HAVE_GSTREAMER + case CAP_GSTREAMER: + capture = createGStreamerCapture(index); + break; +#endif #ifdef HAVE_DSHOW case CAP_DSHOW: capture = makePtr(index); @@ -536,6 +530,14 @@ static Ptr IVideoCapture_create(const String& filename, int apiPr { bool useAny = (apiPreference == CAP_ANY); Ptr capture; +#ifdef HAVE_GSTREAMER + if (useAny || apiPreference == CAP_GSTREAMER) + { + capture = createGStreamerCapture(filename); + if (capture && capture->isOpened()) + return capture; + } +#endif #ifdef HAVE_XINE if (useAny || apiPreference == CAP_XINE) { diff --git a/modules/videoio/src/cap_gstreamer.cpp b/modules/videoio/src/cap_gstreamer.cpp index 3be2a1011d..9c3fd049b7 100644 --- a/modules/videoio/src/cap_gstreamer.cpp +++ b/modules/videoio/src/cap_gstreamer.cpp @@ -48,6 +48,8 @@ * \brief Use GStreamer to read/write video */ #include "precomp.hpp" +#include +using namespace std; #ifndef _MSC_VER #include #endif @@ -72,7 +74,7 @@ #ifdef NDEBUG #define CV_WARN(message) #else -#define CV_WARN(message) fprintf(stderr, "warning: %s (%s:%d)\n", message, __FILE__, __LINE__) +#define CV_WARN(message) fprintf(stderr, "OpenCV | GStreamer warning: %s (%s:%d)\n", message, __FILE__, __LINE__) #endif #if GST_VERSION_MAJOR == 0 @@ -100,6 +102,7 @@ inline char *realpath(const char *path, char *resolved_path) void toFraction(double decimal, double &numerator, double &denominator); void handleMessage(GstElement * pipeline); +using namespace cv; static cv::Mutex gst_initializer_mutex; @@ -120,116 +123,118 @@ private: gst_initializer() { gst_init(NULL, NULL); + guint major, minor, micro, nano; + gst_version(&major, &minor, µ, &nano); + if (GST_VERSION_MAJOR == major) + { + CV_WARN("incompatible gstreamer version"); + } // gst_debug_set_active(1); // gst_debug_set_colored(1); // gst_debug_set_default_threshold(GST_LEVEL_INFO); } }; -/*! - * \brief The CvCapture_GStreamer class - * Use GStreamer to capture video - */ -class CvCapture_GStreamer CV_FINAL : public CvCapture +inline static string get_gst_propname(int propId) { + switch (propId) + { + case CV_CAP_PROP_BRIGHTNESS: return "brightness"; + case CV_CAP_PROP_CONTRAST: return "contrast"; + case CV_CAP_PROP_SATURATION: return "saturation"; + case CV_CAP_PROP_HUE: return "hue"; + default: return string(); + } +} + +inline static bool is_gst_element_exists(const std::string & name) +{ + GstElementFactory * testfac = gst_element_factory_find(name.c_str()); + if (!testfac) + return false; + g_object_unref(G_OBJECT(testfac)); + return true; +} + +//================================================================================================== + +class GStreamerCapture CV_FINAL : public IVideoCapture +{ +private: + GstElement* pipeline; + GstElement* v4l2src; + GstElement* sink; +#if GST_VERSION_MAJOR > 0 + GstSample* sample; +#else + void * sample; // unused + GstBuffer* buffer; +#endif + GstCaps* caps; + gint64 duration; + gint width; + gint height; + gint channels; + double fps; + bool isPosFramesSupported; + bool isPosFramesEmulated; + gint64 emulatedFrameNumber; + bool isOutputByteBuffer; + public: - CvCapture_GStreamer() { init(); } - virtual ~CvCapture_GStreamer() { close(); } - - virtual bool open( int type, const char* filename ); - virtual void close(); - - virtual double getProperty(int) const CV_OVERRIDE; - virtual bool setProperty(int, double) CV_OVERRIDE; + GStreamerCapture(); + ~GStreamerCapture(); virtual bool grabFrame() CV_OVERRIDE; - virtual IplImage* retrieveFrame(int) CV_OVERRIDE; + virtual bool retrieveFrame(int /*unused*/, OutputArray dst) CV_OVERRIDE; + virtual double getProperty(int propId) const CV_OVERRIDE; + virtual bool setProperty(int propId, double value) CV_OVERRIDE; + virtual bool isOpened() const CV_OVERRIDE; + virtual int getCaptureDomain() CV_OVERRIDE; // Return the type of the capture object: CAP_VFW, etc... + bool open(int id); + bool open(const String &filename_); + static void newPad(GstElement * /*elem*/, GstPad *pad, gpointer data); protected: - void init(); - bool reopen(); + bool determineFrameDims(Size & sz); bool isPipelinePlaying(); void startPipeline(); void stopPipeline(); void restartPipeline(); - void setFilter(const char* prop, int type, int v1, int v2 = 0); + void setFilter(const char *prop, int type, int v1, int v2); void removeFilter(const char *filter); - static void newPad(GstElement *myelement, - GstPad *pad, - gpointer data); - GstElement* pipeline; - GstElement* uridecodebin; - GstElement* v4l2src; - GstElement* color; - GstElement* sink; -#if GST_VERSION_MAJOR > 0 - GstSample* sample; -#endif - GstBuffer* buffer; - GstCaps* caps; - IplImage* frame; - gint64 duration; - gint width; - gint height; - double fps; - - bool isPosFramesSupported; - bool isPosFramesEmulated; - gint64 emulatedFrameNumber; - - bool isOutputByteBuffer; }; /*! * \brief CvCapture_GStreamer::init * inits the class */ -void CvCapture_GStreamer::init() -{ - pipeline = NULL; - uridecodebin = NULL; - v4l2src = NULL; - color = NULL; - sink = NULL; -#if GST_VERSION_MAJOR > 0 - sample = NULL; +GStreamerCapture::GStreamerCapture() : + pipeline(NULL), v4l2src(NULL), sink(NULL), sample(NULL), +#if GST_VERSION_MAJOR == 0 + buffer(NULL), #endif - buffer = NULL; - caps = NULL; - frame = NULL; - duration = -1; - width = -1; - height = -1; - fps = -1; - - isPosFramesSupported = false; - isPosFramesEmulated = false; - emulatedFrameNumber = -1; - - isOutputByteBuffer = false; + caps(NULL), + duration(-1), width(-1), height(-1), channels(0), fps(-1), + isPosFramesSupported(false), + isPosFramesEmulated(false), + emulatedFrameNumber(-1), + isOutputByteBuffer(false) +{ } /*! * \brief CvCapture_GStreamer::close * Closes the pipeline and destroys all instances */ -void CvCapture_GStreamer::close() +GStreamerCapture::~GStreamerCapture() { if (isPipelinePlaying()) - this->stopPipeline(); - - if(pipeline) { + stopPipeline(); + if (pipeline && GST_IS_ELEMENT(pipeline)) + { gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL); gst_object_unref(GST_OBJECT(pipeline)); - pipeline = NULL; } - - duration = -1; - width = -1; - height = -1; - fps = -1; - isPosFramesSupported = false; - isPosFramesEmulated = false; - emulatedFrameNumber = -1; } /*! @@ -238,7 +243,7 @@ void CvCapture_GStreamer::close() * Grabs a sample from the pipeline, awaiting consumation by retreiveFrame. * The pipeline is started if it was not running yet */ -bool CvCapture_GStreamer::grabFrame() +bool GStreamerCapture::grabFrame() { if(!pipeline) return false; @@ -254,23 +259,18 @@ bool CvCapture_GStreamer::grabFrame() #if GST_VERSION_MAJOR == 0 if(buffer) gst_buffer_unref(buffer); - buffer = gst_app_sink_pull_buffer(GST_APP_SINK(sink)); + if(!buffer) + return false; #else if(sample) gst_sample_unref(sample); - sample = gst_app_sink_pull_sample(GST_APP_SINK(sink)); - if(!sample) return false; - - buffer = gst_sample_get_buffer(sample); + gst_sample_ref(sample); #endif - if(!buffer) - return false; - if (isPosFramesEmulated) emulatedFrameNumber++; @@ -282,146 +282,145 @@ bool CvCapture_GStreamer::grabFrame() * \return IplImage pointer. [Transfer Full] * Retrieve the previously grabbed buffer, and wrap it in an IPLImage structure */ -IplImage * CvCapture_GStreamer::retrieveFrame(int) +bool GStreamerCapture::retrieveFrame(int, OutputArray dst) { - if(!buffer) - return 0; - - //construct a frame header if we did not have any yet - if(!frame) - { #if GST_VERSION_MAJOR == 0 - GstCaps* buffer_caps = gst_buffer_get_caps(buffer); + if (!buffer) + return false; #else - GstCaps* buffer_caps = gst_sample_get_caps(sample); + if(!sample) + return false; #endif - // bail out in no caps - assert(gst_caps_get_size(buffer_caps) == 1); - GstStructure* structure = gst_caps_get_structure(buffer_caps, 0); - - // bail out if width or height are 0 - if(!gst_structure_get_int(structure, "width", &width) || - !gst_structure_get_int(structure, "height", &height)) - { - gst_caps_unref(buffer_caps); - return 0; - } - - int depth = 3; - bool height_extend = false; - -#if GST_VERSION_MAJOR > 0 - depth = 0; - const gchar* name = gst_structure_get_name(structure); - const gchar* format = gst_structure_get_string(structure, "format"); - - if (!name) - return 0; - - // we support 11 types of data: - // video/x-raw, format=BGR -> 8bit, 3 channels - // video/x-raw, format=GRAY8 -> 8bit, 1 channel - // video/x-raw, format=UYVY -> 8bit, 2 channel - // video/x-raw, format=YUY2 -> 8bit, 2 channel - // video/x-raw, format=YVYU -> 8bit, 2 channel - // video/x-raw, format=NV12 -> 8bit, 1 channel (height is 1.5x larger than true height) - // video/x-raw, format=NV21 -> 8bit, 1 channel (height is 1.5x larger than true height) - // video/x-raw, format=YV12 -> 8bit, 1 channel (height is 1.5x larger than true height) - // video/x-raw, format=I420 -> 8bit, 1 channel (height is 1.5x larger than true height) - // video/x-bayer -> 8bit, 1 channel - // image/jpeg -> 8bit, mjpeg: buffer_size x 1 x 1 - // bayer data is never decoded, the user is responsible for that - // everything is 8 bit, so we just test the caps for bit depth - - if (strcasecmp(name, "video/x-raw") == 0) - { - if (!format) - return 0; - - if (strcasecmp(format, "BGR") == 0) { - depth = 3; - } - else if( (strcasecmp(format, "UYVY") == 0) || (strcasecmp(format, "YUY2") == 0) || (strcasecmp(format, "YVYU") == 0) ){ - depth = 2; - } - else if( (strcasecmp(format, "NV12") == 0) || (strcasecmp(format, "NV21") == 0) || (strcasecmp(format, "YV12") == 0) || (strcasecmp(format, "I420") == 0) ){ - depth = 1; - height_extend = true; - } - else if(strcasecmp(format, "GRAY8") == 0){ - depth = 1; - } - } - else if (strcasecmp(name, "video/x-bayer") == 0) - { - depth = 1; - } else if(strcasecmp(name, "image/jpeg") == 0) { - depth = 1; - // the correct size will be set once the first frame arrives - isOutputByteBuffer = true; - } -#endif - if (depth > 0) { - if(height_extend){ - frame = cvCreateImageHeader(cvSize(width, height*3/2), IPL_DEPTH_8U, depth); - }else{ - frame = cvCreateImageHeader(cvSize(width, height), IPL_DEPTH_8U, depth); - } - } else { - gst_caps_unref(buffer_caps); - return 0; - } - - gst_caps_unref(buffer_caps); - } + Size sz; + if (!determineFrameDims(sz)) + return false; // gstreamer expects us to handle the memory at this point // so we can just wrap the raw buffer and be done with it #if GST_VERSION_MAJOR == 0 - frame->imageData = (char *)GST_BUFFER_DATA(buffer); + Mat src(sz, CV_8UC1, (uchar*)GST_BUFFER_DATA(buffer)); + src.copyTo(dst); #else - // info.data ptr is valid until next grabFrame where the associated sample is unref'd - GstMapInfo info = GstMapInfo(); - gboolean success = gst_buffer_map(buffer,&info, (GstMapFlags)GST_MAP_READ); - - // with MJPEG streams frame size can change arbitrarily - if (isOutputByteBuffer && (size_t)info.size != (size_t)frame->imageSize) + GstBuffer * buf = gst_sample_get_buffer(sample); + if (!buf) + return false; + GstMapInfo info; + if (!gst_buffer_map(buf, &info, GST_MAP_READ)) { - cvReleaseImageHeader(&frame); - frame = cvCreateImageHeader(cvSize(info.size, 1), IPL_DEPTH_8U, 1); + //something weird went wrong here. abort. abort. + CV_WARN("Failed to map GStreamerbuffer to system memory"); + return false; } - if (!success){ - //something weird went wrong here. abort. abort. - //fprintf(stderr,"GStreamer: unable to map buffer"); - return 0; + { + Mat src; + if (isOutputByteBuffer) + src = Mat(Size(info.size, 1), CV_8UC1, info.data); + else + src = Mat(sz, CV_MAKETYPE(CV_8U, channels), info.data); + CV_Assert(src.isContinuous()); + src.copyTo(dst); } - frame->imageData = (char*)info.data; - gst_buffer_unmap(buffer,&info); + gst_buffer_unmap(buf, &info); #endif - return frame; + return true; } +bool GStreamerCapture::determineFrameDims(Size &sz) +{ +#if GST_VERSION_MAJOR == 0 + GstCaps * frame_caps = gst_buffer_get_caps(buffer); +#else + GstCaps * frame_caps = gst_sample_get_caps(sample); +#endif + // bail out in no caps + if (!GST_CAPS_IS_SIMPLE(frame_caps)) + return false; + + GstStructure* structure = gst_caps_get_structure(frame_caps, 0); + + // bail out if width or height are 0 + if (!gst_structure_get_int(structure, "width", &width) + || !gst_structure_get_int(structure, "height", &height)) + return false; + + sz = Size(width, height); + +#if GST_VERSION_MAJOR > 0 + const gchar* name = gst_structure_get_name(structure); + + if (!name) + return false; + + // we support 11 types of data: + // video/x-raw, format=BGR -> 8bit, 3 channels + // video/x-raw, format=GRAY8 -> 8bit, 1 channel + // video/x-raw, format=UYVY -> 8bit, 2 channel + // video/x-raw, format=YUY2 -> 8bit, 2 channel + // video/x-raw, format=YVYU -> 8bit, 2 channel + // video/x-raw, format=NV12 -> 8bit, 1 channel (height is 1.5x larger than true height) + // video/x-raw, format=NV21 -> 8bit, 1 channel (height is 1.5x larger than true height) + // video/x-raw, format=YV12 -> 8bit, 1 channel (height is 1.5x larger than true height) + // video/x-raw, format=I420 -> 8bit, 1 channel (height is 1.5x larger than true height) + // video/x-bayer -> 8bit, 1 channel + // image/jpeg -> 8bit, mjpeg: buffer_size x 1 x 1 + // bayer data is never decoded, the user is responsible for that + // everything is 8 bit, so we just test the caps for bit depth + if (strcasecmp(name, "video/x-raw") == 0) + { + const gchar* format = gst_structure_get_string(structure, "format"); + if (!format) + return false; + if (strcasecmp(format, "BGR") == 0) + { + channels = 3; + } + else if( (strcasecmp(format, "UYVY") == 0) || (strcasecmp(format, "YUY2") == 0) || (strcasecmp(format, "YVYU") == 0) ) + { + channels = 2; + } + else if( (strcasecmp(format, "NV12") == 0) || (strcasecmp(format, "NV21") == 0) || (strcasecmp(format, "YV12") == 0) || (strcasecmp(format, "I420") == 0) ) + { + channels = 1; + sz.height = sz.height * 3 / 2; + } + else if(strcasecmp(format, "GRAY8") == 0) + { + channels = 1; + } + } + else if (strcasecmp(name, "video/x-bayer") == 0) + { + channels = 1; + } + else if(strcasecmp(name, "image/jpeg") == 0) + { + // the correct size will be set once the first frame arrives + channels = 1; + isOutputByteBuffer = true; + } +#else + // we support only video/x-raw, format=BGR -> 8bit, 3 channels + channels = 3; +#endif + return true; +} /*! * \brief CvCapture_GStreamer::isPipelinePlaying * \return if the pipeline is currently playing. */ -bool CvCapture_GStreamer::isPipelinePlaying() +bool GStreamerCapture::isPipelinePlaying() { GstState current, pending; GstClockTime timeout = 5*GST_SECOND; - if(!GST_IS_ELEMENT(pipeline)){ + GstStateChangeReturn ret = gst_element_get_state(pipeline, ¤t, &pending, timeout); + if (!ret) + { + CV_WARN("GStreamer: unable to query pipeline state"); return false; } - - GstStateChangeReturn ret = gst_element_get_state(GST_ELEMENT(pipeline),¤t, &pending, timeout); - if (!ret){ - //fprintf(stderr, "GStreamer: unable to query pipeline state\n"); - return false; - } - return current == GST_STATE_PLAYING; } @@ -429,12 +428,8 @@ bool CvCapture_GStreamer::isPipelinePlaying() * \brief CvCapture_GStreamer::startPipeline * Start the pipeline by setting it to the playing state */ -void CvCapture_GStreamer::startPipeline() +void GStreamerCapture::startPipeline() { - CV_FUNCNAME("icvStartPipeline"); - - __BEGIN__; - //fprintf(stderr, "relinked, pausing\n"); GstStateChangeReturn status = gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING); if (status == GST_STATE_CHANGE_ASYNC) @@ -447,7 +442,7 @@ void CvCapture_GStreamer::startPipeline() handleMessage(pipeline); gst_object_unref(pipeline); pipeline = NULL; - CV_ERROR(CV_StsError, "GStreamer: unable to start pipeline\n"); + CV_WARN("GStreamer: unable to start pipeline"); return; } @@ -456,36 +451,28 @@ void CvCapture_GStreamer::startPipeline() //printf("state now playing\n"); handleMessage(pipeline); - __END__; } - /*! * \brief CvCapture_GStreamer::stopPipeline * Stop the pipeline by setting it to NULL */ -void CvCapture_GStreamer::stopPipeline() +void GStreamerCapture::stopPipeline() { - CV_FUNCNAME("icvStopPipeline"); - - __BEGIN__; - //fprintf(stderr, "restarting pipeline, going to ready\n"); - if(gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL) == - GST_STATE_CHANGE_FAILURE) { - CV_ERROR(CV_StsError, "GStreamer: unable to stop pipeline\n"); + if(gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE) + { + CV_WARN("GStreamer: unable to stop pipeline"); gst_object_unref(pipeline); pipeline = NULL; - return; } - __END__; } /*! * \brief CvCapture_GStreamer::restartPipeline * Restart the pipeline */ -void CvCapture_GStreamer::restartPipeline() +void GStreamerCapture::restartPipeline() { handleMessage(pipeline); @@ -493,7 +480,6 @@ void CvCapture_GStreamer::restartPipeline() this->startPipeline(); } - /*! * \brief CvCapture_GStreamer::setFilter * \param prop the property name @@ -502,7 +488,7 @@ void CvCapture_GStreamer::restartPipeline() * \param v2 second value of property type requires it, else NULL * Filter the output formats by setting appsink caps properties */ -void CvCapture_GStreamer::setFilter(const char *prop, int type, int v1, int v2) +void GStreamerCapture::setFilter(const char *prop, int type, int v1, int v2) { //printf("GStreamer: setFilter \n"); if(!caps || !( GST_IS_CAPS (caps) )) @@ -545,13 +531,12 @@ void CvCapture_GStreamer::setFilter(const char *prop, int type, int v1, int v2) //printf("filtering with %s\n", gst_caps_to_string(caps)); } - /*! * \brief CvCapture_GStreamer::removeFilter * \param filter filter to remove * remove the specified filter from the appsink template caps */ -void CvCapture_GStreamer::removeFilter(const char *filter) +void GStreamerCapture::removeFilter(const char *filter) { if(!caps) return; @@ -574,9 +559,7 @@ void CvCapture_GStreamer::removeFilter(const char *filter) * decodebin creates pads based on stream information, which is not known upfront * on receiving the pad-added signal, we connect it to the colorspace conversion element */ -void CvCapture_GStreamer::newPad(GstElement * /*elem*/, - GstPad *pad, - gpointer data) +void GStreamerCapture::newPad(GstElement *, GstPad *pad, gpointer data) { GstPad *sinkpad; GstElement *color = (GstElement *) data; @@ -591,6 +574,13 @@ void CvCapture_GStreamer::newPad(GstElement * /*elem*/, gst_object_unref (sinkpad); } +bool GStreamerCapture::isOpened() const +{ + return pipeline != NULL; +} + +int GStreamerCapture::getCaptureDomain() { return CAP_GSTREAMER; } + /*! * \brief CvCapture_GStreamer::open Open the given file with gstreamer * \param type CvCapture type. One of CV_CAP_GSTREAMER_* @@ -622,40 +612,31 @@ void CvCapture_GStreamer::newPad(GstElement * /*elem*/, * I expect this to be the same for CV_CAP_GSTREAMER_1394. Is anyone actually still using v4l (v1)? * */ -bool CvCapture_GStreamer::open( int type, const char* filename ) +bool GStreamerCapture::open(int id) { - CV_FUNCNAME("cvCaptureFromCAM_GStreamer"); - - __BEGIN__; + if (!is_gst_element_exists("v4l2src")) + return false; + std::ostringstream desc; + desc << "v4l2src device-name=/dev/video" << id + << " ! " << COLOR_ELEM + << " ! appsink"; + return open(desc.str()); +} +bool GStreamerCapture::open(const String &filename_) +{ gst_initializer::init(); + const gchar * filename = filename_.c_str(); + bool file = false; - bool stream = false; + //bool stream = false; bool manualpipeline = false; char *uri = NULL; - uridecodebin = NULL; - GstElementFactory * testfac; + GstElement* uridecodebin = NULL; + GstElement* color = NULL; GstStateChangeReturn status; - if (type == CV_CAP_GSTREAMER_V4L){ - testfac = gst_element_factory_find("v4lsrc"); - if (!testfac){ - return false; - } - g_object_unref(G_OBJECT(testfac)); - filename = "v4lsrc ! " COLOR_ELEM " ! appsink"; - } - if (type == CV_CAP_GSTREAMER_V4L2){ - testfac = gst_element_factory_find("v4l2src"); - if (!testfac){ - return false; - } - g_object_unref(G_OBJECT(testfac)); - filename = "v4l2src ! " COLOR_ELEM " ! appsink"; - } - - // test if we have a valid uri. If so, open it with an uridecodebin // else, we might have a file or a manual pipeline. // if gstreamer cannot parse the manual pipeline, we assume we were given and @@ -674,7 +655,7 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) #else uri = realpath(filename, NULL); #endif - stream = false; + //stream = false; if(uri) { uri = g_filename_to_uri(uri, NULL, NULL); @@ -687,7 +668,6 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) CV_WARN("GStreamer: Error opening file\n"); CV_WARN(filename); CV_WARN(uri); - close(); return false; } } @@ -700,13 +680,13 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) fprintf(stderr, "GStreamer: Error opening bin: %s\n", err->message); return false; } - stream = true; + //stream = true; manualpipeline = true; } } else { - stream = true; + //stream = true; uri = g_strdup(filename); } @@ -736,8 +716,7 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) if(!uridecodebin) { - //fprintf(stderr, "GStreamer: Error opening bin: %s\n", err->message); - close(); + CV_WARN("Can not parse GStreamer URI bin"); return false; } } @@ -802,7 +781,7 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) if (!sink) { - CV_ERROR(CV_StsError, "GStreamer: cannot find appsink in manual pipeline\n"); + CV_WARN("GStreamer: cannot find appsink in manual pipeline\n"); return false; } @@ -822,7 +801,7 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) { if(!gst_element_link(uridecodebin, color)) { - CV_ERROR(CV_StsError, "GStreamer: cannot link color -> sink\n"); + CV_WARN("cannot link color -> sink"); gst_object_unref(pipeline); pipeline = NULL; return false; @@ -835,7 +814,7 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) if(!gst_element_link(color, sink)) { - CV_ERROR(CV_StsError, "GStreamer: cannot link color -> sink\n"); + CV_WARN("GStreamer: cannot link color -> sink\n"); gst_object_unref(pipeline); pipeline = NULL; return false; @@ -844,9 +823,10 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) //TODO: is 1 single buffer really high enough? gst_app_sink_set_max_buffers (GST_APP_SINK(sink), 1); - gst_app_sink_set_drop (GST_APP_SINK(sink), stream); +// gst_app_sink_set_drop (GST_APP_SINK(sink), stream); //do not emit signals: all calls will be synchronous and blocking - gst_app_sink_set_emit_signals (GST_APP_SINK(sink), 0); + gst_app_sink_set_emit_signals (GST_APP_SINK(sink), FALSE); +// gst_base_sink_set_sync(GST_BASE_SINK(sink), FALSE); #if GST_VERSION_MAJOR == 0 caps = gst_caps_new_simple("video/x-raw-rgb", @@ -875,6 +855,8 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) gst_caps_unref(caps); { + GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline-init"); + status = gst_element_set_state(GST_ELEMENT(pipeline), file ? GST_STATE_PAUSED : GST_STATE_PLAYING); if (status == GST_STATE_CHANGE_ASYNC) @@ -884,10 +866,11 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) } if (status == GST_STATE_CHANGE_FAILURE) { + GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline-error"); handleMessage(pipeline); gst_object_unref(pipeline); pipeline = NULL; - CV_ERROR(CV_StsError, "GStreamer: unable to start pipeline\n"); + CV_WARN("GStreamer: unable to start pipeline\n"); return false; } @@ -944,12 +927,12 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) #else #define FORMAT format_ #endif - status_ = gst_element_query_position(pipeline, FORMAT, &value_); + status_ = gst_element_query_position(sink, FORMAT, &value_); #undef FORMAT if (!status_ || value_ != 0 || duration < 0) { CV_WARN(cv::format("Cannot query video position: status=%d value=%lld duration=%lld\n", - (int)status_, (long long int)value_, (long long int)duration).c_str()); + (int)status_, (long long int)value_, (long long int)duration).c_str()); isPosFramesSupported = false; isPosFramesEmulated = true; emulatedFrameNumber = 0; @@ -961,8 +944,6 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline"); } - __END__; - return true; } @@ -975,7 +956,7 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) * For frame-based properties, we use the caps of the lasst receivef sample. This means that some properties * are not available until a first frame was received */ -double CvCapture_GStreamer::getProperty( int propId ) const +double GStreamerCapture::getProperty(int propId) const { GstFormat format; gint64 value; @@ -1032,38 +1013,28 @@ double CvCapture_GStreamer::getProperty( int propId ) const return height; case CV_CAP_PROP_FPS: return fps; - case CV_CAP_PROP_FOURCC: - break; case CV_CAP_PROP_FRAME_COUNT: return duration; - case CV_CAP_PROP_FORMAT: - case CV_CAP_PROP_MODE: case CV_CAP_PROP_BRIGHTNESS: case CV_CAP_PROP_CONTRAST: case CV_CAP_PROP_SATURATION: case CV_CAP_PROP_HUE: if (v4l2src) { - const gchar * propName = - propId == CV_CAP_PROP_BRIGHTNESS ? "brightness" : - propId == CV_CAP_PROP_CONTRAST ? "contrast" : - propId == CV_CAP_PROP_SATURATION ? "saturation" : - propId == CV_CAP_PROP_HUE ? "hue" : NULL; - - if (propName) + string propName = get_gst_propname(propId); + if (!propName.empty()) { - gint32 value32 = 0; - g_object_get(G_OBJECT(v4l2src), propName, &value32, NULL); - return value32; + gint32 val = 0; + g_object_get(G_OBJECT(v4l2src), propName.c_str(), &val, NULL); + return static_cast(val); } } - case CV_CAP_PROP_GAIN: - case CV_CAP_PROP_CONVERT_RGB: break; case CV_CAP_GSTREAMER_QUEUE_LENGTH: - if(!sink) { - CV_WARN("GStreamer: there is no sink yet"); - return false; + if(!sink) + { + CV_WARN("there is no sink yet"); + return 0; } return gst_app_sink_get_max_buffers(GST_APP_SINK(sink)); default: @@ -1084,13 +1055,13 @@ double CvCapture_GStreamer::getProperty( int propId ) const * Sets the desired property id with val. If the pipeline is running, * it is briefly stopped and started again after the property was set */ -bool CvCapture_GStreamer::setProperty( int propId, double value ) +bool GStreamerCapture::setProperty(int propId, double value) { - GstFormat format; - GstSeekFlags flags; + const GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE); - if(!pipeline) { - CV_WARN("GStreamer: no pipeline"); + if(!pipeline) + { + CV_WARN("no pipeline"); return false; } @@ -1098,12 +1069,10 @@ bool CvCapture_GStreamer::setProperty( int propId, double value ) if (wasPlaying) this->stopPipeline(); - - switch(propId) { + switch(propId) + { case CV_CAP_PROP_POS_MSEC: - format = GST_FORMAT_TIME; - flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH|GST_SEEK_FLAG_ACCURATE); - if(!gst_element_seek_simple(GST_ELEMENT(pipeline), format, + if(!gst_element_seek_simple(GST_ELEMENT(pipeline), GST_FORMAT_TIME, flags, (gint64) (value * GST_MSECOND))) { handleMessage(pipeline); CV_WARN("GStreamer: unable to seek"); @@ -1138,10 +1107,9 @@ bool CvCapture_GStreamer::setProperty( int propId, double value ) } } return false; + CV_WARN("unable to seek"); } - format = GST_FORMAT_DEFAULT; - flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH|GST_SEEK_FLAG_ACCURATE); - if(!gst_element_seek_simple(GST_ELEMENT(pipeline), format, + if(!gst_element_seek_simple(GST_ELEMENT(pipeline), GST_FORMAT_DEFAULT, flags, (gint64) value)) { handleMessage(pipeline); CV_WARN("GStreamer: unable to seek"); @@ -1152,9 +1120,7 @@ bool CvCapture_GStreamer::setProperty( int propId, double value ) return true; } case CV_CAP_PROP_POS_AVI_RATIO: - format = GST_FORMAT_PERCENT; - flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH|GST_SEEK_FLAG_ACCURATE); - if(!gst_element_seek_simple(GST_ELEMENT(pipeline), format, + if(!gst_element_seek_simple(GST_ELEMENT(pipeline), GST_FORMAT_PERCENT, flags, (gint64) (value * GST_FORMAT_PERCENT_MAX))) { handleMessage(pipeline); CV_WARN("GStreamer: unable to seek"); @@ -1195,37 +1161,34 @@ bool CvCapture_GStreamer::setProperty( int propId, double value ) } else removeFilter("framerate"); break; - case CV_CAP_PROP_FOURCC: - case CV_CAP_PROP_FRAME_COUNT: - case CV_CAP_PROP_FORMAT: - case CV_CAP_PROP_MODE: case CV_CAP_PROP_BRIGHTNESS: case CV_CAP_PROP_CONTRAST: case CV_CAP_PROP_SATURATION: case CV_CAP_PROP_HUE: if (v4l2src) { - const gchar * propName = - propId == CV_CAP_PROP_BRIGHTNESS ? "brightness" : - propId == CV_CAP_PROP_CONTRAST ? "contrast" : - propId == CV_CAP_PROP_SATURATION ? "saturation" : - propId == CV_CAP_PROP_HUE ? "hue" : NULL; - - if (propName) + string propName = get_gst_propname(propId); + if (!propName.empty()) { - gint32 value32 = cv::saturate_cast(value); - g_object_set(G_OBJECT(v4l2src), propName, &value32, NULL); + gint32 val = cv::saturate_cast(value); + g_object_set(G_OBJECT(v4l2src), propName.c_str(), &val, NULL); return true; } } + return false; case CV_CAP_PROP_GAIN: case CV_CAP_PROP_CONVERT_RGB: break; case CV_CAP_GSTREAMER_QUEUE_LENGTH: + { if(!sink) - break; + { + CV_WARN("there is no sink yet"); + return false; + } gst_app_sink_set_max_buffers(GST_APP_SINK(sink), (guint) value); - break; + return true; + } default: CV_WARN("GStreamer: unhandled property"); } @@ -1236,23 +1199,24 @@ bool CvCapture_GStreamer::setProperty( int propId, double value ) return false; } -/*! - * \brief cvCreateCapture_GStreamer - * \param type - * \param filename - * \return - */ -CvCapture* cvCreateCapture_GStreamer(int type, const char* filename ) + +Ptr cv::createGStreamerCapture(const String& filename) { - CvCapture_GStreamer* capture = new CvCapture_GStreamer; - - if( capture->open( type, filename )) - return capture; - - delete capture; - return 0; + Ptr cap = makePtr(); + if (cap && cap->open(filename)) + return cap; + return Ptr(); } +Ptr cv::createGStreamerCapture(int index) +{ + Ptr cap = makePtr(); + if (cap && cap->open(index)) + return cap; + return Ptr(); +} + +//================================================================================================== /*! * \brief The CvVideoWriter_GStreamer class @@ -1420,8 +1384,6 @@ const char* CvVideoWriter_GStreamer::filenameToMimetype(const char *filename) bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, double fps, CvSize frameSize, bool is_color ) { - CV_FUNCNAME("CvVideoWriter_GStreamer::open"); - // check arguments assert (filename); assert (fps > 0); @@ -1459,8 +1421,6 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, // we first try to construct a pipeline from the given string. // if that fails, we assume it is an ordinary filename - __BEGIN__; - encodebin = gst_parse_launch(filename, &err); manualpipeline = (encodebin != NULL); @@ -1469,7 +1429,7 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, #if GST_VERSION_MAJOR == 0 it = gst_bin_iterate_sources(GST_BIN(encodebin)); if(gst_iterator_next(it, (gpointer *)&source) != GST_ITERATOR_OK) { - CV_ERROR(CV_StsError, "GStreamer: cannot find appsink in manual pipeline\n"); + CV_WARN("GStreamer: cannot find appsink in manual pipeline\n"); return false; } #else @@ -1503,7 +1463,7 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, gst_iterator_free (it); if (!source){ - CV_ERROR(CV_StsError, "GStreamer: cannot find appsrc in manual pipeline\n"); + CV_WARN("GStreamer: cannot find appsrc in manual pipeline\n"); return false; } #endif @@ -1528,13 +1488,15 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, videocaps = gst_riff_create_video_caps(fourcc, NULL, NULL, NULL, NULL, NULL); if (!videocaps){ - CV_ERROR( CV_StsUnsupportedFormat, "Gstreamer Opencv backend does not support this codec."); + CV_WARN("Gstreamer Opencv backend does not support this codec."); + return false; } //create container caps from file extension mime = filenameToMimetype(filename); if (!mime) { - CV_ERROR( CV_StsUnsupportedFormat, "Gstreamer Opencv backend does not support this file type."); + CV_WARN("Gstreamer Opencv backend does not support this file type."); + return false; } #if FULL_GST_VERSION >= VERSION_NUM(0,10,32) @@ -1566,7 +1528,8 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, NULL); caps = gst_caps_fixate(caps); #else - CV_ERROR( CV_StsUnsupportedFormat, "Gstreamer 0.10 Opencv backend does not support writing encoded MJPEG data."); + CV_WARN("Gstreamer 0.10 Opencv backend does not support writing encoded MJPEG data."); + return false; #endif } else if(is_color) @@ -1633,7 +1596,8 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, g_object_set(G_OBJECT(file), "buffer-size", bufsize, NULL); gst_bin_add_many(GST_BIN(pipeline), source, encodebin, file, NULL); if(!gst_element_link_many(source, encodebin, file, NULL)) { - CV_ERROR(CV_StsError, "GStreamer: cannot link elements\n"); + CV_WARN("GStreamer: cannot link elements\n"); + return false; } } @@ -1697,7 +1661,8 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, stateret = gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING); if(stateret == GST_STATE_CHANGE_FAILURE) { handleMessage(pipeline); - CV_ERROR(CV_StsError, "GStreamer: cannot put pipeline to play\n"); + CV_WARN("GStreamer: cannot put pipeline to play\n"); + return false; } framerate = fps; @@ -1705,8 +1670,6 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, handleMessage(pipeline); - __END__; - return true; } @@ -1721,38 +1684,37 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, */ bool CvVideoWriter_GStreamer::writeFrame( const IplImage * image ) { - CV_FUNCNAME("CvVideoWriter_GStreamer::writerFrame"); - GstClockTime duration, timestamp; GstFlowReturn ret; int size; - __BEGIN__; - handleMessage(pipeline); #if GST_VERSION_MAJOR > 0 if (input_pix_fmt == GST_VIDEO_FORMAT_ENCODED) { if (image->nChannels != 1 || image->depth != IPL_DEPTH_8U || image->height != 1) { - CV_ERROR(CV_StsUnsupportedFormat, "cvWriteFrame() needs images with depth = IPL_DEPTH_8U, nChannels = 1 and height = 1."); + CV_WARN("cvWriteFrame() needs images with depth = IPL_DEPTH_8U, nChannels = 1 and height = 1."); + return false; } } else #endif if(input_pix_fmt == GST_VIDEO_FORMAT_BGR) { if (image->nChannels != 3 || image->depth != IPL_DEPTH_8U) { - CV_ERROR(CV_StsUnsupportedFormat, "cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 3."); + CV_WARN("cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 3."); + return false; } } #if FULL_GST_VERSION >= VERSION_NUM(0,10,29) else if (input_pix_fmt == GST_VIDEO_FORMAT_GRAY8) { if (image->nChannels != 1 || image->depth != IPL_DEPTH_8U) { - CV_ERROR(CV_StsUnsupportedFormat, "cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 1."); + CV_WARN("cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 1."); + return false; } } #endif else { - CV_ERROR(CV_StsUnsupportedFormat, "cvWriteFrame() needs BGR or grayscale images\n"); + CV_WARN("cvWriteFrame() needs BGR or grayscale images\n"); return false; } @@ -1765,7 +1727,7 @@ bool CvVideoWriter_GStreamer::writeFrame( const IplImage * image ) buffer = gst_buffer_try_new_and_alloc (size); if (!buffer) { - CV_ERROR(CV_StsBadSize, "Cannot create GStreamer buffer"); + CV_WARN("Cannot create GStreamer buffer"); } memcpy(GST_BUFFER_DATA (buffer), (guint8*)image->imageData, size); @@ -1794,8 +1756,6 @@ bool CvVideoWriter_GStreamer::writeFrame( const IplImage * image ) ++num_frames; - __END__; - return true; } @@ -1848,8 +1808,6 @@ void toFraction(double decimal, double &numerator, double &denominator) */ void handleMessage(GstElement * pipeline) { - CV_FUNCNAME("handlemessage"); - GError *err = NULL; gchar *debug = NULL; GstBus* bus = NULL; @@ -1857,7 +1815,6 @@ void handleMessage(GstElement * pipeline) GstElement * elem = NULL; GstMessage* msg = NULL; - __BEGIN__; bus = gst_element_get_bus(pipeline); while(gst_bus_have_pending(bus)) { @@ -1867,7 +1824,7 @@ void handleMessage(GstElement * pipeline) if(gst_is_missing_plugin_message(msg)) { - CV_ERROR(CV_StsError, "GStreamer: your gstreamer installation is missing a required plugin\n"); + CV_WARN("your gstreamer installation is missing a required plugin\n"); } else { @@ -1906,6 +1863,4 @@ void handleMessage(GstElement * pipeline) } gst_object_unref(GST_OBJECT(bus)); - - __END__ } diff --git a/modules/videoio/src/cap_mjpeg_encoder.cpp b/modules/videoio/src/cap_mjpeg_encoder.cpp index a518404e94..b564f608b9 100644 --- a/modules/videoio/src/cap_mjpeg_encoder.cpp +++ b/modules/videoio/src/cap_mjpeg_encoder.cpp @@ -96,7 +96,6 @@ static bool createEncodeHuffmanTable( const int* src, unsigned* table, int max_s if( size > max_size ) { CV_Error(CV_StsOutOfRange, "too big maximum Huffman code size"); - return false; } memset( table, 0, size*sizeof(table[0])); diff --git a/modules/videoio/src/precomp.hpp b/modules/videoio/src/precomp.hpp index 1f5918b240..5ecc6c74b7 100644 --- a/modules/videoio/src/precomp.hpp +++ b/modules/videoio/src/precomp.hpp @@ -139,7 +139,6 @@ CvVideoWriter* cvCreateVideoWriter_Images(const char* filename); #define CV_CAP_GSTREAMER_V4L2 2 #define CV_CAP_GSTREAMER_FILE 3 -CvCapture* cvCreateCapture_GStreamer(int type, const char *filename); CvCapture* cvCreateFileCapture_FFMPEG_proxy(const char* filename); @@ -194,7 +193,11 @@ namespace cv Ptr createGPhoto2Capture(int index); Ptr createGPhoto2Capture(const String& deviceName); + Ptr createXINECapture(const char* filename); + + Ptr createGStreamerCapture(const String& filename); + Ptr createGStreamerCapture(int index); } #endif /* __VIDEOIO_H_ */ diff --git a/modules/videoio/test/test_gstreamer.cpp b/modules/videoio/test/test_gstreamer.cpp index cb5bbd4954..55accb52c0 100644 --- a/modules/videoio/test/test_gstreamer.cpp +++ b/modules/videoio/test/test_gstreamer.cpp @@ -7,6 +7,7 @@ namespace opencv_test { + typedef tuple< string, Size, Size, int > Param; typedef testing::TestWithParam< Param > Videoio_Gstreamer_Test; @@ -19,8 +20,9 @@ TEST_P(Videoio_Gstreamer_Test, test_object_structure) int count_frames = 10; std::ostringstream pipeline; pipeline << "videotestsrc pattern=ball num-buffers=" << count_frames << " ! " << format; - pipeline << ", framerate=1/1, width=" << frame_size.width << ", height=" << frame_size.height << " ! appsink"; - VideoCapture cap(pipeline.str(), CAP_GSTREAMER); + pipeline << ", width=" << frame_size.width << ", height=" << frame_size.height << " ! appsink"; + VideoCapture cap; + ASSERT_NO_THROW(cap.open(pipeline.str(), CAP_GSTREAMER)); ASSERT_TRUE(cap.isOpened()); Mat buffer, decode_frame, gray_frame, rgb_frame; diff --git a/modules/videoio/test/test_video_io.cpp b/modules/videoio/test/test_video_io.cpp index e5bd3216dc..c1834be5ec 100644 --- a/modules/videoio/test/test_video_io.cpp +++ b/modules/videoio/test/test_video_io.cpp @@ -46,12 +46,62 @@ namespace opencv_test { +struct VideoCaptureAPI +{ + VideoCaptureAPIs api; + + inline const char * toString() const + { + switch (api) + { + case CAP_ANY: return "CAP_ANY"; + #ifdef __linux__ + case CAP_V4L2: return "CAP_V4L/CAP_V4L2"; + #else + case CAP_VFW: return "CAP_VFW"; + #endif + case CAP_FIREWIRE: return "CAP_FIREWIRE"; + case CAP_QT: return "CAP_QT"; + case CAP_UNICAP: return "CAP_UNICAP"; + case CAP_DSHOW: return "CAP_DSHOW"; + case CAP_PVAPI: return "CAP_PVAPI"; + case CAP_OPENNI: return "CAP_OPENNI"; + case CAP_OPENNI_ASUS: return "CAP_OPENNI_ASUS"; + case CAP_ANDROID: return "CAP_ANDROID"; + case CAP_XIAPI: return "CAP_XIAPI"; + case CAP_AVFOUNDATION: return "CAP_AVFOUNDATION"; + case CAP_GIGANETIX: return "CAP_GIGANETIX"; + case CAP_MSMF: return "CAP_MSMF"; + case CAP_WINRT: return "CAP_WINRT"; + case CAP_INTELPERC: return "CAP_INTELPERC"; + case CAP_OPENNI2: return "CAP_OPENNI2"; + case CAP_OPENNI2_ASUS: return "CAP_OPENNI2_ASUS"; + case CAP_GPHOTO2: return "CAP_GPHOTO2"; + case CAP_GSTREAMER: return "CAP_GSTREAMER"; + case CAP_FFMPEG: return "CAP_FFMPEG"; + case CAP_IMAGES: return "CAP_IMAGES"; + case CAP_ARAVIS: return "CAP_ARAVIS"; + case CAP_OPENCV_MJPEG: return "CAP_OPENCV_MJPEG"; + case CAP_INTEL_MFX: return "CAP_INTEL_MFX"; + case CAP_XINE: return "CAP_XINE"; + } + return "unknown"; + } + VideoCaptureAPI(int api_ = CAP_ANY) : api((VideoCaptureAPIs)api_) {} + operator int() { return api; } +}; + +inline std::ostream &operator<<(std::ostream &out, const VideoCaptureAPI & api) +{ + out << api.toString(); return out; +} + class Videoio_Test_Base { protected: string ext; string video_file; - int apiPref; + VideoCaptureAPI apiPref; protected: Videoio_Test_Base() {} virtual ~Videoio_Test_Base() {} @@ -60,14 +110,16 @@ protected: void checkFrameRead(int idx, VideoCapture & cap) { //int frameID = (int)cap.get(CAP_PROP_POS_FRAMES); - Mat img; cap >> img; + Mat img; + ASSERT_NO_THROW(cap >> img); //std::cout << "idx=" << idx << " img=" << img.size() << " frameID=" << frameID << std::endl; ASSERT_FALSE(img.empty()) << "idx=" << idx; checkFrameContent(img, idx); } void checkFrameSeek(int idx, VideoCapture & cap) { - bool canSeek = cap.set(CAP_PROP_POS_FRAMES, idx); + bool canSeek = false; + ASSERT_NO_THROW(canSeek = cap.set(CAP_PROP_POS_FRAMES, idx)); if (!canSeek) { std::cout << "Seek to frame '" << idx << "' is not supported. SKIP." << std::endl; @@ -79,26 +131,15 @@ protected: public: void doTest() { - if (apiPref == CAP_AVFOUNDATION) - { - // TODO: fix this backend - std::cout << "SKIP test: AVFoundation backend returns invalid frame count" << std::endl; - return; - } - else if (apiPref == CAP_VFW) - { - // TODO: fix this backend - std::cout << "SKIP test: Video for Windows backend not open files" << std::endl; - return; - } - - VideoCapture cap(video_file, apiPref); + VideoCapture cap; + ASSERT_NO_THROW(cap.open(video_file, apiPref)); if (!cap.isOpened()) { std::cout << "SKIP test: backend " << apiPref << " can't open the video: " << video_file << std::endl; return; } - int n_frames = (int)cap.get(CAP_PROP_FRAME_COUNT); + int n_frames = -1; + EXPECT_NO_THROW(n_frames = (int)cap.get(CAP_PROP_FRAME_COUNT)); if (n_frames > 0) { ASSERT_GT(n_frames, 0); @@ -124,7 +165,8 @@ public: checkFrameRead(k, cap); } } - bool canSeek = cap.set(CAP_PROP_POS_FRAMES, 0); + bool canSeek = false; + EXPECT_NO_THROW(canSeek = cap.set(CAP_PROP_POS_FRAMES, 0)); if (!canSeek) { std::cout << "Seek to frame '0' is not supported. SKIP all 'seek' tests." << std::endl; @@ -134,7 +176,9 @@ public: if (ext != "wmv" && ext != "h264" && ext != "h265") { SCOPED_TRACE("progressive seek"); - ASSERT_TRUE(cap.set(CAP_PROP_POS_FRAMES, 0)); + bool res = false; + EXPECT_NO_THROW(res = cap.set(CAP_PROP_POS_FRAMES, 0)); + ASSERT_TRUE(res); for (int k = 0; k < n_frames; k += 20) { checkFrameSeek(k, cap); @@ -144,7 +188,9 @@ public: if (ext != "mpg" && ext != "wmv" && ext != "h264" && ext != "h265") { SCOPED_TRACE("random seek"); - ASSERT_TRUE(cap.set(CAP_PROP_POS_FRAMES, 0)); + bool res = false; + EXPECT_NO_THROW(res = cap.set(CAP_PROP_POS_FRAMES, 0)); + ASSERT_TRUE(res); for (int k = 0; k < 10; ++k) { checkFrameSeek(cvtest::TS::ptr()->get_rng().uniform(0, n_frames), cap); @@ -154,7 +200,7 @@ public: }; //================================================================================================== -typedef tuple Backend_Type_Params; +typedef tuple Backend_Type_Params; class Videoio_Bunny : public Videoio_Test_Base, public testing::TestWithParam { @@ -168,37 +214,29 @@ public: } void doFrameCountTest() { - if (apiPref == CAP_AVFOUNDATION) - { - // TODO: fix this backend - std::cout << "SKIP test: AVFoundation backend returns invalid frame count" << std::endl; - return; - } - else if (apiPref == CAP_VFW) - { - // TODO: fix this backend - std::cout << "SKIP test: Video for Windows backend not open files" << std::endl; - return; - } - - VideoCapture cap(video_file, apiPref); + VideoCapture cap; + EXPECT_NO_THROW(cap.open(video_file, apiPref)); if (!cap.isOpened()) { std::cout << "SKIP test: backend " << apiPref << " can't open the video: " << video_file << std::endl; return; } - EXPECT_EQ(bunny_param.getWidth() , cap.get(CAP_PROP_FRAME_WIDTH)); - EXPECT_EQ(bunny_param.getHeight(), cap.get(CAP_PROP_FRAME_HEIGHT)); + Size actual; + EXPECT_NO_THROW(actual = Size((int)cap.get(CAP_PROP_FRAME_WIDTH), + (int)cap.get(CAP_PROP_FRAME_HEIGHT))); + EXPECT_EQ(bunny_param.getWidth(), actual.width); + EXPECT_EQ(bunny_param.getHeight(), actual.height); - double fps_prop = cap.get(CAP_PROP_FPS); + double fps_prop = 0; + EXPECT_NO_THROW(fps_prop = cap.get(CAP_PROP_FPS)); if (fps_prop > 0) EXPECT_NEAR(fps_prop, bunny_param.getFps(), 1); else std::cout << "FPS is not available. SKIP check." << std::endl; int count_prop = 0; - count_prop = (int)cap.get(CAP_PROP_FRAME_COUNT); + EXPECT_NO_THROW(count_prop = (int)cap.get(CAP_PROP_FRAME_COUNT)); // mpg file reports 5.08 sec * 24 fps => property returns 122 frames // but actual number of frames returned is 125 if (ext != "mpg") @@ -213,7 +251,7 @@ public: while (cap.isOpened()) { Mat frame; - cap >> frame; + EXPECT_NO_THROW(cap >> frame); if (frame.empty()) break; EXPECT_EQ(bunny_param.getWidth(), frame.cols); @@ -229,7 +267,15 @@ public: } }; -typedef tuple Ext_Fourcc_PSNR; +//================================================================================================== + +struct Ext_Fourcc_PSNR +{ + string ext; + string fourcc; + float PSNR; + VideoCaptureAPI api; +}; typedef tuple Size_Ext_Fourcc_PSNR; class Videoio_Synthetic : public Videoio_Test_Base, public testing::TestWithParam @@ -243,39 +289,27 @@ public: Videoio_Synthetic() { frame_size = get<0>(GetParam()); - const Ext_Fourcc_PSNR ¶m = get<1>(GetParam()); - ext = get<0>(param); - fourcc = fourccFromString(get<1>(param)); - PSNR_GT = get<2>(param); + const Ext_Fourcc_PSNR p = get<1>(GetParam()); + ext = p.ext; + fourcc = fourccFromString(p.fourcc); + PSNR_GT = p.PSNR; video_file = cv::tempfile((fourccToString(fourcc) + "." + ext).c_str()); frame_count = 100; fps = 25.; - apiPref = get<3>(param); + apiPref = p.api; } void SetUp() { - - if (apiPref == CAP_AVFOUNDATION) - { - // TODO: fix this backend - std::cout << "SKIP test: AVFoundation backend can not write video" << std::endl; - return; - } - else if (apiPref == CAP_VFW) - { - // TODO: fix this backend - std::cout << "SKIP test: Video for Windows backend not open files" << std::endl; - return; - } Mat img(frame_size, CV_8UC3); - VideoWriter writer(video_file, apiPref, fourcc, fps, frame_size, true); + VideoWriter writer; + EXPECT_NO_THROW(writer.open(video_file, apiPref, fourcc, fps, frame_size, true)); ASSERT_TRUE(writer.isOpened()); for(int i = 0; i < frame_count; ++i ) { generateFrame(i, frame_count, img); - writer << img; + EXPECT_NO_THROW(writer << img); } - writer.release(); + EXPECT_NO_THROW(writer.release()); } void TearDown() { @@ -301,6 +335,10 @@ public: if (fourcc == VideoWriter::fourcc('M', 'P', 'E', 'G') && ext == "mkv") expected_frame_count.end += 1; + // Workaround for some gstreamer pipelines + if (apiPref == CAP_GSTREAMER) + expected_frame_count.start -= 1; + ASSERT_LE(expected_frame_count.start, actual); ASSERT_GE(expected_frame_count.end, actual); @@ -310,22 +348,24 @@ public: //================================================================================================== -int backend_params[] = { +static VideoCaptureAPI backend_params[] = { #ifdef HAVE_QUICKTIME CAP_QT, #endif -#ifdef HAVE_AVFOUNDATION - CAP_AVFOUNDATION, -#endif +// TODO: Broken? +//#ifdef HAVE_AVFOUNDATION +// CAP_AVFOUNDATION, +//#endif #ifdef HAVE_MSMF CAP_MSMF, #endif -#ifdef HAVE_VFW - CAP_VFW, -#endif +// TODO: Broken? +//#ifdef HAVE_VFW +// CAP_VFW, +//#endif #ifdef HAVE_GSTREAMER CAP_GSTREAMER, @@ -343,7 +383,7 @@ int backend_params[] = { // CAP_INTEL_MFX }; -string bunny_params[] = { +static string bunny_params[] = { #ifdef HAVE_VIDEO_INPUT string("wmv"), string("mov"), @@ -368,12 +408,22 @@ INSTANTIATE_TEST_CASE_P(videoio, Videoio_Bunny, //================================================================================================== -inline Ext_Fourcc_PSNR makeParam(const char * ext, const char * fourcc, float psnr, int apipref) +inline Ext_Fourcc_PSNR makeParam(const char * ext, const char * fourcc, float psnr, VideoCaptureAPIs apipref) { - return make_tuple(string(ext), string(fourcc), (float)psnr, (int)apipref); + Ext_Fourcc_PSNR res; + res.ext = ext; + res.fourcc = fourcc; + res.PSNR = psnr; + res.api = apipref; + return res; } -Ext_Fourcc_PSNR synthetic_params[] = { +inline static std::ostream &operator<<(std::ostream &out, const Ext_Fourcc_PSNR &p) +{ + out << "FOURCC(" << p.fourcc << "), ." << p.ext << ", " << p.api << ", " << p.PSNR << "dB"; return out; +} + +static Ext_Fourcc_PSNR synthetic_params[] = { #ifdef HAVE_MSMF #if !defined(_M_ARM) @@ -385,16 +435,17 @@ Ext_Fourcc_PSNR synthetic_params[] = { makeParam("mov", "H264", 30.f, CAP_MSMF), #endif -#ifdef HAVE_VFW -#if !defined(_M_ARM) - makeParam("wmv", "WMV1", 30.f, CAP_VFW), - makeParam("wmv", "WMV2", 30.f, CAP_VFW), -#endif - makeParam("wmv", "WMV3", 30.f, CAP_VFW), - makeParam("wmv", "WVC1", 30.f, CAP_VFW), - makeParam("avi", "H264", 30.f, CAP_VFW), - makeParam("avi", "MJPG", 30.f, CAP_VFW), -#endif +// TODO: Broken? +//#ifdef HAVE_VFW +//#if !defined(_M_ARM) +// makeParam("wmv", "WMV1", 30.f, CAP_VFW), +// makeParam("wmv", "WMV2", 30.f, CAP_VFW), +//#endif +// makeParam("wmv", "WMV3", 30.f, CAP_VFW), +// makeParam("wmv", "WVC1", 30.f, CAP_VFW), +// makeParam("avi", "H264", 30.f, CAP_VFW), +// makeParam("avi", "MJPG", 30.f, CAP_VFW), +//#endif #ifdef HAVE_QUICKTIME makeParam("mov", "mp4v", 30.f, CAP_QT), @@ -408,17 +459,18 @@ Ext_Fourcc_PSNR synthetic_params[] = { makeParam("mkv", "MJPG", 30.f, CAP_QT), #endif -#ifdef HAVE_AVFOUNDATION - makeParam("mov", "mp4v", 30.f, CAP_AVFOUNDATION), - makeParam("avi", "XVID", 30.f, CAP_AVFOUNDATION), - makeParam("avi", "MPEG", 30.f, CAP_AVFOUNDATION), - makeParam("avi", "IYUV", 30.f, CAP_AVFOUNDATION), - makeParam("avi", "MJPG", 30.f, CAP_AVFOUNDATION), +// TODO: Broken? +//#ifdef HAVE_AVFOUNDATION +// makeParam("mov", "mp4v", 30.f, CAP_AVFOUNDATION), +// makeParam("avi", "XVID", 30.f, CAP_AVFOUNDATION), +// makeParam("avi", "MPEG", 30.f, CAP_AVFOUNDATION), +// makeParam("avi", "IYUV", 30.f, CAP_AVFOUNDATION), +// makeParam("avi", "MJPG", 30.f, CAP_AVFOUNDATION), - makeParam("mkv", "XVID", 30.f, CAP_AVFOUNDATION), - makeParam("mkv", "MPEG", 30.f, CAP_AVFOUNDATION), - makeParam("mkv", "MJPG", 30.f, CAP_AVFOUNDATION), -#endif +// makeParam("mkv", "XVID", 30.f, CAP_AVFOUNDATION), +// makeParam("mkv", "MPEG", 30.f, CAP_AVFOUNDATION), +// makeParam("mkv", "MJPG", 30.f, CAP_AVFOUNDATION), +//#endif #ifdef HAVE_FFMPEG makeParam("avi", "XVID", 30.f, CAP_FFMPEG), @@ -432,15 +484,13 @@ Ext_Fourcc_PSNR synthetic_params[] = { #endif #ifdef HAVE_GSTREAMER - // makeParam("avi", "XVID", 30.f, CAP_GSTREAMER), - corrupted frames, broken indexes makeParam("avi", "MPEG", 30.f, CAP_GSTREAMER), - makeParam("avi", "IYUV", 30.f, CAP_GSTREAMER), makeParam("avi", "MJPG", 30.f, CAP_GSTREAMER), makeParam("avi", "H264", 30.f, CAP_GSTREAMER), - // makeParam("mkv", "XVID", 30.f, CAP_GSTREAMER), makeParam("mkv", "MPEG", 30.f, CAP_GSTREAMER), makeParam("mkv", "MJPG", 30.f, CAP_GSTREAMER), + makeParam("mkv", "H264", 30.f, CAP_GSTREAMER), #endif makeParam("avi", "MJPG", 30.f, CAP_OPENCV_MJPEG), diff --git a/samples/cpp/gstreamer_pipeline.cpp b/samples/cpp/gstreamer_pipeline.cpp index 4e3f108b48..19b6187ca1 100644 --- a/samples/cpp/gstreamer_pipeline.cpp +++ b/samples/cpp/gstreamer_pipeline.cpp @@ -8,144 +8,430 @@ using namespace std; using namespace cv; -string getGstDemuxPlugin(string container); -string getGstAvDecodePlugin(string codec); - -int main(int argc, char *argv[]) +class GStreamerPipeline { - const string keys = - "{h help usage ? | | print help messages }" - "{p pipeline |gst-default| pipeline name (supported: 'gst-default', 'gst-vaapi', 'gst-libav', 'ffmpeg') }" - "{ct container |mp4 | container name (supported: 'mp4', 'mov', 'avi', 'mkv') }" - "{cd codec |h264 | codec name (supported: 'h264', 'h265', 'mpeg2', 'mpeg4', 'mjpeg', 'vp8') }" - "{f file path | | path to file }" - "{fm fast | | fast measure fps }"; - - CommandLineParser parser(argc, argv, keys); - - parser.about("This program shows how to read a video file with GStreamer pipeline with OpenCV."); - - if (parser.has("help")) + public: + // Preprocessing arguments command line + GStreamerPipeline(int argc, char *argv[]) { - parser.printMessage(); - return 0; + const string keys = + "{h help usage ? | | print help messages }" + "{m mode | | coding mode (supported: encode, decode) }" + "{p pipeline |default | pipeline name (supported: 'default', 'gst-basic', 'gst-vaapi', 'gst-libav', 'ffmpeg') }" + "{ct container |mp4 | container name (supported: 'mp4', 'mov', 'avi', 'mkv') }" + "{cd codec |h264 | codec name (supported: 'h264', 'h265', 'mpeg2', 'mpeg4', 'mjpeg', 'vp8') }" + "{f file path | | path to file }" + "{vr resolution |720p | video resolution for encoding (supported: '720p', '1080p', '4k') }" + "{fps |30 | fix frame per second for encoding (supported: fps > 0) }" + "{fm fast | | fast measure fps }"; + cmd_parser = new CommandLineParser(argc, argv, keys); + cmd_parser->about("This program shows how to read a video file with GStreamer pipeline with OpenCV."); + + if (cmd_parser->has("help")) + { + cmd_parser->printMessage(); + exit_code = -1; + } + + fast_measure = cmd_parser->has("fast"); // fast measure fps + fix_fps = cmd_parser->get("fps"); // fixed frame per second + pipeline = cmd_parser->get("pipeline"), // gstreamer pipeline type + container = cmd_parser->get("container"), // container type + mode = cmd_parser->get("mode"), // coding mode + codec = cmd_parser->get("codec"), // codec type + file_name = cmd_parser->get("file"), // path to videofile + resolution = cmd_parser->get("resolution"); // video resolution + + if (!cmd_parser->check()) + { + cmd_parser->printErrors(); + exit_code = -1; + } + exit_code = 0; } - bool arg_fast_measure = parser.has("fast"); // fast measure fps - string arg_pipeline = parser.get("pipeline"), // GStreamer pipeline type - arg_container = parser.get("container"), // container type - arg_codec = parser.get("codec"), // codec type - arg_file_name = parser.get("file"); // path to videofile - VideoCapture cap; + ~GStreamerPipeline() { delete cmd_parser; } - if (!parser.check()) + // Start pipeline + int run() { - parser.printErrors(); - return 0; - } - - // Choose the constructed GStreamer pipeline - if (arg_pipeline.find("gst") == 0) - { - ostringstream pipeline; - pipeline << "filesrc location=\"" << arg_file_name << "\""; - pipeline << " ! " << getGstDemuxPlugin(arg_container); - - if (arg_pipeline.find("default") == 4) { - pipeline << " ! decodebin"; - } - else if (arg_pipeline.find("vaapi1710") == 4) + if (exit_code < 0) { return exit_code; } + if (mode == "decode") { if (createDecodePipeline() < 0) return -1; } + else if (mode == "encode") { if (createEncodePipeline() < 0) return -1; } + else { - pipeline << " ! vaapidecodebin"; - if (arg_container == "mkv") - { - pipeline << " ! autovideoconvert"; - } - else - { - pipeline << " ! video/x-raw, format=YV12"; - } + cout << "Unsupported mode: " << mode << endl; + cmd_parser->printErrors(); + return -1; } - else if (arg_pipeline.find("libav") == 4) + cout << "_____________________________________" << endl; + cout << "Pipeline " << mode << ":" << endl; + cout << stream_pipeline.str() << endl; + // Choose a show video or only measure fps + cout << "_____________________________________" << endl; + cout << "Start measure frame per seconds (fps)" << endl; + cout << "Loading ..." << endl; + + vector tick_counts; + + cout << "Start " << mode << ": " << file_name; + cout << " (" << pipeline << ")" << endl; + + while(true) { - pipeline << " ! " << getGstAvDecodePlugin(arg_codec); + int64 temp_count_tick = 0; + if (mode == "decode") + { + Mat frame; + temp_count_tick = getTickCount(); + cap >> frame; + temp_count_tick = getTickCount() - temp_count_tick; + if (frame.empty()) { break; } + } + else if (mode == "encode") + { + Mat element; + while(!cap.grab()); + cap.retrieve(element); + temp_count_tick = getTickCount(); + wrt << element; + temp_count_tick = getTickCount() - temp_count_tick; + } + + tick_counts.push_back(static_cast(temp_count_tick)); + if (((mode == "decode") && fast_measure && (tick_counts.size() > 1e3)) || + ((mode == "encode") && (tick_counts.size() > 3e3)) || + ((mode == "encode") && fast_measure && (tick_counts.size() > 1e2))) + { break; } + + } + double time_fps = sum(tick_counts)[0] / getTickFrequency(); + + if (tick_counts.size() != 0) + { + cout << "Finished: " << tick_counts.size() << " in " << time_fps <<" sec ~ " ; + cout << tick_counts.size() / time_fps <<" fps " << endl; } else { - parser.printMessage(); - cout << "Unsupported pipeline: " << arg_pipeline << endl; - return -4; + cout << "Failed " << mode << ": " << file_name; + cout << " (" << pipeline << ")" << endl; + return -1; + } + return 0; + } + + // Free video resource + void close() + { + cap.release(); + wrt.release(); + } + + private: + // Choose the constructed GStreamer pipeline for decode + int createDecodePipeline() + { + if (pipeline == "default") { + cap = VideoCapture(file_name, CAP_GSTREAMER); + } + else if (pipeline.find("gst") == 0) + { + stream_pipeline << "filesrc location=\"" << file_name << "\""; + stream_pipeline << " ! " << getGstMuxPlugin(); + + if (pipeline.find("basic") == 4) + { + stream_pipeline << getGstDefaultCodePlugin(); + } + else if (pipeline.find("vaapi1710") == 4) + { + stream_pipeline << getGstVaapiCodePlugin(); + } + else if (pipeline.find("libav") == 4) + { + stream_pipeline << getGstAvCodePlugin(); + } + else + { + cout << "Unsupported pipeline: " << pipeline << endl; + cmd_parser->printErrors(); + return -1; + } + + stream_pipeline << " ! videoconvert n-threads=" << getNumThreads(); + stream_pipeline << " ! appsink sync=false"; + cap = VideoCapture(stream_pipeline.str(), CAP_GSTREAMER); + } + else if (pipeline == "ffmpeg") + { + cap = VideoCapture(file_name, CAP_FFMPEG); + stream_pipeline << "default pipeline for ffmpeg" << endl; + } + else + { + cout << "Unsupported pipeline: " << pipeline << endl; + cmd_parser->printErrors(); + return -1; + } + return 0; + } + + // Choose the constructed GStreamer pipeline for encode + int createEncodePipeline() + { + if (checkConfiguration() < 0) return -1; + ostringstream test_pipeline; + test_pipeline << "videotestsrc pattern=smpte"; + test_pipeline << " ! video/x-raw, " << getVideoSettings(); + test_pipeline << " ! appsink sync=false"; + cap = VideoCapture(test_pipeline.str(), CAP_GSTREAMER); + + if (pipeline == "default") { + wrt = VideoWriter(file_name, CAP_GSTREAMER, getFourccCode(), fix_fps, fix_size, true); + } + else if (pipeline.find("gst") == 0) + { + stream_pipeline << "appsrc ! videoconvert n-threads=" << getNumThreads() << " ! "; + + if (pipeline.find("basic") == 4) + { + stream_pipeline << getGstDefaultCodePlugin(); + } + else if (pipeline.find("vaapi1710") == 4) + { + stream_pipeline << getGstVaapiCodePlugin(); + } + else if (pipeline.find("libav") == 4) + { + stream_pipeline << getGstAvCodePlugin(); + } + else + { + cout << "Unsupported pipeline: " << pipeline << endl; + cmd_parser->printErrors(); + return -1; + } + + stream_pipeline << " ! " << getGstMuxPlugin(); + stream_pipeline << " ! filesink location=\"" << file_name << "\""; + wrt = VideoWriter(stream_pipeline.str(), CAP_GSTREAMER, 0, fix_fps, fix_size, true); + } + else if (pipeline == "ffmpeg") + { + wrt = VideoWriter(file_name, CAP_FFMPEG, getFourccCode(), fix_fps, fix_size, true); + stream_pipeline << "default pipeline for ffmpeg" << endl; + } + else + { + cout << "Unsupported pipeline: " << pipeline << endl; + cmd_parser->printErrors(); + return -1; + } + return 0; + } + + // Choose video resolution for encoding + string getVideoSettings() + { + ostringstream video_size; + if (fix_fps > 0) { video_size << "framerate=" << fix_fps << "/1, "; } + else + { + cout << "Unsupported fps (< 0): " << fix_fps << endl; + cmd_parser->printErrors(); + return string(); } - pipeline << " ! videoconvert"; - pipeline << " n-threads=" << getNumThreads(); - pipeline << " ! appsink sync=false"; - cap = VideoCapture(pipeline.str(), CAP_GSTREAMER); + if (resolution == "720p") { fix_size = Size(1280, 720); } + else if (resolution == "1080p") { fix_size = Size(1920, 1080); } + else if (resolution == "4k") { fix_size = Size(3840, 2160); } + else + { + cout << "Unsupported video resolution: " << resolution << endl; + cmd_parser->printErrors(); + return string(); + } + + video_size << "width=" << fix_size.width << ", height=" << fix_size.height; + return video_size.str(); } - else if (arg_pipeline == "ffmpeg") + + // Choose a video container + string getGstMuxPlugin() { - cap = VideoCapture(arg_file_name, CAP_FFMPEG); + ostringstream plugin; + if (container == "avi") { plugin << "avi"; } + else if (container == "mp4") { plugin << "qt"; } + else if (container == "mov") { plugin << "qt"; } + else if (container == "mkv") { plugin << "matroska"; } + else + { + cout << "Unsupported container: " << container << endl; + cmd_parser->printErrors(); + return string(); + } + + if (mode == "decode") { plugin << "demux"; } + else if (mode == "encode") { plugin << "mux"; } + else + { + cout << "Unsupported mode: " << mode << endl; + cmd_parser->printErrors(); + return string(); + } + + return plugin.str(); } - else + + // Choose a libav codec + string getGstAvCodePlugin() { - parser.printMessage(); - cout << "Unsupported pipeline: " << arg_pipeline << endl; - return -4; + ostringstream plugin; + if (mode == "decode") + { + if (codec == "h264") { plugin << "h264parse ! "; } + else if (codec == "h265") { plugin << "h265parse ! "; } + plugin << "avdec_"; + } + else if (mode == "encode") { plugin << "avenc_"; } + else + { + cout << "Unsupported mode: " << mode << endl; + cmd_parser->printErrors(); + return string(); + } + + if (codec == "h264") { plugin << "h264"; } + else if (codec == "h265") { plugin << "h265"; } + else if (codec == "mpeg2") { plugin << "mpeg2video"; } + else if (codec == "mpeg4") { plugin << "mpeg4"; } + else if (codec == "mjpeg") { plugin << "mjpeg"; } + else if (codec == "vp8") { plugin << "vp8"; } + else + { + cout << "Unsupported libav codec: " << codec << endl; + cmd_parser->printErrors(); + return string(); + } + + return plugin.str(); } - // Choose a show video or only measure fps - cout << "_____________________________________" << '\n'; - cout << "Start measure frame per seconds (fps)" << '\n'; - cout << "Loading ..." << '\n'; - - Mat frame; - vector tick_counts; - - cout << "Start decoding: " << arg_file_name; - cout << " (" << arg_pipeline << ")" << endl; - - while(true) + // Choose a vaapi codec + string getGstVaapiCodePlugin() { - int64 temp_count_tick = getTickCount(); - cap >> frame; - temp_count_tick = getTickCount() - temp_count_tick; - if (frame.empty()) { break; } - tick_counts.push_back(static_cast(temp_count_tick)); - if (arg_fast_measure && (tick_counts.size() > 1000)) { break; } + ostringstream plugin; + if (mode == "decode") + { + plugin << "vaapidecodebin"; + if (container == "mkv") { plugin << " ! autovideoconvert"; } + else { plugin << " ! video/x-raw, format=YV12"; } + } + else if (mode == "encode") + { + if (codec == "h264") { plugin << "vaapih264enc"; } + else if (codec == "h265") { plugin << "vaapih265enc"; } + else if (codec == "mpeg2") { plugin << "vaapimpeg2enc"; } + else if (codec == "mjpeg") { plugin << "vaapijpegenc"; } + else if (codec == "vp8") { plugin << "vaapivp8enc"; } + else + { + cout << "Unsupported vaapi codec: " << codec << endl; + cmd_parser->printErrors(); + return string(); + } + } + else + { + cout << "Unsupported mode: " << resolution << endl; + cmd_parser->printErrors(); + return string(); + } + return plugin.str(); + } + // Choose a default codec + string getGstDefaultCodePlugin() + { + ostringstream plugin; + if (mode == "decode") + { + plugin << " ! decodebin"; + } + else if (mode == "encode") + { + if (codec == "h264") { plugin << "x264enc"; } + else if (codec == "h265") { plugin << "x265enc"; } + else if (codec == "mpeg2") { plugin << "mpeg2enc"; } + else if (codec == "mjpeg") { plugin << "jpegenc"; } + else if (codec == "vp8") { plugin << "vp8enc"; } + else + { + cout << "Unsupported default codec: " << codec << endl; + cmd_parser->printErrors(); + return string(); + } + } + else + { + cout << "Unsupported mode: " << resolution << endl; + cmd_parser->printErrors(); + return string(); + } + return plugin.str(); + } + // Get fourcc for codec + int getFourccCode() + { + if (codec == "h264") { return VideoWriter::fourcc('H','2','6','4'); } + else if (codec == "h265") { return VideoWriter::fourcc('H','E','V','C'); } + else if (codec == "mpeg2") { return VideoWriter::fourcc('M','P','E','G'); } + else if (codec == "mpeg4") { return VideoWriter::fourcc('M','P','4','2'); } + else if (codec == "mjpeg") { return VideoWriter::fourcc('M','J','P','G'); } + else if (codec == "vp8") { return VideoWriter::fourcc('V','P','8','0'); } + else + { + cout << "Unsupported ffmpeg codec: " << codec << endl; + cmd_parser->printErrors(); + return 0; + } } - double time_fps = sum(tick_counts)[0] / getTickFrequency(); - if (tick_counts.size() != 0) + // Check bad configuration + int checkConfiguration() { - cout << "Finished: " << tick_counts.size() << " in " << time_fps <<" sec ~ " ; - cout << tick_counts.size() / time_fps <<" fps " << endl; + if ((codec == "mpeg2" && getGstMuxPlugin() == "qtmux") || + (codec == "h265" && getGstMuxPlugin() == "avimux") || + (pipeline == "gst-libav" && (codec == "h264" || codec == "h265")) || + (pipeline == "gst-vaapi1710" && codec=="mpeg2" && resolution=="4k") || + (pipeline == "gst-vaapi1710" && codec=="mpeg2" && resolution=="1080p" && fix_fps > 30)) + { + cout << "Unsupported configuration" << endl; + cmd_parser->printErrors(); + return -1; + } + return 0; } - else - { - cout << "Failed decoding: " << arg_file_name; - cout << " (" << arg_pipeline << ")" << endl; - return -5; - } - return 0; -} - -// Choose a video container -string getGstDemuxPlugin(string container) { - if (container == "avi") { return "avidemux"; } - else if (container == "mp4") { return "qtdemux"; } - else if (container == "mov") { return "qtdemux"; } - else if (container == "mkv") { return "matroskademux"; } - return string(); -} - -// Choose a codec -string getGstAvDecodePlugin(string codec) { - if (codec == "h264") { return "h264parse ! avdec_h264"; } - else if (codec == "h265") { return "h265parse ! avdec_h265"; } - else if (codec == "mpeg2") { return "avdec_mpeg2video"; } - else if (codec == "mpeg4") { return "avdec_mpeg4"; } - else if (codec == "mjpeg") { return "avdec_mjpeg"; } - else if (codec == "vp8") { return "avdec_vp8"; } - return string(); + + bool fast_measure; // fast measure fps + string pipeline, // gstreamer pipeline type + container, // container type + mode, // coding mode + codec, // codec type + file_name, // path to videofile + resolution; // video resolution + int fix_fps; // fixed frame per second + Size fix_size; // fixed frame size + int exit_code; + VideoWriter wrt; + VideoCapture cap; + ostringstream stream_pipeline; + CommandLineParser* cmd_parser; +}; + +int main(int argc, char *argv[]) +{ + GStreamerPipeline pipe(argc, argv); + return pipe.run(); } diff --git a/samples/cpp/tutorial_code/dnn/custom_layers.cpp b/samples/cpp/tutorial_code/dnn/custom_layers.cpp new file mode 100644 index 0000000000..3fc9e61279 --- /dev/null +++ b/samples/cpp/tutorial_code/dnn/custom_layers.cpp @@ -0,0 +1,232 @@ +#include + +//! [A custom layer interface] +class MyLayer : public cv::dnn::Layer +{ +public: + //! [MyLayer::MyLayer] + MyLayer(const cv::dnn::LayerParams ¶ms); + //! [MyLayer::MyLayer] + + //! [MyLayer::create] + static cv::Ptr create(cv::dnn::LayerParams& params); + //! [MyLayer::create] + + //! [MyLayer::getMemoryShapes] + virtual bool getMemoryShapes(const std::vector > &inputs, + const int requiredOutputs, + std::vector > &outputs, + std::vector > &internals) const; + //! [MyLayer::getMemoryShapes] + + //! [MyLayer::forward] + virtual void forward(std::vector &inputs, std::vector &outputs, std::vector &internals); + //! [MyLayer::forward] + + //! [MyLayer::finalize] + virtual void finalize(const std::vector &inputs, std::vector &outputs); + //! [MyLayer::finalize] + + virtual void forward(cv::InputArrayOfArrays inputs, cv::OutputArrayOfArrays outputs, cv::OutputArrayOfArrays internals); +}; +//! [A custom layer interface] + +//! [InterpLayer] +class InterpLayer : public cv::dnn::Layer +{ +public: + InterpLayer(const cv::dnn::LayerParams ¶ms) : Layer(params) + { + outWidth = params.get("width", 0); + outHeight = params.get("height", 0); + } + + static cv::Ptr create(cv::dnn::LayerParams& params) + { + return cv::Ptr(new InterpLayer(params)); + } + + virtual bool getMemoryShapes(const std::vector > &inputs, + const int requiredOutputs, + std::vector > &outputs, + std::vector > &internals) const + { + CV_UNUSED(requiredOutputs); CV_UNUSED(internals); + std::vector outShape(4); + outShape[0] = inputs[0][0]; // batch size + outShape[1] = inputs[0][1]; // number of channels + outShape[2] = outHeight; + outShape[3] = outWidth; + outputs.assign(1, outShape); + return false; + } + + // Implementation of this custom layer is based on https://github.com/cdmh/deeplab-public/blob/master/src/caffe/layers/interp_layer.cpp + virtual void forward(std::vector &inputs, std::vector &outputs, std::vector &internals) + { + CV_UNUSED(internals); + cv::Mat& inp = *inputs[0]; + cv::Mat& out = outputs[0]; + const float* inpData = (float*)inp.data; + float* outData = (float*)out.data; + + const int batchSize = inp.size[0]; + const int numChannels = inp.size[1]; + const int inpHeight = inp.size[2]; + const int inpWidth = inp.size[3]; + + const float rheight = (outHeight > 1) ? static_cast(inpHeight - 1) / (outHeight - 1) : 0.f; + const float rwidth = (outWidth > 1) ? static_cast(inpWidth - 1) / (outWidth - 1) : 0.f; + for (int h2 = 0; h2 < outHeight; ++h2) + { + const float h1r = rheight * h2; + const int h1 = static_cast(h1r); + const int h1p = (h1 < inpHeight - 1) ? 1 : 0; + const float h1lambda = h1r - h1; + const float h0lambda = 1.f - h1lambda; + for (int w2 = 0; w2 < outWidth; ++w2) + { + const float w1r = rwidth * w2; + const int w1 = static_cast(w1r); + const int w1p = (w1 < inpWidth - 1) ? 1 : 0; + const float w1lambda = w1r - w1; + const float w0lambda = 1.f - w1lambda; + const float* pos1 = inpData + h1 * inpWidth + w1; + float* pos2 = outData + h2 * outWidth + w2; + for (int c = 0; c < batchSize * numChannels; ++c) + { + pos2[0] = + h0lambda * (w0lambda * pos1[0] + w1lambda * pos1[w1p]) + + h1lambda * (w0lambda * pos1[h1p * inpWidth] + w1lambda * pos1[h1p * inpWidth + w1p]); + pos1 += inpWidth * inpHeight; + pos2 += outWidth * outHeight; + } + } + } + } + + virtual void forward(cv::InputArrayOfArrays, cv::OutputArrayOfArrays, cv::OutputArrayOfArrays) {} + +private: + int outWidth, outHeight; +}; +//! [InterpLayer] + +//! [ResizeBilinearLayer] +class ResizeBilinearLayer : public cv::dnn::Layer +{ +public: + ResizeBilinearLayer(const cv::dnn::LayerParams ¶ms) : Layer(params) + { + CV_Assert(!params.get("align_corners", false)); + CV_Assert(blobs.size() == 1, blobs[0].type() == CV_32SC1); + outHeight = blobs[0].at(0, 0); + outWidth = blobs[0].at(0, 1); + } + + static cv::Ptr create(cv::dnn::LayerParams& params) + { + return cv::Ptr(new ResizeBilinearLayer(params)); + } + + virtual bool getMemoryShapes(const std::vector > &inputs, + const int requiredOutputs, + std::vector > &outputs, + std::vector > &internals) const + { + CV_UNUSED(requiredOutputs); CV_UNUSED(internals); + std::vector outShape(4); + outShape[0] = inputs[0][0]; // batch size + outShape[1] = inputs[0][1]; // number of channels + outShape[2] = outHeight; + outShape[3] = outWidth; + outputs.assign(1, outShape); + return false; + } + + // This implementation is based on a reference implementation from + // https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/lite/kernels/internal/reference/reference_ops.h + virtual void forward(std::vector &inputs, std::vector &outputs, std::vector &internals) + { + CV_UNUSED(internals); + cv::Mat& inp = *inputs[0]; + cv::Mat& out = outputs[0]; + const float* inpData = (float*)inp.data; + float* outData = (float*)out.data; + + const int batchSize = inp.size[0]; + const int numChannels = inp.size[1]; + const int inpHeight = inp.size[2]; + const int inpWidth = inp.size[3]; + + float heightScale = static_cast(inpHeight) / outHeight; + float widthScale = static_cast(inpWidth) / outWidth; + for (int b = 0; b < batchSize; ++b) + { + for (int y = 0; y < outHeight; ++y) + { + float input_y = y * heightScale; + int y0 = static_cast(std::floor(input_y)); + int y1 = std::min(y0 + 1, inpHeight - 1); + for (int x = 0; x < outWidth; ++x) + { + float input_x = x * widthScale; + int x0 = static_cast(std::floor(input_x)); + int x1 = std::min(x0 + 1, inpWidth - 1); + for (int c = 0; c < numChannels; ++c) + { + float interpolation = + inpData[offset(inp.size, c, x0, y0, b)] * (1 - (input_y - y0)) * (1 - (input_x - x0)) + + inpData[offset(inp.size, c, x0, y1, b)] * (input_y - y0) * (1 - (input_x - x0)) + + inpData[offset(inp.size, c, x1, y0, b)] * (1 - (input_y - y0)) * (input_x - x0) + + inpData[offset(inp.size, c, x1, y1, b)] * (input_y - y0) * (input_x - x0); + outData[offset(out.size, c, x, y, b)] = interpolation; + } + } + } + } + } + + virtual void forward(cv::InputArrayOfArrays, cv::OutputArrayOfArrays, cv::OutputArrayOfArrays) {} + +private: + static inline int offset(const cv::MatSize& size, int c, int x, int y, int b) + { + return x + size[3] * (y + size[2] * (c + size[1] * b)); + } + + int outWidth, outHeight; +}; +//! [ResizeBilinearLayer] + +//! [Register a custom layer] +#include // CV_DNN_REGISTER_LAYER_CLASS macro + +int main(int argc, char** argv) +{ + CV_DNN_REGISTER_LAYER_CLASS(MyType, MyLayer); + // ... + //! [Register a custom layer] + CV_UNUSED(argc); CV_UNUSED(argv); + //! [Register InterpLayer] + CV_DNN_REGISTER_LAYER_CLASS(Interp, InterpLayer); + cv::dnn::Net caffeNet = cv::dnn::readNet("/path/to/config.prototxt", "/path/to/weights.caffemodel"); + //! [Register InterpLayer] + + //! [Register ResizeBilinearLayer] + CV_DNN_REGISTER_LAYER_CLASS(ResizeBilinear, ResizeBilinearLayer); + cv::dnn::Net tfNet = cv::dnn::readNet("/path/to/graph.pb"); + //! [Register ResizeBilinearLayer] +} + +cv::Ptr MyLayer::create(cv::dnn::LayerParams& params) +{ + return cv::Ptr(new MyLayer(params)); +} +MyLayer::MyLayer(const cv::dnn::LayerParams&) {} +bool MyLayer::getMemoryShapes(const std::vector >&, const int, + std::vector >&, + std::vector >&) const { return false; } +void MyLayer::forward(std::vector&, std::vector&, std::vector&) {} +void MyLayer::finalize(const std::vector&, std::vector&) {} +void MyLayer::forward(cv::InputArrayOfArrays, cv::OutputArrayOfArrays, cv::OutputArrayOfArrays) {} diff --git a/samples/dnn/object_detection.cpp b/samples/dnn/object_detection.cpp index 16d82eb0e7..5ff537bdbd 100644 --- a/samples/dnn/object_detection.cpp +++ b/samples/dnn/object_detection.cpp @@ -214,7 +214,7 @@ void postprocess(Mat& frame, const std::vector& outs, Net& net) } } std::vector indices; - NMSBoxes(boxes, confidences, confThreshold, 0.4, indices); + NMSBoxes(boxes, confidences, confThreshold, 0.4f, indices); for (size_t i = 0; i < indices.size(); ++i) { int idx = indices[i];