Skip to content

Add CV_16UC1/GRAY16_LE support to GStreamer backend for VideoWriter#18535

Merged
alalek merged 5 commits intoopencv:masterfrom
joshdoe:gray16_gstreamer_writing
Dec 5, 2020
Merged

Add CV_16UC1/GRAY16_LE support to GStreamer backend for VideoWriter#18535
alalek merged 5 commits intoopencv:masterfrom
joshdoe:gray16_gstreamer_writing

Conversation

@joshdoe
Copy link
Copy Markdown
Contributor

@joshdoe joshdoe commented Oct 7, 2020

See #14035 for a companion PR to add CV_16UC1/GRAY16_LE capture support. I'm new to test writing, but added one that seems to make sense. I'm not clear if all PRs are still supposed to be based off 3.4, but I ran into roadblocks trying to build 3.4 at all, master just worked so easily.

Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

  • I agree to contribute to the project under Apache 2 License.
  • To the best of my knowledge, the proposed patch is not based on a code under GPL or other license that is incompatible with OpenCV
  • The PR is proposed to proper branch
  • There is reference to original bug report and related work
  • There is accuracy test, performance test and test data in opencv_extra repository, if applicable
    Patch to opencv_extra has the same branch name.
  • The feature is well documented and sample code can be built with the project CMake
force_builders=Linux AVX2,Custom
build_image:Custom=gstreamer:16.04
buildworker:Custom=linux-1,linux-2,linux-4
test_modules:Custom=videoio,python2,python3,java

@asmorkalov
Copy link
Copy Markdown
Contributor

@joshdoe Thanks for the patch! It's very cool that you bring test with the new feature, but there is issue with VideoWriter behavior.

Actual pipeline initialization is delayed till the first frame. It means that open call returns true and VideoWriter reports that it's open, but actual initialization may fail on first frame. It's not what existing code expects to get. I propose to set depth on open call explicitly. Please take a look on cv::VideoWriter Documentation: https://docs.opencv.org/master/dd/d9e/classcv_1_1VideoWriter.html#aff57b7ffbd654ef2f83e7ad76916426b. It takes std::vector<int> to get extra configuration options in key key->value way. You need to introduce new key with output video depth and pass it to GStreamer backed in create_GStreamer_writer (https://github.com/opencv/opencv/blob/master/modules/videoio/src/cap_gstreamer.cpp#L1697). Please re-use existing constants for cv::Mat depth. Default behavior (rgb/gray) should be presumed to not break existing code.

@asmorkalov asmorkalov requested review from asmorkalov and removed request for VadimLevin November 24, 2020 10:32
@asmorkalov asmorkalov self-assigned this Nov 24, 2020
@joshdoe
Copy link
Copy Markdown
Contributor Author

joshdoe commented Nov 24, 2020

@asmorkalov thanks for taking a look at this. Do you think I should get rid of all autodetection from first frame, or make it optional based on if VIDEOWRITER_PROP_DEPTH is -1 (but default it to CV_8U)?

@joshdoe joshdoe closed this Nov 24, 2020
@joshdoe joshdoe force-pushed the gray16_gstreamer_writing branch from 69abe4b to e1a8fc0 Compare November 24, 2020 19:29
@joshdoe joshdoe reopened this Nov 24, 2020
@joshdoe
Copy link
Copy Markdown
Contributor Author

joshdoe commented Nov 24, 2020

I think I've done what you asked. If you don't like the auto-detection option, that can easily be removed.

@joshdoe joshdoe force-pushed the gray16_gstreamer_writing branch from 41bc9eb to fff866d Compare November 24, 2020 20:00
@joshdoe
Copy link
Copy Markdown
Contributor Author

joshdoe commented Nov 24, 2020

I'm getting failures on the buildbot with the tests I've implemented, however locally with Win64 VS2019 I have no issues. I'm trying to build now on Ubuntu 16.04.

@joshdoe
Copy link
Copy Markdown
Contributor Author

joshdoe commented Nov 24, 2020

Tests are passing for me in Ubuntu 16.04 as well. Not sure how to replicate the failures from the buildbot.

@joshdoe
Copy link
Copy Markdown
Contributor Author

joshdoe commented Nov 25, 2020

