diff --git a/modules/videoio/include/opencv2/videoio.hpp b/modules/videoio/include/opencv2/videoio.hpp index e5b74eed38..c25c8e83ae 100644 --- a/modules/videoio/include/opencv2/videoio.hpp +++ b/modules/videoio/include/opencv2/videoio.hpp @@ -189,7 +189,9 @@ enum VideoCaptureProperties { enum VideoWriterProperties { VIDEOWRITER_PROP_QUALITY = 1, //!< Current quality (0..100%) of the encoded videostream. Can be adjusted dynamically in some codecs. VIDEOWRITER_PROP_FRAMEBYTES = 2, //!< (Read-only): Size of just encoded video frame. Note that the encoding order may be different from representation order. - VIDEOWRITER_PROP_NSTRIPES = 3 //!< Number of stripes for parallel encoding. -1 for auto detection. + VIDEOWRITER_PROP_NSTRIPES = 3, //!< Number of stripes for parallel encoding. -1 for auto detection. + VIDEOWRITER_PROP_IS_COLOR = 4 //!< If it is not zero, the encoder will expect and encode color frames, otherwise it + //!< will work with grayscale frames. }; //! @} videoio_flags_base @@ -876,7 +878,7 @@ public: @param fps Framerate of the created video stream. @param frameSize Size of the video frames. @param isColor If it is not zero, the encoder will expect and encode color frames, otherwise it - will work with grayscale frames (the flag is currently supported on Windows only). + will work with grayscale frames. @b Tips: - With some backends `fourcc=-1` pops up the codec selection dialog from the system. @@ -896,6 +898,18 @@ public: CV_WRAP VideoWriter(const String& filename, int apiPreference, int fourcc, double fps, Size frameSize, bool isColor = true); + /** @overload + * The `params` parameter allows to specify extra encoder parameters encoded as pairs (paramId_1, paramValue_1, paramId_2, paramValue_2, ... .) + * see cv::VideoWriterProperties + */ + CV_WRAP VideoWriter(const String& filename, int fourcc, double fps, const Size& frameSize, + const std::vector& params); + + /** @overload + */ + CV_WRAP VideoWriter(const String& filename, int apiPreference, int fourcc, double fps, + const Size& frameSize, const std::vector& params); + /** @brief Default destructor The method first calls VideoWriter::release to close the already opened file. @@ -918,6 +932,16 @@ public: CV_WRAP bool open(const String& filename, int apiPreference, int fourcc, double fps, Size frameSize, bool isColor = true); + /** @overload + */ + CV_WRAP bool open(const String& filename, int fourcc, double fps, const Size& frameSize, + const std::vector& params); + + /** @overload + */ + CV_WRAP bool open(const String& filename, int apiPreference, int fourcc, double fps, + const Size& frameSize, const std::vector& params); + /** @brief Returns true if video writer has been successfully initialized. */ CV_WRAP virtual bool isOpened() const; diff --git a/modules/videoio/src/backend.hpp b/modules/videoio/src/backend.hpp index e8b269ba79..1b2a1e6cbb 100644 --- a/modules/videoio/src/backend.hpp +++ b/modules/videoio/src/backend.hpp @@ -18,7 +18,8 @@ public: virtual ~IBackend() {} virtual Ptr createCapture(int camera) const = 0; virtual Ptr createCapture(const std::string &filename) const = 0; - virtual Ptr createWriter(const std::string &filename, int fourcc, double fps, const cv::Size &sz, bool isColor) const = 0; + virtual Ptr createWriter(const std::string& filename, int fourcc, double fps, const cv::Size& sz, + const VideoWriterParameters& params) const = 0; }; class IBackendFactory @@ -32,7 +33,8 @@ public: typedef Ptr (*FN_createCaptureFile)(const std::string & filename); typedef Ptr (*FN_createCaptureCamera)(int camera); -typedef Ptr (*FN_createWriter)(const std::string& filename, int fourcc, double fps, const Size& sz, bool isColor); +typedef Ptr (*FN_createWriter)(const std::string& filename, int fourcc, double fps, const Size& sz, + const VideoWriterParameters& params); Ptr createBackendFactory(FN_createCaptureFile createCaptureFile, FN_createCaptureCamera createCaptureCamera, FN_createWriter createWriter); diff --git a/modules/videoio/src/backend_plugin.cpp b/modules/videoio/src/backend_plugin.cpp index d8b7c3014e..e7f95a1135 100644 --- a/modules/videoio/src/backend_plugin.cpp +++ b/modules/videoio/src/backend_plugin.cpp @@ -243,7 +243,8 @@ public: Ptr createCapture(int camera) const CV_OVERRIDE; Ptr createCapture(const std::string &filename) const CV_OVERRIDE; - Ptr createWriter(const std::string &filename, int fourcc, double fps, const cv::Size &sz, bool isColor) const CV_OVERRIDE; + Ptr createWriter(const std::string& filename, int fourcc, double fps, + const cv::Size& sz, const VideoWriterParameters& params) const CV_OVERRIDE; }; class PluginBackendFactory : public IBackendFactory @@ -502,7 +503,8 @@ class PluginWriter : public cv::IVideoWriter public: static Ptr create(const OpenCV_VideoIO_Plugin_API_preview* plugin_api, - const std::string &filename, int fourcc, double fps, const cv::Size &sz, bool isColor) + const std::string& filename, int fourcc, double fps, const cv::Size& sz, + const VideoWriterParameters& params) { CV_Assert(plugin_api); CvPluginWriter writer = NULL; @@ -510,6 +512,7 @@ public: { CV_Assert(plugin_api->Writer_release); CV_Assert(!filename.empty()); + const bool isColor = params.get(VIDEOWRITER_PROP_IS_COLOR, true); if (CV_ERROR_OK == plugin_api->Writer_open(filename.c_str(), fourcc, fps, sz.width, sz.height, isColor, &writer)) { CV_Assert(writer); @@ -597,12 +600,13 @@ Ptr PluginBackend::createCapture(const std::string &filename) con return Ptr(); } -Ptr PluginBackend::createWriter(const std::string &filename, int fourcc, double fps, const cv::Size &sz, bool isColor) const +Ptr PluginBackend::createWriter(const std::string& filename, int fourcc, double fps, + const cv::Size& sz, const VideoWriterParameters& params) const { try { if (plugin_api_) - return PluginWriter::create(plugin_api_, filename, fourcc, fps, sz, isColor); //.staticCast(); + return PluginWriter::create(plugin_api_, filename, fourcc, fps, sz, params); //.staticCast(); } catch (...) { diff --git a/modules/videoio/src/backend_static.cpp b/modules/videoio/src/backend_static.cpp index 45ece063e8..567628665b 100644 --- a/modules/videoio/src/backend_static.cpp +++ b/modules/videoio/src/backend_static.cpp @@ -34,10 +34,11 @@ public: return fn_createCaptureFile_(filename); return Ptr(); } - Ptr createWriter(const std::string &filename, int fourcc, double fps, const cv::Size &sz, bool isColor) const CV_OVERRIDE + Ptr createWriter(const std::string& filename, int fourcc, double fps, + const cv::Size& sz, const VideoWriterParameters& params) const CV_OVERRIDE { if (fn_createWriter_) - return fn_createWriter_(filename, fourcc, fps, sz, isColor); + return fn_createWriter_(filename, fourcc, fps, sz, params); return Ptr(); } }; // StaticBackend diff --git a/modules/videoio/src/cap.cpp b/modules/videoio/src/cap.cpp index c4206fbfbd..77a2c3671e 100644 --- a/modules/videoio/src/cap.cpp +++ b/modules/videoio/src/cap.cpp @@ -458,6 +458,18 @@ VideoWriter::VideoWriter(const String& filename, int apiPreference, int _fourcc, open(filename, apiPreference, _fourcc, fps, frameSize, isColor); } +VideoWriter::VideoWriter(const cv::String& filename, int fourcc, double fps, + const cv::Size& frameSize, const std::vector& params) +{ + open(filename, fourcc, fps, frameSize, params); +} + +VideoWriter::VideoWriter(const cv::String& filename, int apiPreference, int fourcc, double fps, + const cv::Size& frameSize, const std::vector& params) +{ + open(filename, apiPreference, fourcc, fps, frameSize, params); +} + void VideoWriter::release() { iwriter.release(); @@ -471,11 +483,26 @@ VideoWriter::~VideoWriter() bool VideoWriter::open(const String& filename, int _fourcc, double fps, Size frameSize, bool isColor) { - return open(filename, CAP_ANY, _fourcc, fps, frameSize, isColor); + return open(filename, CAP_ANY, _fourcc, fps, frameSize, + std::vector { VIDEOWRITER_PROP_IS_COLOR, static_cast(isColor) }); } bool VideoWriter::open(const String& filename, int apiPreference, int _fourcc, double fps, Size frameSize, bool isColor) +{ + return open(filename, apiPreference, _fourcc, fps, frameSize, + std::vector { VIDEOWRITER_PROP_IS_COLOR, static_cast(isColor) }); +} + + +bool VideoWriter::open(const String& filename, int fourcc, double fps, const Size& frameSize, + const std::vector& params) +{ + return open(filename, CAP_ANY, fourcc, fps, frameSize, params); +} + +bool VideoWriter::open(const String& filename, int apiPreference, int fourcc, double fps, + const Size& frameSize, const std::vector& params) { CV_INSTRUMENT_REGION(); @@ -484,30 +511,39 @@ bool VideoWriter::open(const String& filename, int apiPreference, int _fourcc, d release(); } - const std::vector backends = cv::videoio_registry::getAvailableBackends_Writer(); - for (size_t i = 0; i < backends.size(); i++) + const VideoWriterParameters parameters(params); + for (const auto& info : videoio_registry::getAvailableBackends_Writer()) { - const VideoBackendInfo& info = backends[i]; if (apiPreference == CAP_ANY || apiPreference == info.id) { CV_WRITER_LOG_DEBUG(NULL, cv::format("VIDEOIO(%s): trying writer with filename='%s' " "fourcc=0x%08x fps=%g sz=%dx%d isColor=%d...", - info.name, filename.c_str(), (unsigned)_fourcc, fps, - frameSize.width, frameSize.height, (int)isColor)); + info.name, filename.c_str(), (unsigned)fourcc, fps, + frameSize.width, frameSize.height, + parameters.get(VIDEOWRITER_PROP_IS_COLOR, true))); CV_Assert(!info.backendFactory.empty()); const Ptr backend = info.backendFactory->getBackend(); if (!backend.empty()) { try { - iwriter = backend->createWriter(filename, _fourcc, fps, frameSize, isColor); + iwriter = backend->createWriter(filename, fourcc, fps, frameSize, parameters); if (!iwriter.empty()) { CV_WRITER_LOG_DEBUG(NULL, cv::format("VIDEOIO(%s): created, isOpened=%d", info.name, iwriter->isOpened())); + if (param_VIDEOIO_DEBUG || param_VIDEOWRITER_DEBUG) + { + for (int key: parameters.getUnused()) + { + CV_LOG_WARNING(NULL, + cv::format("VIDEOIO(%s): parameter with key '%d' was unused", + info.name, key)); + } + } if (iwriter->isOpened()) { return true; @@ -529,7 +565,7 @@ bool VideoWriter::open(const String& filename, int apiPreference, int _fourcc, d catch (const std::exception& e) { CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised C++ exception:\n\n%s\n", - info.name, e.what())); + info.name, e.what())); } catch (...) { diff --git a/modules/videoio/src/cap_avfoundation.mm b/modules/videoio/src/cap_avfoundation.mm index 9a61292ee8..3b94d49015 100644 --- a/modules/videoio/src/cap_avfoundation.mm +++ b/modules/videoio/src/cap_avfoundation.mm @@ -221,9 +221,12 @@ cv::Ptr cv::create_AVFoundation_capture_cam(int index) return 0; } -cv::Ptr cv::create_AVFoundation_writer(const std::string& filename, int fourcc, double fps, const cv::Size &frameSize, bool isColor) +cv::Ptr cv::create_AVFoundation_writer(const std::string& filename, int fourcc, + double fps, const cv::Size &frameSize, + const cv::VideoWriterParameters& params) { CvSize sz = { frameSize.width, frameSize.height }; + const bool isColor = params.get(VIDEOWRITER_PROP_IS_COLOR, true); CvVideoWriter_AVFoundation* wrt = new CvVideoWriter_AVFoundation(filename.c_str(), fourcc, fps, sz, isColor); return cv::makePtr(wrt); } diff --git a/modules/videoio/src/cap_avfoundation_mac.mm b/modules/videoio/src/cap_avfoundation_mac.mm index 230e6d743d..011bc08466 100644 --- a/modules/videoio/src/cap_avfoundation_mac.mm +++ b/modules/videoio/src/cap_avfoundation_mac.mm @@ -231,9 +231,12 @@ cv::Ptr cv::create_AVFoundation_capture_cam(int index) return 0; } -cv::Ptr cv::create_AVFoundation_writer(const std::string& filename, int fourcc, double fps, const cv::Size &frameSize, bool isColor) +cv::Ptr cv::create_AVFoundation_writer(const std::string& filename, int fourcc, + double fps, const cv::Size& frameSize, + const cv::VideoWriterParameters& params) { CvSize sz = { frameSize.width, frameSize.height }; + const bool isColor = params.get(VIDEOWRITER_PROP_IS_COLOR, true); CvVideoWriter_AVFoundation* wrt = new CvVideoWriter_AVFoundation(filename, fourcc, fps, sz, isColor); if (wrt->isOpened()) { diff --git a/modules/videoio/src/cap_ffmpeg.cpp b/modules/videoio/src/cap_ffmpeg.cpp index 5d8003cdef..b436b75e11 100644 --- a/modules/videoio/src/cap_ffmpeg.cpp +++ b/modules/videoio/src/cap_ffmpeg.cpp @@ -170,9 +170,12 @@ protected: } // namespace -cv::Ptr cvCreateVideoWriter_FFMPEG_proxy(const std::string& filename, int fourcc, double fps, const cv::Size &frameSize, bool isColor) +cv::Ptr cvCreateVideoWriter_FFMPEG_proxy(const std::string& filename, int fourcc, + double fps, const cv::Size& frameSize, + const VideoWriterParameters& params) { - cv::Ptr writer = cv::makePtr(filename, fourcc, fps, frameSize, isColor != 0); + const bool isColor = params.get(VIDEOWRITER_PROP_IS_COLOR, true); + cv::Ptr writer = cv::makePtr(filename, fourcc, fps, frameSize, isColor); if (writer && writer->isOpened()) return writer; return cv::Ptr(); diff --git a/modules/videoio/src/cap_gstreamer.cpp b/modules/videoio/src/cap_gstreamer.cpp index 18d6101ece..4d9330daf8 100644 --- a/modules/videoio/src/cap_gstreamer.cpp +++ b/modules/videoio/src/cap_gstreamer.cpp @@ -1673,9 +1673,11 @@ bool CvVideoWriter_GStreamer::writeFrame( const IplImage * image ) return true; } -Ptr create_GStreamer_writer(const std::string &filename, int fourcc, double fps, const cv::Size &frameSize, bool isColor) +Ptr create_GStreamer_writer(const std::string& filename, int fourcc, double fps, + const cv::Size& frameSize, const VideoWriterParameters& params) { CvVideoWriter_GStreamer* wrt = new CvVideoWriter_GStreamer; + const bool isColor = params.get(VIDEOWRITER_PROP_IS_COLOR, true); try { if (wrt->open(filename, fourcc, fps, frameSize, isColor)) diff --git a/modules/videoio/src/cap_images.cpp b/modules/videoio/src/cap_images.cpp index 92de9247a8..b506dd1c06 100644 --- a/modules/videoio/src/cap_images.cpp +++ b/modules/videoio/src/cap_images.cpp @@ -429,7 +429,8 @@ bool CvVideoWriter_Images::setProperty( int id, double value ) return false; // not supported } -Ptr create_Images_writer(const std::string &filename, int, double, const Size &, bool) +Ptr create_Images_writer(const std::string &filename, int, double, const Size &, + const cv::VideoWriterParameters&) { CvVideoWriter_Images *writer = new CvVideoWriter_Images; diff --git a/modules/videoio/src/cap_interface.hpp b/modules/videoio/src/cap_interface.hpp index 1ebdd33063..34bef9b9e1 100644 --- a/modules/videoio/src/cap_interface.hpp +++ b/modules/videoio/src/cap_interface.hpp @@ -37,6 +37,88 @@ struct CvVideoWriter namespace cv { +namespace +{ +template +inline T castParameterTo(int paramValue) +{ + return static_cast(paramValue); +} + +template <> +inline bool castParameterTo(int paramValue) +{ + return paramValue != 0; +} +} + +class VideoWriterParameters +{ +public: + struct VideoWriterParameter { + VideoWriterParameter() = default; + + VideoWriterParameter(int key_, int value_) : key(key_), value(value_) {} + + int key{-1}; + int value{-1}; + mutable bool isConsumed{false}; + }; + + VideoWriterParameters() = default; + + explicit VideoWriterParameters(const std::vector& params) + { + const auto count = params.size(); + if (count % 2 != 0) + { + CV_Error_(Error::StsVecLengthErr, + ("Vector of VideoWriter parameters should have even length")); + } + params_.reserve(count / 2); + for (std::size_t i = 0; i < count; i += 2) + { + add(params[i], params[i + 1]); + } + } + + void add(int key, int value) + { + params_.emplace_back(key, value); + } + + template + ValueType get(int key, ValueType defaultValue) const CV_NOEXCEPT + { + auto it = std::find_if(params_.begin(), params_.end(), + [key](const VideoWriterParameter ¶m) { + return param.key == key; + }); + if (it != params_.end()) + { + it->isConsumed = true; + return castParameterTo(it->value); + } + else + { + return defaultValue; + } + } + + std::vector getUnused() const CV_NOEXCEPT { + std::vector unusedParams; + for (const auto ¶m : params_) + { + if (!param.isConsumed) + { + unusedParams.push_back(param.key); + } + } + return unusedParams; + } +private: + std::vector params_; +}; class IVideoCapture { @@ -168,24 +250,34 @@ public: //================================================================================================== Ptr cvCreateFileCapture_FFMPEG_proxy(const std::string &filename); -Ptr cvCreateVideoWriter_FFMPEG_proxy(const std::string& filename, int fourcc, double fps, const Size &frameSize, bool isColor); +Ptr cvCreateVideoWriter_FFMPEG_proxy(const std::string& filename, int fourcc, + double fps, const Size& frameSize, + const VideoWriterParameters& params); Ptr createGStreamerCapture_file(const std::string& filename); Ptr createGStreamerCapture_cam(int index); -Ptr create_GStreamer_writer(const std::string& filename, int fourcc, double fps, const Size &frameSize, bool isColor); +Ptr create_GStreamer_writer(const std::string& filename, int fourcc, + double fps, const Size& frameSize, + const VideoWriterParameters& params); Ptr create_MFX_capture(const std::string &filename); -Ptr create_MFX_writer(const std::string &filename, int _fourcc, double fps, const Size &frameSize, bool isColor); +Ptr create_MFX_writer(const std::string& filename, int _fourcc, + double fps, const Size& frameSize, + const VideoWriterParameters& params); Ptr create_AVFoundation_capture_file(const std::string &filename); Ptr create_AVFoundation_capture_cam(int index); -Ptr create_AVFoundation_writer(const std::string& filename, int fourcc, double fps, const Size &frameSize, bool isColor); +Ptr create_AVFoundation_writer(const std::string& filename, int fourcc, + double fps, const Size& frameSize, + const VideoWriterParameters& params); Ptr create_WRT_capture(int device); Ptr cvCreateCapture_MSMF(int index); Ptr cvCreateCapture_MSMF(const std::string& filename); -Ptr cvCreateVideoWriter_MSMF(const std::string& filename, int fourcc, double fps, const Size &frameSize, bool is_color); +Ptr cvCreateVideoWriter_MSMF(const std::string& filename, int fourcc, + double fps, const Size& frameSize, + const VideoWriterParameters& params); Ptr create_DShow_capture(int index); @@ -196,7 +288,9 @@ Ptr create_OpenNI2_capture_cam( int index ); Ptr create_OpenNI2_capture_file( const std::string &filename ); Ptr create_Images_capture(const std::string &filename); -Ptr create_Images_writer(const std::string &filename, int fourcc, double fps, const Size &frameSize, bool iscolor); +Ptr create_Images_writer(const std::string& filename, int fourcc, + double fps, const Size& frameSize, + const VideoWriterParameters& params); Ptr create_DC1394_capture(int index); @@ -210,7 +304,9 @@ Ptr create_XIMEA_capture_file( const std::string &serialNumber ); Ptr create_Aravis_capture( int index ); Ptr createMotionJpegCapture(const std::string& filename); -Ptr createMotionJpegWriter(const std::string &filename, int fourcc, double fps, const Size &frameSize, bool iscolor); +Ptr createMotionJpegWriter(const std::string& filename, int fourcc, + double fps, const Size& frameSize, + const VideoWriterParameters& params); Ptr createGPhoto2Capture(int index); Ptr createGPhoto2Capture(const std::string& deviceName); diff --git a/modules/videoio/src/cap_mfx_writer.cpp b/modules/videoio/src/cap_mfx_writer.cpp index 07d4ec40ce..907684cbfc 100644 --- a/modules/videoio/src/cap_mfx_writer.cpp +++ b/modules/videoio/src/cap_mfx_writer.cpp @@ -251,10 +251,12 @@ bool VideoWriter_IntelMFX::write_one(cv::InputArray bgr) } } -Ptr cv::create_MFX_writer(const std::string &filename, int _fourcc, double fps, const Size &frameSize, bool isColor) +Ptr cv::create_MFX_writer(const std::string& filename, int _fourcc, double fps, + const Size& frameSize, const VideoWriterParameters& params) { if (codecIdByFourCC(_fourcc) > 0) { + const bool isColor = params.get(VIDEOWRITER_PROP_IS_COLOR, true); Ptr a = makePtr(filename, _fourcc, fps, frameSize, isColor); if (a->isOpened()) return a; diff --git a/modules/videoio/src/cap_mjpeg_encoder.cpp b/modules/videoio/src/cap_mjpeg_encoder.cpp index 851710cf07..48920ac23f 100644 --- a/modules/videoio/src/cap_mjpeg_encoder.cpp +++ b/modules/videoio/src/cap_mjpeg_encoder.cpp @@ -1532,12 +1532,15 @@ void MotionJpegWriter::writeFrameData( const uchar* data, int step, int colorspa } -Ptr createMotionJpegWriter(const std::string &filename, int fourcc, double fps, const Size &frameSize, bool iscolor) +Ptr createMotionJpegWriter(const std::string& filename, int fourcc, + double fps, const Size& frameSize, + const VideoWriterParameters& params) { if (fourcc != CV_FOURCC('M', 'J', 'P', 'G')) return Ptr(); - Ptr iwriter = makePtr(filename, fps, frameSize, iscolor); + const bool isColor = params.get(VIDEOWRITER_PROP_IS_COLOR, true); + Ptr iwriter = makePtr(filename, fps, frameSize, isColor); if( !iwriter->isOpened() ) iwriter.release(); return iwriter; diff --git a/modules/videoio/src/cap_msmf.cpp b/modules/videoio/src/cap_msmf.cpp index b58e6beb11..c90579e0b8 100644 --- a/modules/videoio/src/cap_msmf.cpp +++ b/modules/videoio/src/cap_msmf.cpp @@ -1656,11 +1656,13 @@ void CvVideoWriter_MSMF::write(cv::InputArray img) } cv::Ptr cv::cvCreateVideoWriter_MSMF( const std::string& filename, int fourcc, - double fps, const cv::Size &frameSize, bool isColor ) + double fps, const cv::Size& frameSize, + const VideoWriterParameters& params) { cv::Ptr writer = cv::makePtr(); if (writer) { + const bool isColor = params.get(VIDEOWRITER_PROP_IS_COLOR, true); writer->open(filename, fourcc, fps, frameSize, isColor); if (writer->isOpened()) return writer;