mirror of
https://github.com/opencv/opencv.git
synced 2026-07-21 19:33:03 +04:00
Merge pull request #28844 from 0lekW:videoio-image-seq-start
Add CAP_PROP_IMAGE_SEQ_START #28844 Resolves https://github.com/opencv/opencv/issues/28843 New CAP_PROP_IMAGE_SEQ_START as an open only property for setting the first frame index when a VideoCap is opening with pattern, squashes test_images TODO. `CAP_FFMPEG`: forwarded to the image2 demuxers "start_number" option. `CAP_IMAGES`: Now a WithParams variant so the value reaches open() before probe runs, replaces 0 or 1 auto probe for sequence start. Property is opt in so default behavior is not affected. Please note that G_STREAMER is not considered here as I think its out of scope, it already has a "start-index" property and its backend is driven by the pipeline strings rather then these printf patterns. If an alternative solution is preferred that will not involve a new property or promoting CAP_IMAGES to a "WithParams" variant I don't mind implementing. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the 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. - [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
@@ -210,6 +210,7 @@ enum VideoCaptureProperties {
|
||||
CAP_PROP_N_THREADS = 70, //!< (**open-only**) Set the maximum number of threads to use. Use 0 to use as many threads as CPU cores (applicable for FFmpeg back-end only).
|
||||
CAP_PROP_PTS = 71, //!< (read-only) FFmpeg back-end only - presentation timestamp of the most recently read frame using the FPS time base. e.g. fps = 25, VideoCapture::get(\ref CAP_PROP_PTS) = 3, presentation time = 3/25 seconds.
|
||||
CAP_PROP_DTS_DELAY = 72, //!< (read-only) FFmpeg back-end only - maximum difference between presentation (pts) and decompression timestamps (dts) using FPS time base. e.g. delay is maximum when frame_num = 0, if true, VideoCapture::get(\ref CAP_PROP_PTS) = 0 and VideoCapture::get(\ref CAP_PROP_DTS_DELAY) = 2, dts = -2. Non zero values usually imply the stream is encoded using B-frames which are not decoded in presentation order.
|
||||
CAP_PROP_IMAGE_SEQ_START = 73, //!< (**open-only**) Start number for image sequences opened with a printf-style pattern (e.g. `frame_%05d.dpx`). Sets the initial frame number and disables automatic first-frame detection. Applicable to \ref CAP_FFMPEG (passed as the image2 demuxer `start_number`) and \ref CAP_IMAGES backends. Default: not set (automatic detection).
|
||||
#ifndef CV_DOXYGEN
|
||||
CV__CAP_PROP_LATEST
|
||||
#endif
|
||||
|
||||
@@ -624,6 +624,7 @@ struct CvCapture_FFMPEG
|
||||
int use_opencl;
|
||||
int extraDataIdx;
|
||||
int requestedThreads;
|
||||
int image_seq_start; // image2 demuxer start_number; -1 means unset
|
||||
};
|
||||
|
||||
void CvCapture_FFMPEG::init()
|
||||
@@ -679,6 +680,7 @@ void CvCapture_FFMPEG::init()
|
||||
use_opencl = 0;
|
||||
extraDataIdx = 1;
|
||||
requestedThreads = cv::getNumberOfCPUs();
|
||||
image_seq_start = -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1160,6 +1162,10 @@ bool CvCapture_FFMPEG::open(const char* _filename, int index, const Ptr<IStreamR
|
||||
{
|
||||
nThreads = requestedThreads = params.get<int>(CAP_PROP_N_THREADS);
|
||||
}
|
||||
if (params.has(CAP_PROP_IMAGE_SEQ_START))
|
||||
{
|
||||
image_seq_start = params.get<int>(CAP_PROP_IMAGE_SEQ_START);
|
||||
}
|
||||
if (params.warnUnusedParameters())
|
||||
{
|
||||
CV_LOG_ERROR(NULL, "VIDEOIO/FFMPEG: unsupported parameters in .open(), see logger INFO channel for details. Bailout");
|
||||
@@ -1194,6 +1200,10 @@ bool CvCapture_FFMPEG::open(const char* _filename, int index, const Ptr<IStreamR
|
||||
av_dict_set(&dict, "rtsp_transport", "tcp", 0);
|
||||
#endif
|
||||
}
|
||||
if (image_seq_start >= 0)
|
||||
{
|
||||
av_dict_set_int(&dict, "start_number", image_seq_start, 0);
|
||||
}
|
||||
CV_FFMPEG_FMT_CONST AVInputFormat* input_format = NULL;
|
||||
AVDictionaryEntry* entry = av_dict_get(dict, "input_format", NULL, 0);
|
||||
if (entry != 0)
|
||||
|
||||
@@ -77,11 +77,6 @@ public:
|
||||
{
|
||||
init();
|
||||
}
|
||||
CvCapture_Images(const String& _filename)
|
||||
{
|
||||
init();
|
||||
open(_filename);
|
||||
}
|
||||
|
||||
virtual ~CvCapture_Images() CV_OVERRIDE
|
||||
{
|
||||
@@ -94,7 +89,7 @@ public:
|
||||
virtual bool isOpened() const CV_OVERRIDE;
|
||||
virtual int getCaptureDomain() /*const*/ CV_OVERRIDE { return cv::CAP_IMAGES; }
|
||||
|
||||
bool open(const String&);
|
||||
bool open(const String&, int image_seq_start = -1);
|
||||
void close();
|
||||
protected:
|
||||
std::string filename_pattern; // actually a printf-pattern
|
||||
@@ -285,13 +280,16 @@ std::string icvExtractPattern(const std::string& filename, unsigned *offset)
|
||||
}
|
||||
|
||||
|
||||
bool CvCapture_Images::open(const std::string& _filename)
|
||||
bool CvCapture_Images::open(const std::string& _filename, int image_seq_start)
|
||||
{
|
||||
unsigned offset = 0;
|
||||
close();
|
||||
|
||||
CV_Assert(!_filename.empty());
|
||||
filename_pattern = icvExtractPattern(_filename, &offset);
|
||||
const bool explicit_start = (image_seq_start >= 0);
|
||||
if (explicit_start)
|
||||
offset = static_cast<unsigned>(image_seq_start);
|
||||
if (filename_pattern.empty())
|
||||
{
|
||||
filename_pattern = _filename;
|
||||
@@ -317,7 +315,7 @@ bool CvCapture_Images::open(const std::string& _filename)
|
||||
cv::String filename = cv::format(filename_pattern.c_str(), (int)(offset + length));
|
||||
if (!utils::fs::exists(filename))
|
||||
{
|
||||
if (length == 0 && offset == 0) // allow starting with 0 or 1
|
||||
if (length == 0 && offset == 0 && !explicit_start) // allow starting with 0 or 1
|
||||
{
|
||||
offset++;
|
||||
continue;
|
||||
@@ -355,9 +353,22 @@ bool CvCapture_Images::isOpened() const
|
||||
return !filename_pattern.empty();
|
||||
}
|
||||
|
||||
Ptr<IVideoCapture> create_Images_capture(const std::string &filename)
|
||||
Ptr<IVideoCapture> create_Images_capture(const std::string& filename, const VideoCaptureParameters& params)
|
||||
{
|
||||
return makePtr<CvCapture_Images>(filename);
|
||||
int image_seq_start = -1;
|
||||
if (params.has(CAP_PROP_IMAGE_SEQ_START))
|
||||
{
|
||||
image_seq_start = params.get<int>(CAP_PROP_IMAGE_SEQ_START);
|
||||
}
|
||||
if (params.warnUnusedParameters())
|
||||
{
|
||||
CV_LOG_ERROR(NULL, "VIDEOIO/IMAGES: unsupported parameters in .open(), see logger INFO channel for details. Bailout");
|
||||
return Ptr<IVideoCapture>();
|
||||
}
|
||||
Ptr<CvCapture_Images> cap = makePtr<CvCapture_Images>();
|
||||
if (!cap->open(filename, image_seq_start))
|
||||
return Ptr<IVideoCapture>();
|
||||
return cap;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -367,7 +367,7 @@ Ptr<IVideoCapture> create_V4L_capture_file(const std::string &filename);
|
||||
Ptr<IVideoCapture> create_OpenNI2_capture_cam( int index );
|
||||
Ptr<IVideoCapture> create_OpenNI2_capture_file( const std::string &filename );
|
||||
|
||||
Ptr<IVideoCapture> create_Images_capture(const std::string &filename);
|
||||
Ptr<IVideoCapture> create_Images_capture(const std::string& filename, const VideoCaptureParameters& params);
|
||||
Ptr<IVideoWriter> create_Images_writer(const std::string& filename, int fourcc,
|
||||
double fps, const Size& frameSize,
|
||||
const VideoWriterParameters& params);
|
||||
|
||||
@@ -300,10 +300,56 @@ TEST(videoio_images, bug_26457)
|
||||
EXPECT_MAT_N_DIFF(img, col.getFirstFrame(), 0);
|
||||
}
|
||||
|
||||
typedef testing::TestWithParam<VideoCaptureAPIs> videoio_image_seq_start;
|
||||
|
||||
TEST_P(videoio_image_seq_start, open)
|
||||
{
|
||||
const VideoCaptureAPIs apiPref = GetParam();
|
||||
if (!videoio_registry::hasBackend(apiPref))
|
||||
throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref));
|
||||
|
||||
// Sequence starts at an index outside the auto-probe range
|
||||
// Opening with only the pattern must fail without the property, and succeed when it is set
|
||||
const int start = 100;
|
||||
const size_t count = 5;
|
||||
ImageCollection col;
|
||||
col.generate(count, start);
|
||||
const std::string pattern = col.getPatternFilename();
|
||||
|
||||
{
|
||||
VideoCapture cap(pattern, apiPref);
|
||||
EXPECT_FALSE(cap.isOpened());
|
||||
}
|
||||
|
||||
{
|
||||
VideoCapture cap;
|
||||
ASSERT_TRUE(cap.open(pattern, apiPref,
|
||||
{ CAP_PROP_IMAGE_SEQ_START, start }));
|
||||
ASSERT_TRUE(cap.isOpened());
|
||||
for (size_t idx = 0; idx < count; ++idx)
|
||||
{
|
||||
Mat img;
|
||||
ASSERT_TRUE(cap.read(img));
|
||||
EXPECT_MAT_N_DIFF(img, col.getFrame(start + idx), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PR: https://github.com/opencv/opencv/pull/28844/
|
||||
// Requires FFmpeg wrapper re-build on Windows
|
||||
static const VideoCaptureAPIs BackendsWithSeqStart[] =
|
||||
{
|
||||
CAP_IMAGES
|
||||
#ifndef _WIN32
|
||||
,CAP_FFMPEG
|
||||
#endif
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(videoio_images, videoio_image_seq_start, testing::ValuesIn(BackendsWithSeqStart));
|
||||
|
||||
// TODO: should writer overwrite files?
|
||||
// TODO: is clamping good for seeking?
|
||||
// TODO: missing files? E.g. 3, 4, 6, 7, 8 (should it finish OR jump over OR return empty frame?)
|
||||
// TODO: non-numbered files (https://github.com/opencv/opencv/pull/23815)
|
||||
// TODO: when opening with pattern (e.g. test%01d.png), first frame can be only 0 (test0.png)
|
||||
|
||||
}} // opencv_test::<anonymous>::
|
||||
|
||||
Reference in New Issue
Block a user