I see the problem now. The buildbot builds the GStreamer backend as a plugin, and I've been building it statically. I've just tried building it as a plugin, and hit a compilation error. From the looks of it, the way plugins are defined, there's no way to pass arbitrary parameters, that is, not all entries from VideoWriterParameters get propagated to cv_writer_open. I see a few options:

  1. Only enable GRAY16_LE support when GStreamer is built statically. This would be suboptimal.
  2. Using Writer_setProperty allow the depth to be set after opening, but before writing the first frame, which would override the input_pix_fmt and caps. This wouldn't require changes outside the GStreamer module sources, however it would be unintuitive I think.
  3. Modify the plugin API to pass VideoWriterParameters to the plugin Writer_open function. This would be a breaking change, unless an Writer_open2 function was introduced. And also the parameters I'm guessing would need to be repacked into a more elementary data structure.

Maybe @mshabunin could give some input on the way forward. Thanks.

@asmorkalov
Copy link
Copy Markdown
Contributor

@joshdoe Let me check the plugin issue with teammates. and I'll come back.

@asmorkalov
Copy link
Copy Markdown
Contributor

@joshdoe I discussed the situation with @alalek , @vpisarev and core team members and we propose
to ntroduce Writer_open_with_params function in addition to existing open call. The call should not include is_color parameter, but get int* array with parameters and size_t parameter for array size. The array should just propagate VideoWriterParameters content. is_color flag and depth option is passed through this array. It's scalable solution that we can re-use in other back-ends too.

@joshdoe
Copy link
Copy Markdown
Contributor Author

joshdoe commented Nov 30, 2020

@asmorkalov trying to do this now. There's no state for the OpenCV_VideoIO_Plugin_API_preview struct, so do I also add a Writer_write2 to accept a depth, or do something different? Maybe add state, need to be careful about ABI though...

@joshdoe joshdoe force-pushed the gray16_gstreamer_writing branch from fff866d to 7378aa0 Compare December 2, 2020 13:21
@joshdoe
Copy link
Copy Markdown
Contributor Author

joshdoe commented Dec 2, 2020

@asmorkalov I've taken a stab at this, seems to work on my end, let's see what the buildbot thinks.

@joshdoe joshdoe force-pushed the gray16_gstreamer_writing branch from 7378aa0 to 7468782 Compare December 2, 2020 15:40
@joshdoe
Copy link
Copy Markdown
Contributor Author

joshdoe commented Dec 2, 2020

@asmorkalov Builds are passing now after I fixed some warnings in the FFMPEG backend. Do review my API changes, not sure if this is the best way to handle it or not.

Copy link
Copy Markdown
Contributor

@asmorkalov asmorkalov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good in general. Please address several local remarks.

// generate a noise frame
Size frame_size(320, 240);
Mat frame = Mat::zeros(frame_size, CV_16U);
cv::randu(frame, 0, 65535);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use theRNG(); as random generator to get reproducible results.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cv::randu() uses cv::theRNG() internally, so code is fine here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why the numbers have to be reproducible, as I'm simply comparing what I've generated to what I read back in. If this change request still stands, would I do it like this?

cv::theRNG().state = 123456;
cv::randu(frame, 0, 65535);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed.
theRNG() initial state is initialized by OpenCV test framework (using the same value) before calling of each test case body.

BTW, initial theRNG() state is configurable through command-line (however this mode is not used widely). Test data should not rely on its value.


// generate a noise frame
Size frame_size(320, 240);
Mat frame = Mat::zeros(frame_size, CV_16U);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mat::zeros() call is ambiguous as you fill the mat with random values.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mat::zeros() call is ambiguous as you fill the mat with random values.

Not correct.

zeros

fill with zeros values.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ambiguous isn't the right word to use, but since all the values get overwritten immediately, it is unnecessary to initialize to zero, even if it is a very fast operation. I've changed this to simply Mat(frame_size, CV_16U)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for my English, ambiguous is not the right word. I mean redundant.

Mat written_frame(frame_size, CV_16U);
std::ifstream fs(temp_file, std::ios::in | std::ios::binary);
fs.read((char*)written_frame.ptr(0), frame_size.width * frame_size.height * 2);
ASSERT_TRUE(fs);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be before read call.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this assertion ensures that .read() call is successful (it is really necessary).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did some tests, I could put an assertion both before and after .read(), however the second assertion will catch the same issue that the first assertion would, that is if a file is missing or can't be opened. I left this as is for now.

