imgcodecs(CAP_IMAGES): fix handling of input pattern

pull/14623/head
Alexander Alekhin 6 years ago
parent 1810702bf0
commit 22701f0c27
  1. 232
      modules/videoio/src/cap_images.cpp
  2. 2
      modules/videoio/src/precomp.hpp

@ -50,24 +50,23 @@
// //
#include "precomp.hpp" #include "precomp.hpp"
#include <sys/stat.h>
#ifdef NDEBUG #include "opencv2/core/utils/filesystem.hpp"
#if 0
#define CV_WARN(message) #define CV_WARN(message)
#else #else
#define CV_WARN(message) fprintf(stderr, "warning: %s (%s:%d)\n", message, __FILE__, __LINE__) #define CV_WARN(message) CV_LOG_INFO(NULL, "CAP_IMAGES warning: %s (%s:%d)" << message)
#endif #endif
#ifndef _MAX_PATH using namespace cv;
#define _MAX_PATH 1024 namespace cv {
#endif
class CvCapture_Images : public CvCapture class CvCapture_Images : public CvCapture
{ {
public: public:
CvCapture_Images() CvCapture_Images()
{ {
filename = NULL;
currentframe = firstframe = 0; currentframe = firstframe = 0;
length = 0; length = 0;
frame = NULL; frame = NULL;
@ -88,7 +87,7 @@ public:
int getCaptureDomain() /*const*/ CV_OVERRIDE { return cv::CAP_IMAGES; } int getCaptureDomain() /*const*/ CV_OVERRIDE { return cv::CAP_IMAGES; }
protected: protected:
char* filename; // actually a printf-pattern std::string filename_pattern; // actually a printf-pattern
unsigned currentframe; unsigned currentframe;
unsigned firstframe; // number of first frame unsigned firstframe; // number of first frame
unsigned length; // length of sequence unsigned length; // length of sequence
@ -100,21 +99,16 @@ protected:
void CvCapture_Images::close() void CvCapture_Images::close()
{ {
if( filename )
{
free(filename);
filename = NULL;
}
currentframe = firstframe = 0; currentframe = firstframe = 0;
length = 0; length = 0;
cvReleaseImage( &frame ); cvReleaseImage(&frame);
} }
bool CvCapture_Images::grabFrame() bool CvCapture_Images::grabFrame()
{ {
char str[_MAX_PATH]; cv::String filename = cv::format(filename_pattern.c_str(), (int)(firstframe + currentframe));
sprintf(str, filename, firstframe + currentframe); CV_Assert(!filename.empty());
if (grabbedInOpen) if (grabbedInOpen)
{ {
@ -125,8 +119,8 @@ bool CvCapture_Images::grabFrame()
} }
cvReleaseImage(&frame); cvReleaseImage(&frame);
frame = cvLoadImage(str, CV_LOAD_IMAGE_UNCHANGED); frame = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_UNCHANGED);
if( frame ) if (frame)
currentframe++; currentframe++;
return frame != NULL; return frame != NULL;
@ -142,7 +136,7 @@ double CvCapture_Images::getProperty(int id) const
switch(id) switch(id)
{ {
case CV_CAP_PROP_POS_MSEC: case CV_CAP_PROP_POS_MSEC:
CV_WARN("collections of images don't have framerates\n"); CV_WARN("collections of images don't have framerates");
return 0; return 0;
case CV_CAP_PROP_POS_FRAMES: case CV_CAP_PROP_POS_FRAMES:
return currentframe; return currentframe;
@ -155,10 +149,10 @@ double CvCapture_Images::getProperty(int id) const
case CV_CAP_PROP_FRAME_HEIGHT: case CV_CAP_PROP_FRAME_HEIGHT:
return frame ? frame->height : 0; return frame ? frame->height : 0;
case CV_CAP_PROP_FPS: case CV_CAP_PROP_FPS:
CV_WARN("collections of images don't have framerates\n"); CV_WARN("collections of images don't have framerates");
return 1; return 1;
case CV_CAP_PROP_FOURCC: case CV_CAP_PROP_FOURCC:
CV_WARN("collections of images don't have 4-character codes\n"); CV_WARN("collections of images don't have 4-character codes");
return 0; return 0;
} }
return 0; return 0;
@ -171,11 +165,11 @@ bool CvCapture_Images::setProperty(int id, double value)
case CV_CAP_PROP_POS_MSEC: case CV_CAP_PROP_POS_MSEC:
case CV_CAP_PROP_POS_FRAMES: case CV_CAP_PROP_POS_FRAMES:
if(value < 0) { if(value < 0) {
CV_WARN("seeking to negative positions does not work - clamping\n"); CV_WARN("seeking to negative positions does not work - clamping");
value = 0; value = 0;
} }
if(value >= length) { if(value >= length) {
CV_WARN("seeking beyond end of sequence - clamping\n"); CV_WARN("seeking beyond end of sequence - clamping");
value = length - 1; value = length - 1;
} }
currentframe = cvRound(value); currentframe = cvRound(value);
@ -184,10 +178,10 @@ bool CvCapture_Images::setProperty(int id, double value)
return true; return true;
case CV_CAP_PROP_POS_AVI_RATIO: case CV_CAP_PROP_POS_AVI_RATIO:
if(value > 1) { if(value > 1) {
CV_WARN("seeking beyond end of sequence - clamping\n"); CV_WARN("seeking beyond end of sequence - clamping");
value = 1; value = 1;
} else if(value < 0) { } else if(value < 0) {
CV_WARN("seeking to negative positions does not work - clamping\n"); CV_WARN("seeking to negative positions does not work - clamping");
value = 0; value = 0;
} }
currentframe = cvRound((length - 1) * value); currentframe = cvRound((length - 1) * value);
@ -195,66 +189,92 @@ bool CvCapture_Images::setProperty(int id, double value)
grabbedInOpen = false; // grabbed frame is not valid anymore grabbedInOpen = false; // grabbed frame is not valid anymore
return true; return true;
} }
CV_WARN("unknown/unhandled property\n"); CV_WARN("unknown/unhandled property");
return false; return false;
} }
static char* icvExtractPattern(const char *filename, unsigned *offset) static
std::string icvExtractPattern(const std::string& filename, unsigned *offset)
{ {
char *name = (char *)filename; size_t len = filename.size();
CV_Assert(!filename.empty());
CV_Assert(offset);
if( !filename ) *offset = 0;
return 0;
// check whether this is a valid image sequence filename // check whether this is a valid image sequence filename
char *at = strchr(name, '%'); std::string::size_type pos = filename.find('%');
if(at) if (pos != std::string::npos)
{ {
unsigned int dummy; pos++; CV_Assert(pos < len);
if(sscanf(at + 1, "%ud", &dummy) != 1) if (filename[pos] == '0') // optional zero prefix
return 0; {
name = strdup(filename); pos++; CV_Assert(pos < len);
}
if (filename[pos] >= '1' && filename[pos] <= '9') // optional numeric size (1..9) (one symbol only)
{
pos++; CV_Assert(pos < len);
}
if (filename[pos] == 'd' || filename[pos] == 'u')
{
pos++;
if (pos == len)
return filename; // end of string '...%5d'
CV_Assert(pos < len);
if (filename.find('%', pos) == std::string::npos)
return filename; // no more patterns
CV_Error_(Error::StsBadArg, ("CAP_IMAGES: invalid multiple patterns: %s", filename.c_str()));
}
CV_Error_(Error::StsBadArg, ("CAP_IMAGES: error, expected '0?[1-9][du]' pattern, got: %s", filename.c_str()));
} }
else // no pattern filename was given - extract the pattern else // no pattern filename was given - extract the pattern
{ {
at = name; pos = filename.rfind('/');
// ignore directory names
char *slash = strrchr(at, '/');
if (slash) at = slash + 1;
#ifdef _WIN32 #ifdef _WIN32
slash = strrchr(at, '\\'); if (pos == std::string::npos)
if (slash) at = slash + 1; pos = filename.rfind('\\');
#endif #endif
if (pos != std::string::npos)
pos++;
else
pos = 0;
while (*at && !isdigit(*at)) at++; while (pos < len && !isdigit(filename[pos])) pos++;
if(!*at)
return 0;
sscanf(at, "%u", offset);
int size = (int)strlen(filename) + 20; if (pos == len)
name = (char *)malloc(size); {
CV_Assert(name != NULL); CV_Error_(Error::StsBadArg, ("CAP_IMAGES: can't find starting number (in the name of file): %s", filename.c_str()));
strncpy(name, filename, at - filename); }
name[at - filename] = 0;
strcat(name, "%0"); std::string::size_type pos0 = pos;
int i; const int64_t max_number = 1000000000;
char *extension; CV_Assert(max_number < INT_MAX); // offset is 'int'
for(i = 0, extension = at; isdigit(at[i]); i++, extension++)
;
char places[13] = {0};
sprintf(places, "%dd", i);
strcat(name, places); int number_str_size = 0;
strcat(name, extension); uint64_t number = 0;
while (pos < len && isdigit(filename[pos]))
{
char ch = filename[pos];
number = (number * 10) + (uint64_t)((int)ch - (int)'0');
CV_Assert(number < max_number);
number_str_size++;
CV_Assert(number_str_size <= 64); // don't allow huge zero prefixes
pos++;
}
CV_Assert(number_str_size > 0);
*offset = (int)number;
std::string result;
if (pos0 > 0)
result += filename.substr(0, pos0);
result += cv::format("%%0%dd", number_str_size);
if (pos < len)
result += filename.substr(pos);
CV_LOG_INFO(NULL, "Pattern: " << result << " @ " << number);
return result;
} }
return name;
} }
@ -263,33 +283,34 @@ bool CvCapture_Images::open(const char * _filename)
unsigned offset = 0; unsigned offset = 0;
close(); close();
filename = icvExtractPattern(_filename, &offset); CV_Assert(_filename);
if(!filename) filename_pattern = icvExtractPattern(_filename, &offset);
return false; CV_Assert(!filename_pattern.empty());
// determine the length of the sequence // determine the length of the sequence
length = 0; for (length = 0; ;)
char str[_MAX_PATH];
for(;;)
{ {
sprintf(str, filename, offset + length); cv::String filename = cv::format(filename_pattern.c_str(), (int)(offset + length));
struct stat s; if (!utils::fs::exists(filename))
if(stat(str, &s))
{ {
if(length == 0 && offset == 0) // allow starting with 0 or 1 if (length == 0 && offset == 0) // allow starting with 0 or 1
{ {
offset++; offset++;
continue; continue;
} }
break;
} }
if(!cvHaveImageReader(str)) if (!cvHaveImageReader(filename.c_str()))
{
CV_LOG_INFO(NULL, "CAP_IMAGES: Stop scanning. Can't read image file: " << filename);
break; break;
}
length++; length++;
} }
if(length == 0) if (length == 0)
{ {
close(); close();
return false; return false;
@ -310,10 +331,18 @@ CvCapture* cvCreateFileCapture_Images(const char * filename)
{ {
CvCapture_Images* capture = new CvCapture_Images; CvCapture_Images* capture = new CvCapture_Images;
if( capture->open(filename) ) try
{
if (capture->open(filename))
return capture; return capture;
delete capture; delete capture;
}
catch (...)
{
delete capture;
throw;
}
return NULL; return NULL;
} }
@ -327,7 +356,6 @@ class CvVideoWriter_Images CV_FINAL : public CvVideoWriter
public: public:
CvVideoWriter_Images() CvVideoWriter_Images()
{ {
filename = 0;
currentframe = 0; currentframe = 0;
} }
virtual ~CvVideoWriter_Images() { close(); } virtual ~CvVideoWriter_Images() { close(); }
@ -339,19 +367,21 @@ public:
int getCaptureDomain() const CV_OVERRIDE { return cv::CAP_IMAGES; } int getCaptureDomain() const CV_OVERRIDE { return cv::CAP_IMAGES; }
protected: protected:
char* filename; std::string filename_pattern;
unsigned currentframe; unsigned currentframe;
std::vector<int> params; std::vector<int> params;
}; };
bool CvVideoWriter_Images::writeFrame( const IplImage* image ) bool CvVideoWriter_Images::writeFrame( const IplImage* image )
{ {
char str[_MAX_PATH]; CV_Assert(!filename_pattern.empty());
sprintf(str, filename, currentframe); cv::String filename = cv::format(filename_pattern.c_str(), (int)currentframe);
CV_Assert(!filename.empty());
std::vector<int> image_params = params; std::vector<int> image_params = params;
image_params.push_back(0); // append parameters 'stop' mark image_params.push_back(0); // append parameters 'stop' mark
image_params.push_back(0); image_params.push_back(0);
int ret = cvSaveImage(str, image, &image_params[0]); int ret = cvSaveImage(filename.c_str(), image, &image_params[0]);
currentframe++; currentframe++;
@ -360,11 +390,6 @@ bool CvVideoWriter_Images::writeFrame( const IplImage* image )
void CvVideoWriter_Images::close() void CvVideoWriter_Images::close()
{ {
if( filename )
{
free( filename );
filename = 0;
}
currentframe = 0; currentframe = 0;
params.clear(); params.clear();
} }
@ -373,16 +398,14 @@ void CvVideoWriter_Images::close()
bool CvVideoWriter_Images::open( const char* _filename ) bool CvVideoWriter_Images::open( const char* _filename )
{ {
unsigned offset = 0; unsigned offset = 0;
close(); close();
filename = icvExtractPattern(_filename, &offset); CV_Assert(_filename);
if(!filename) filename_pattern = icvExtractPattern(_filename, &offset);
return false; CV_Assert(!filename_pattern.empty());
char str[_MAX_PATH]; cv::String filename = cv::format(filename_pattern.c_str(), (int)currentframe);
sprintf(str, filename, 0); if (!cvHaveImageWriter(filename.c_str()))
if(!cvHaveImageWriter(str))
{ {
close(); close();
return false; return false;
@ -410,9 +433,20 @@ CvVideoWriter* cvCreateVideoWriter_Images( const char* filename )
{ {
CvVideoWriter_Images *writer = new CvVideoWriter_Images; CvVideoWriter_Images *writer = new CvVideoWriter_Images;
if( writer->open( filename )) try
{
if (writer->open(filename))
return writer; return writer;
delete writer; delete writer;
}
catch (...)
{
delete writer;
throw;
}
return 0; return 0;
} }
} // namespace

@ -130,8 +130,10 @@ CvCapture* cvCreateCameraCapture_XIMEA( const char* serialNumber );
CvCapture* cvCreateCameraCapture_AVFoundation(int index); CvCapture* cvCreateCameraCapture_AVFoundation(int index);
CvCapture* cvCreateCameraCapture_Aravis( int index ); CvCapture* cvCreateCameraCapture_Aravis( int index );
namespace cv {
CvCapture* cvCreateFileCapture_Images(const char* filename); CvCapture* cvCreateFileCapture_Images(const char* filename);
CvVideoWriter* cvCreateVideoWriter_Images(const char* filename); CvVideoWriter* cvCreateVideoWriter_Images(const char* filename);
}
#define CV_CAP_GSTREAMER_1394 0 #define CV_CAP_GSTREAMER_1394 0

Loading…
Cancel
Save