1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-31 08:13:04 +04:00

Merge branch 4.x

This commit is contained in:
Alexander Smorkalov
2026-04-07 12:02:55 +03:00
28 changed files with 2027 additions and 118 deletions
+3 -1
View File
@@ -138,7 +138,7 @@ enum VideoCaptureProperties {
CAP_PROP_FOURCC =6, //!< 4-character code of codec. see VideoWriter::fourcc .
CAP_PROP_FRAME_COUNT =7, //!< Number of frames in the video file.
CAP_PROP_FORMAT =8, //!< Format of the %Mat objects (see Mat::type()) returned by VideoCapture::retrieve().
//!< Set value -1 to fetch undecoded RAW video streams (as Mat 8UC1).
//!< Set value -1 to fetch undecoded RAW video streams (as Mat 8UC1). Default is 8UC3. FFmpeg backend supports 8UC4 with alpha, if it's available.
CAP_PROP_MODE =9, //!< Backend-specific value indicating the current capture mode.
CAP_PROP_BRIGHTNESS =10, //!< Brightness of the image (only for those cameras that support).
CAP_PROP_CONTRAST =11, //!< Contrast of the image (only for cameras).
@@ -226,6 +226,8 @@ enum VideoWriterProperties {
VIDEOWRITER_PROP_KEY_FLAG = 11, //!< Set to non-zero to signal that the following frames are key frames or zero if not, when encapsulating raw video (\ref VIDEOWRITER_PROP_RAW_VIDEO != 0). FFmpeg back-end only.
VIDEOWRITER_PROP_PTS = 12, //!< Specifies the frame presentation timestamp for each frame using the FPS time base. This property is **only** necessary when encapsulating **externally** encoded video where the decoding order differs from the presentation order, such as in GOP patterns with bi-directional B-frames. The value should be provided by your external encoder and for video sources with fixed frame rates it is equivalent to dividing the current frame's presentation time (\ref CAP_PROP_POS_MSEC) by the frame duration (1000.0 / VideoCapture::get(\ref CAP_PROP_FPS)). It can be queried from the resulting encapsulated video file using VideoCapture::get(\ref CAP_PROP_PTS). FFmpeg back-end only.
VIDEOWRITER_PROP_DTS_DELAY = 13, //!< Specifies the maximum difference between presentation (pts) and decompression timestamps (dts) using the FPS time base. This property is necessary **only** when encapsulating **externally** encoded video where the decoding order differs from the presentation order, such as in GOP patterns with bi-directional B-frames. The value should be calculated based on the specific GOP pattern used during encoding. For example, in a GOP with presentation order IBP and decoding order IPB, this value would be 1, as the B-frame is the second frame presented but the third to be decoded. It can be queried from the resulting encapsulated video file using VideoCapture::get(\ref CAP_PROP_DTS_DELAY). Non-zero values usually imply the stream is encoded using B-frames. FFmpeg back-end only.
VIDEOWRITER_PROP_COLOR_SPACE = 14, //!< (**open-only**) GStreamer backend only. Pixel format for the encoding profile. Default is "I420". Other values: "NV12", "BGRx". See GStreamer raw video formats for more options.
VIDEOWRITER_PROP_ENABLE_ALPHA = 15, //!< (**open-only**) FFmpeg backend only. Defines that input frames contain alpha channel.
#ifndef CV_DOXYGEN
CV__VIDEOWRITER_PROP_LATEST
#endif
+49 -6
View File
@@ -612,6 +612,7 @@ struct CvCapture_FFMPEG
bool rawModeInitialized;
bool rawSeek;
bool convertRGB;
bool enableAlpha;
AVPacket packet_filtered;
#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(58, 20, 100)
AVBSFContext* bsfc;
@@ -669,6 +670,7 @@ void CvCapture_FFMPEG::init()
rawModeInitialized = false;
rawSeek = false;
convertRGB = true;
enableAlpha = false;
memset(&packet_filtered, 0, sizeof(packet_filtered));
av_init_packet(&packet_filtered);
bsfc = NULL;
@@ -1085,9 +1087,10 @@ bool CvCapture_FFMPEG::open(const char* _filename, int index, const Ptr<IStreamR
CV_LOG_WARNING(NULL, "VIDEOIO/FFMPEG: BGR conversion turned OFF, decoded frame will be "
"returned in its original format. "
"Multiplanar formats are not supported by the backend. "
"Only GRAY8/GRAY16LE pixel formats have been tested. "
"Only GRAY8/GRAY16LE/RGBA pixel formats have been tested. "
"Use at your own risk.");
}
if (params.has(CAP_PROP_FORMAT))
{
int value = params.get<int>(CAP_PROP_FORMAT);
@@ -1098,8 +1101,19 @@ bool CvCapture_FFMPEG::open(const char* _filename, int index, const Ptr<IStreamR
}
else
{
CV_LOG_ERROR(NULL, "VIDEOIO/FFMPEG: CAP_PROP_FORMAT parameter value is invalid/unsupported: " << value);
return false;
if (value == CV_8UC3)
{
enableAlpha = false;
}
if (value == CV_8UC4)
{
enableAlpha = true;
}
else
{
CV_LOG_ERROR(NULL, "VIDEOIO/FFMPEG: CAP_PROP_FORMAT parameter value is invalid/unsupported: " << value);
return false;
}
}
}
if(!rawMode) {
@@ -1879,9 +1893,12 @@ bool CvCapture_FFMPEG::retrieveFrame(int flag, unsigned char** data, int* step,
<< ", primaries: " << av_color_primaries_name(sw_picture->color_primaries)
<< ", transfer: " << av_color_transfer_name(sw_picture->color_trc)
);
const AVPixelFormat result_format = convertRGB ? AV_PIX_FMT_BGR24 : (AVPixelFormat)sw_picture->format;
const AVPixelFormat color_format = enableAlpha ? AV_PIX_FMT_BGRA : AV_PIX_FMT_BGR24;
const AVPixelFormat result_format = convertRGB ? color_format : (AVPixelFormat)sw_picture->format;
switch (result_format)
{
case AV_PIX_FMT_BGRA: *depth = CV_8U; *cn = 4; break;
case AV_PIX_FMT_BGR24: *depth = CV_8U; *cn = 3; break;
case AV_PIX_FMT_GRAY8: *depth = CV_8U; *cn = 1; break;
case AV_PIX_FMT_GRAY16LE: *depth = CV_16U; *cn = 1; break;
@@ -2122,6 +2139,12 @@ double CvCapture_FFMPEG::getProperty( int property_id ) const
case CAP_PROP_FORMAT:
if (rawMode)
return -1;
else if (!convertRGB)
return CV_8UC1;
else if (enableAlpha)
return CV_8UC4;
else
return CV_8UC3;
break;
case CAP_PROP_CONVERT_RGB:
return convertRGB;
@@ -2365,8 +2388,20 @@ bool CvCapture_FFMPEG::setProperty( int property_id, double value )
seek((int64_t)(value*ic->duration));
return true;
case CAP_PROP_FORMAT:
if (!convertRGB)
return false;
if (value == -1)
return setRaw();
else if (value == CV_8UC3)
{
enableAlpha = false;
return true;
}
else if (value == CV_8UC4)
{
enableAlpha = true;
return true;
}
return false;
case CAP_PROP_CONVERT_RGB:
convertRGB = (value != 0);
@@ -2775,6 +2810,12 @@ bool CvVideoWriter_FFMPEG::writeFrame( const unsigned char* data, int step, int
return false;
}
}
else if (input_pix_fmt == AV_PIX_FMT_BGRA) {
if (cn != 4) {
CV_LOG_WARNING(NULL, "write frame skipped - expected 4 channels but got " << cn);
return false;
}
}
else if (input_pix_fmt == AV_PIX_FMT_GRAY8 || input_pix_fmt == AV_PIX_FMT_GRAY16LE) {
if (cn != 1) {
CV_LOG_WARNING(NULL, "write frame skipped - expected 1 channel but got " << cn);
@@ -3151,6 +3192,8 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
use_opencl = params.get<int>(VIDEOWRITER_PROP_HW_ACCELERATION_USE_OPENCL);
}
bool enable_alpha = params.get<bool>(VIDEOWRITER_PROP_ENABLE_ALPHA, false);
if (params.warnUnusedParameters())
{
CV_LOG_ERROR(NULL, "VIDEOIO/FFMPEG: unsupported parameters in VideoWriter, see logger INFO channel for details");
@@ -3185,7 +3228,7 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
{
switch (depth)
{
case CV_8U: input_pix_fmt = AV_PIX_FMT_BGR24; break;
case CV_8U: input_pix_fmt = enable_alpha ? AV_PIX_FMT_BGRA : AV_PIX_FMT_BGR24; break;
default:
CV_LOG_WARNING(NULL, "Unsupported input depth for color image: " << depth);
return false;
@@ -3387,7 +3430,7 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
break;
default:
// good for lossy formats, MPEG, etc.
codec_pix_fmt = AV_PIX_FMT_YUV420P;
codec_pix_fmt = enable_alpha ? AV_PIX_FMT_YUVA420P : AV_PIX_FMT_YUV420P;
break;
}
+33 -14
View File
@@ -2512,25 +2512,43 @@ bool CvVideoWriter_GStreamer::open( const std::string &filename, int fourcc,
CV_WARN("OpenCV backend does not support this file type (extension): " << filename);
return false;
}
//create pipeline elements
encodebin.reset(gst_element_factory_make("encodebin", NULL));
if (!encodebin)
{
CV_WARN("GStreamer: cannot create encodebin element");
return false;
}
GSafePtr<GstCaps> containercaps;
GSafePtr<GstEncodingContainerProfile> containerprofile;
GSafePtr<GstEncodingVideoProfile> videoprofile;
containercaps.attach(gst_caps_from_string(mime));
//create encodebin profile
containerprofile.attach(gst_encoding_container_profile_new("container", "container", containercaps.get(), NULL));
videoprofile.reset(gst_encoding_video_profile_new(videocaps.get(), NULL, NULL, 1));
gst_encoding_container_profile_add_profile(containerprofile.get(), (GstEncodingProfile*)videoprofile.get());
unsigned int colorspace_fourcc = (unsigned int)params.get(VIDEOWRITER_PROP_COLOR_SPACE, CV_FOURCC('I', '4', '2', '0'));
const char* colorspace = gst_video_format_to_string(gst_video_format_from_fourcc(colorspace_fourcc));
GSafePtr<GstCaps> prof_caps;
std::string caps_str = std::string("video/x-raw, format=") + std::string(colorspace);
prof_caps.attach(gst_caps_from_string(caps_str.c_str()));
videoprofile.attach(gst_encoding_video_profile_new(prof_caps.get(), NULL, NULL, 1));
// Transfer ownership to the container profile
gst_encoding_container_profile_add_profile(
containerprofile.get(),
(GstEncodingProfile*)videoprofile.detach()
);
g_object_set(G_OBJECT(encodebin.get()), "profile", containerprofile.get(), NULL);
source.reset(gst_element_factory_make("appsrc", NULL));
if (!source)
{
CV_WARN("GStreamer: cannot create appsrc element");
return false;
}
file.reset(gst_element_factory_make("filesink", NULL));
if (!file)
{
CV_WARN("GStreamer: cannot create filesink element");
return false;
}
g_object_set(G_OBJECT(file.get()), "location", (const char*)filename.c_str(), NULL);
}
@@ -2709,21 +2727,22 @@ void CvVideoWriter_GStreamer::write(InputArray image)
Mat imageMat = image.getMat();
const size_t buf_size = imageMat.total() * imageMat.elemSize();
duration = ((double)1/framerate) * GST_SECOND;
timestamp = num_frames * duration;
duration = gst_util_uint64_scale_int(GST_SECOND, 1, framerate);
timestamp = gst_util_uint64_scale_int(num_frames, GST_SECOND, framerate);
//gst_app_src_push_buffer takes ownership of the buffer, so we need to supply it a copy
GstBuffer *buffer = gst_buffer_new_allocate(NULL, buf_size, NULL);
GstMapInfo info;
gst_buffer_map(buffer, &info, (GstMapFlags)GST_MAP_READ);
memcpy(info.data, (guint8*)imageMat.data, buf_size);
gst_buffer_unmap(buffer, &info);
if (gst_buffer_map(buffer, &info, (GstMapFlags)GST_MAP_WRITE)) {
memcpy(info.data, (guint8*)imageMat.data, buf_size);
gst_buffer_unmap(buffer, &info);
}
GST_BUFFER_DURATION(buffer) = duration;
GST_BUFFER_PTS(buffer) = timestamp;
GST_BUFFER_DTS(buffer) = timestamp;
//set the current number in the frame
GST_BUFFER_OFFSET(buffer) = num_frames;
GST_BUFFER_OFFSET_END(buffer) = num_frames + 1;
ret = gst_app_src_push_buffer(GST_APP_SRC(source.get()), buffer);
if (ret != GST_FLOW_OK)
{
+69
View File
@@ -1030,6 +1030,75 @@ inline static std::string videoio_ffmpeg_mismatch_name_printer(const testing::Te
INSTANTIATE_TEST_CASE_P(/**/, videoio_ffmpeg_channel_mismatch, testing::ValuesIn(mismatch_cases), videoio_ffmpeg_mismatch_name_printer);
#ifndef _WIN32
typedef tuple<string, string> AlphaChannelParams;
typedef testing::TestWithParam< AlphaChannelParams > videoio_ffmpeg_alpha_channel;
// New feature in https://github.com/opencv/opencv/pull/28751 requires FFmpeg wrapper rebuild on Windows
TEST_P(videoio_ffmpeg_alpha_channel, write_read)
{
if (!videoio_registry::hasBackend(CAP_FFMPEG))
throw SkipTestException("FFmpeg backend was not found");
const int fourcc = fourccFromString(get<0>(GetParam()));
const string filename = "video_with_alpha_channel." + get<1>(GetParam());
cv::VideoWriter writer(filename, cv::CAP_FFMPEG, fourcc, 1, Size(320, 240),
{VIDEOWRITER_PROP_IS_COLOR, 1,
VIDEOWRITER_PROP_ENABLE_ALPHA, 1});
ASSERT_TRUE(writer.isOpened());
for (int i = 0; i < 10; i ++)
{
cv::Mat frame;
cv::Mat gray_frame(240, 320, CV_8UC1, cv::Scalar::all(0));
gray_frame(Rect(i*10, i*10, i*10, i*10)).setTo(255);
cv::Mat channels[4] = {gray_frame, gray_frame, gray_frame, gray_frame};
cv::merge(channels, 4, frame);
writer.write(frame);
}
writer.release();
cv::VideoCapture cap(filename, cv::CAP_FFMPEG, {cv::CAP_PROP_FORMAT, CV_8UC4});
ASSERT_TRUE(cap.isOpened());
ASSERT_EQ(10, cap.get(cv::CAP_PROP_FRAME_COUNT));
for (int i = 0; i < 10; i++)
{
cv::Mat frame;
cap >> frame;
EXPECT_EQ(4, frame.channels());
EXPECT_EQ(320, frame.cols);
EXPECT_EQ(240, frame.rows);
EXPECT_EQ(0, frame.data[0]);
EXPECT_EQ(0, frame.data[1]);
EXPECT_EQ(0, frame.data[2]);
EXPECT_EQ(0, frame.data[3]);
cv::Mat channels[4];
cv::split(frame, channels);
int g_non_zero = cv::countNonZero(channels[1]);
int alpha_non_zero = cv::countNonZero(channels[3]);
EXPECT_EQ(g_non_zero, alpha_non_zero);
}
remove(filename.c_str());
}
AlphaChannelParams alpha_params[] =
{
make_tuple("FFV1", "mkv"),
make_tuple("FFV1", "avi")
// webm and hevc formats are disable as require fresh FFmpeg
//make_tuple("VP90", "webm")
//make_tuple("hevc", "mp4")
};
INSTANTIATE_TEST_CASE_P(/**/, videoio_ffmpeg_alpha_channel, testing::ValuesIn(alpha_params));
#endif
// PR: https://github.com/opencv/opencv/pull/27523
// TODO: Enable the tests back on Windows after FFmpeg plugin rebuild
#ifndef _WIN32