Comment on lines +515 to +517
std::vector<int> vint_params;
for (const auto& param : params.getAll())
{
vint_params.push_back(param.key);
vint_params.push_back(param.value);
}
int* c_params = &vint_params[0];
size_t n_params = vint_params.size() / 2;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose to extract this code as method of VideoWriterParameters class. E.g. return std::vector and pass it's content to C code as plain C array with size. We can potentially optimized the class behavior without plugins code modification.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I added getIntVector().

Comment on lines +124 to +128
// compare to make sure it's identical
Mat diff;
absdiff(frame, written_frame, diff);
Scalar total = sum(diff);
ASSERT_EQ(total[0], 0);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use this for "identity" checks:

EXPECT_EQ(0, cv::norm(frame, written_frame, NORM_INF));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@joshdoe joshdoe force-pushed the gray16_gstreamer_writing branch from 7468782 to 77d12cb Compare December 3, 2020 12:45
Copy link
Copy Markdown
Contributor

@asmorkalov asmorkalov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Please squash the commits and I think we can merge the patch.

@joshdoe
Copy link
Copy Markdown
Contributor Author

joshdoe commented Dec 3, 2020

Looks good to me. Please squash the commits and I think we can merge the patch.

@asmorkalov Squash all three? I thought it might be useful to keep the plugin API change as a separate one, maybe squash the two GStreamer-specific ones?

@asmorkalov
Copy link
Copy Markdown
Contributor

Yes, dedicated commit for API change is good idea. Thanks.

Copy link
Copy Markdown
Member

@alalek alalek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loading/using of existed FFmpeg plugin on Windows is broken:

[ INFO:0] global C:\build\precommit_windows64\opencv\modules\videoio\src\backend_plugin.cpp (211) cv::impl::PluginBackend::PluginBackend Video I/O: plugin is incompatible: C:\build\precommit_windows64\build\bin\Release\opencv_videoio_ffmpeg451_64.dll

...

[ RUN      ] videoio/videoio_ffmpeg.write_big/0, where GetParam() = ("", "avi", 4096x4096)
[     SKIP ] FFmpeg backend was not found
[       OK ] videoio/videoio_ffmpeg.write_big/0 (0 ms)

I will take a look how to fix this on this week.

This will allow arbitrary parameters to be passed to plugin backends
This introduces a new property VIDEOWRITER_PROP_DEPTH, which defaults to
CV_8U, but for GStreamer can be set to CV_16U.

Also, fix another test to not fail if plugin isn't found, copying logic
from the read_write test.
@joshdoe
Copy link
Copy Markdown
Contributor Author

joshdoe commented Dec 3, 2020

@alalek looks like any existing plugin will fail with the API_VERSION changing to 1. A backwards compatible change would require PluginBackend to probe for multiple values of API_VERSION, this is something naive I tried and works with a quick check:

for (int i = API_VERSION; i >= 0; --i)
{
    CV_LOG_INFO(NULL, "Video I/O: checking if plugin supports API_VERSION " << i);
    plugin_api_ = fn_init(ABI_VERSION, i, NULL);
    if (plugin_api_)
        break;
}

if (!plugin_api_)
{
    CV_LOG_INFO(NULL, "Video I/O: plugin is incompatible: " << lib->getName());
    return;
}

I also squashed the two GStreamer-specific commits.

@joshdoe joshdoe force-pushed the gray16_gstreamer_writing branch from 77d12cb to ba4a822 Compare December 3, 2020 19:34
Copy link
Copy Markdown
Member

@alalek alalek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

Thank you for contribution!

@alalek alalek merged commit 541a09b into opencv:master Dec 5, 2020
@alalek alalek mentioned this pull request Apr 9, 2021
a-sajjad72 pushed a commit to a-sajjad72/opencv that referenced this pull request Mar 30, 2023
Add CV_16UC1/GRAY16_LE support to GStreamer backend for VideoWriter

* videoio(backend): add Writer_open_with_params to plugin API

This will allow arbitrary parameters to be passed to plugin backends

* videoio(gstreamer): add GRAY16_LE/CV_16UC1 writing support to GStreamer

This introduces a new property VIDEOWRITER_PROP_DEPTH, which defaults to
CV_8U, but for GStreamer can be set to CV_16U.

Also, fix another test to not fail if plugin isn't found, copying logic
from the read_write test.

* videoio(plugin): fix handling plugins with previous API level

* videoio: coding style

* fix warning
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants