diff --git a/modules/videoio/src/cap_images.cpp b/modules/videoio/src/cap_images.cpp index 0e3c8f2ac2..76d2574ab8 100644 --- a/modules/videoio/src/cap_images.cpp +++ b/modules/videoio/src/cap_images.cpp @@ -50,24 +50,23 @@ // #include "precomp.hpp" -#include -#ifdef NDEBUG +#include "opencv2/core/utils/filesystem.hpp" + +#if 0 #define CV_WARN(message) #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 -#ifndef _MAX_PATH -#define _MAX_PATH 1024 -#endif +using namespace cv; +namespace cv { class CvCapture_Images : public CvCapture { public: CvCapture_Images() { - filename = NULL; currentframe = firstframe = 0; length = 0; frame = NULL; @@ -88,7 +87,7 @@ public: int getCaptureDomain() /*const*/ CV_OVERRIDE { return cv::CAP_IMAGES; } protected: - char* filename; // actually a printf-pattern + std::string filename_pattern; // actually a printf-pattern unsigned currentframe; unsigned firstframe; // number of first frame unsigned length; // length of sequence @@ -100,21 +99,16 @@ protected: void CvCapture_Images::close() { - if( filename ) - { - free(filename); - filename = NULL; - } currentframe = firstframe = 0; length = 0; - cvReleaseImage( &frame ); + cvReleaseImage(&frame); } bool CvCapture_Images::grabFrame() { - char str[_MAX_PATH]; - sprintf(str, filename, firstframe + currentframe); + cv::String filename = cv::format(filename_pattern.c_str(), (int)(firstframe + currentframe)); + CV_Assert(!filename.empty()); if (grabbedInOpen) { @@ -125,8 +119,8 @@ bool CvCapture_Images::grabFrame() } cvReleaseImage(&frame); - frame = cvLoadImage(str, CV_LOAD_IMAGE_UNCHANGED); - if( frame ) + frame = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_UNCHANGED); + if (frame) currentframe++; return frame != NULL; @@ -142,7 +136,7 @@ double CvCapture_Images::getProperty(int id) const switch(id) { 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; case CV_CAP_PROP_POS_FRAMES: return currentframe; @@ -155,10 +149,10 @@ double CvCapture_Images::getProperty(int id) const case CV_CAP_PROP_FRAME_HEIGHT: return frame ? frame->height : 0; 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; 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; @@ -171,11 +165,11 @@ bool CvCapture_Images::setProperty(int id, double value) case CV_CAP_PROP_POS_MSEC: case CV_CAP_PROP_POS_FRAMES: 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; } if(value >= length) { - CV_WARN("seeking beyond end of sequence - clamping\n"); + CV_WARN("seeking beyond end of sequence - clamping"); value = length - 1; } currentframe = cvRound(value); @@ -184,10 +178,10 @@ bool CvCapture_Images::setProperty(int id, double value) return true; case CV_CAP_PROP_POS_AVI_RATIO: if(value > 1) { - CV_WARN("seeking beyond end of sequence - clamping\n"); + CV_WARN("seeking beyond end of sequence - clamping"); value = 1; } 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; } 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 return true; } - CV_WARN("unknown/unhandled property\n"); + CV_WARN("unknown/unhandled property"); 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 ) - return 0; + *offset = 0; // check whether this is a valid image sequence filename - char *at = strchr(name, '%'); - if(at) + std::string::size_type pos = filename.find('%'); + if (pos != std::string::npos) { - unsigned int dummy; - if(sscanf(at + 1, "%ud", &dummy) != 1) - return 0; - name = strdup(filename); + pos++; CV_Assert(pos < len); + if (filename[pos] == '0') // optional zero prefix + { + 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 { - at = name; - - // ignore directory names - char *slash = strrchr(at, '/'); - if (slash) at = slash + 1; - + pos = filename.rfind('/'); #ifdef _WIN32 - slash = strrchr(at, '\\'); - if (slash) at = slash + 1; + if (pos == std::string::npos) + pos = filename.rfind('\\'); #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; + if (pos == len) + { + CV_Error_(Error::StsBadArg, ("CAP_IMAGES: can't find starting number (in the name of file): %s", filename.c_str())); + } - sscanf(at, "%u", offset); + std::string::size_type pos0 = pos; - int size = (int)strlen(filename) + 20; - name = (char *)malloc(size); - CV_Assert(name != NULL); - strncpy(name, filename, at - filename); - name[at - filename] = 0; + const int64_t max_number = 1000000000; + CV_Assert(max_number < INT_MAX); // offset is 'int' - strcat(name, "%0"); + int number_str_size = 0; + 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); - int i; - char *extension; - for(i = 0, extension = at; isdigit(at[i]); i++, extension++) - ; - char places[13] = {0}; - sprintf(places, "%dd", i); + *offset = (int)number; - strcat(name, places); - strcat(name, extension); + 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; close(); - filename = icvExtractPattern(_filename, &offset); - if(!filename) - return false; + CV_Assert(_filename); + filename_pattern = icvExtractPattern(_filename, &offset); + CV_Assert(!filename_pattern.empty()); // determine the length of the sequence - length = 0; - char str[_MAX_PATH]; - for(;;) + for (length = 0; ;) { - sprintf(str, filename, offset + length); - struct stat s; - if(stat(str, &s)) + 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) // allow starting with 0 or 1 { offset++; 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; + } length++; } - if(length == 0) + if (length == 0) { close(); return false; @@ -310,10 +331,18 @@ CvCapture* cvCreateFileCapture_Images(const char * filename) { CvCapture_Images* capture = new CvCapture_Images; - if( capture->open(filename) ) - return capture; + try + { + if (capture->open(filename)) + return capture; + delete capture; + } + catch (...) + { + delete capture; + throw; + } - delete capture; return NULL; } @@ -327,7 +356,6 @@ class CvVideoWriter_Images CV_FINAL : public CvVideoWriter public: CvVideoWriter_Images() { - filename = 0; currentframe = 0; } virtual ~CvVideoWriter_Images() { close(); } @@ -339,19 +367,21 @@ public: int getCaptureDomain() const CV_OVERRIDE { return cv::CAP_IMAGES; } protected: - char* filename; + std::string filename_pattern; unsigned currentframe; std::vector params; }; bool CvVideoWriter_Images::writeFrame( const IplImage* image ) { - char str[_MAX_PATH]; - sprintf(str, filename, currentframe); + CV_Assert(!filename_pattern.empty()); + cv::String filename = cv::format(filename_pattern.c_str(), (int)currentframe); + CV_Assert(!filename.empty()); + std::vector image_params = params; image_params.push_back(0); // append parameters 'stop' mark image_params.push_back(0); - int ret = cvSaveImage(str, image, &image_params[0]); + int ret = cvSaveImage(filename.c_str(), image, &image_params[0]); currentframe++; @@ -360,11 +390,6 @@ bool CvVideoWriter_Images::writeFrame( const IplImage* image ) void CvVideoWriter_Images::close() { - if( filename ) - { - free( filename ); - filename = 0; - } currentframe = 0; params.clear(); } @@ -373,16 +398,14 @@ void CvVideoWriter_Images::close() bool CvVideoWriter_Images::open( const char* _filename ) { unsigned offset = 0; - close(); - filename = icvExtractPattern(_filename, &offset); - if(!filename) - return false; + CV_Assert(_filename); + filename_pattern = icvExtractPattern(_filename, &offset); + CV_Assert(!filename_pattern.empty()); - char str[_MAX_PATH]; - sprintf(str, filename, 0); - if(!cvHaveImageWriter(str)) + cv::String filename = cv::format(filename_pattern.c_str(), (int)currentframe); + if (!cvHaveImageWriter(filename.c_str())) { close(); return false; @@ -410,9 +433,20 @@ CvVideoWriter* cvCreateVideoWriter_Images( const char* filename ) { CvVideoWriter_Images *writer = new CvVideoWriter_Images; - if( writer->open( filename )) - return writer; + try + { + if (writer->open(filename)) + return writer; + delete writer; + } + catch (...) + { + delete writer; + throw; + } - delete writer; return 0; } + + +} // namespace diff --git a/modules/videoio/src/precomp.hpp b/modules/videoio/src/precomp.hpp index 06f7e4a6f7..ed5fcd61a9 100644 --- a/modules/videoio/src/precomp.hpp +++ b/modules/videoio/src/precomp.hpp @@ -130,8 +130,10 @@ CvCapture* cvCreateCameraCapture_XIMEA( const char* serialNumber ); CvCapture* cvCreateCameraCapture_AVFoundation(int index); CvCapture* cvCreateCameraCapture_Aravis( int index ); +namespace cv { CvCapture* cvCreateFileCapture_Images(const char* filename); CvVideoWriter* cvCreateVideoWriter_Images(const char* filename); +} #define CV_CAP_GSTREAMER_1394 0