1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                        Intel License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2008, Nils Hasler, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 //   * Redistribution's of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
22 //   * Redistribution's in binary form must reproduce the above copyright notice,
23 //     this list of conditions and the following disclaimer in the documentation
24 //     and/or other materials provided with the distribution.
25 //
26 //   * The name of Intel Corporation may not be used to endorse or promote products
27 //     derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41 
42 // Author: Nils Hasler <hasler@mpi-inf.mpg.de>
43 //
44 //         Max-Planck-Institut Informatik
45 
46 //
47 // capture video from a sequence of images
48 // the filename when opening can either be a printf pattern such as
49 // video%04d.png or the first frame of the sequence i.e. video0001.png
50 //
51 
52 #include "precomp.hpp"
53 #include "opencv2/imgcodecs.hpp"
54 
55 #include "opencv2/core/utils/filesystem.hpp"
56 
57 #if 0
58 #define CV_WARN(message)
59 #else
60 #define CV_WARN(message) CV_LOG_INFO(NULL, "CAP_IMAGES warning: %s (%s:%d)" << message)
61 #endif
62 
63 namespace cv {
64 
65 class CvCapture_Images: public IVideoCapture
66 {
67 public:
init()68     void init()
69     {
70         filename_pattern.clear();
71         frame.release();
72         currentframe = firstframe = 0;
73         length = 0;
74         grabbedInOpen = false;
75     }
CvCapture_Images()76     CvCapture_Images()
77     {
78         init();
79     }
CvCapture_Images(const String & _filename)80     CvCapture_Images(const String& _filename)
81     {
82         init();
83         open(_filename);
84     }
85 
~CvCapture_Images()86     virtual ~CvCapture_Images() CV_OVERRIDE
87     {
88         close();
89     }
90     virtual double getProperty(int) const CV_OVERRIDE;
91     virtual bool setProperty(int, double) CV_OVERRIDE;
92     virtual bool grabFrame() CV_OVERRIDE;
93     virtual bool retrieveFrame(int, OutputArray) CV_OVERRIDE;
94     virtual bool isOpened() const CV_OVERRIDE;
getCaptureDomain()95     virtual int getCaptureDomain() /*const*/ CV_OVERRIDE { return cv::CAP_IMAGES; }
96 
97     bool open(const String&);
98     void close();
99 protected:
100     std::string filename_pattern; // actually a printf-pattern
101     unsigned currentframe;
102     unsigned firstframe; // number of first frame
103     unsigned length; // length of sequence
104 
105     Mat frame;
106     bool grabbedInOpen;
107 };
108 
close()109 void CvCapture_Images::close()
110 {
111     init();
112 }
113 
grabFrame()114 bool CvCapture_Images::grabFrame()
115 {
116     cv::String filename = cv::format(filename_pattern.c_str(), (int)(firstframe + currentframe));
117     CV_Assert(!filename.empty());
118 
119     if (grabbedInOpen)
120     {
121         grabbedInOpen = false;
122         ++currentframe;
123 
124         return !frame.empty();
125     }
126 
127     frame = imread(filename, IMREAD_UNCHANGED);
128     if( !frame.empty() )
129         currentframe++;
130 
131     return !frame.empty();
132 }
133 
retrieveFrame(int,OutputArray out)134 bool CvCapture_Images::retrieveFrame(int, OutputArray out)
135 {
136     frame.copyTo(out);
137     return grabbedInOpen ? false : !frame.empty();
138 }
139 
140 
getProperty(int id) const141 double CvCapture_Images::getProperty(int id) const
142 {
143     switch(id)
144     {
145     case CV_CAP_PROP_POS_MSEC:
146         CV_WARN("collections of images don't have framerates");
147         return 0;
148     case CV_CAP_PROP_POS_FRAMES:
149         return currentframe;
150     case CV_CAP_PROP_FRAME_COUNT:
151         return length;
152     case CV_CAP_PROP_POS_AVI_RATIO:
153         return (double)currentframe / (double)(length - 1);
154     case CV_CAP_PROP_FRAME_WIDTH:
155         return frame.cols;
156     case CV_CAP_PROP_FRAME_HEIGHT:
157         return frame.rows;
158     case CV_CAP_PROP_FPS:
159         CV_WARN("collections of images don't have framerates");
160         return 1;
161     case CV_CAP_PROP_FOURCC:
162         CV_WARN("collections of images don't have 4-character codes");
163         return 0;
164     }
165     return 0;
166 }
167 
setProperty(int id,double value)168 bool CvCapture_Images::setProperty(int id, double value)
169 {
170     switch(id)
171     {
172     case CV_CAP_PROP_POS_MSEC:
173     case CV_CAP_PROP_POS_FRAMES:
174         if(value < 0) {
175             CV_WARN("seeking to negative positions does not work - clamping");
176             value = 0;
177         }
178         if(value >= length) {
179             CV_WARN("seeking beyond end of sequence - clamping");
180             value = length - 1;
181         }
182         currentframe = cvRound(value);
183         if (currentframe != 0)
184             grabbedInOpen = false; // grabbed frame is not valid anymore
185         return true;
186     case CV_CAP_PROP_POS_AVI_RATIO:
187         if(value > 1) {
188             CV_WARN("seeking beyond end of sequence - clamping");
189             value = 1;
190         } else if(value < 0) {
191             CV_WARN("seeking to negative positions does not work - clamping");
192             value = 0;
193         }
194         currentframe = cvRound((length - 1) * value);
195         if (currentframe != 0)
196             grabbedInOpen = false; // grabbed frame is not valid anymore
197         return true;
198     }
199     CV_WARN("unknown/unhandled property");
200     return false;
201 }
202 
203 static
icvExtractPattern(const std::string & filename,unsigned * offset)204 std::string icvExtractPattern(const std::string& filename, unsigned *offset)
205 {
206     size_t len = filename.size();
207     CV_Assert(!filename.empty());
208     CV_Assert(offset);
209 
210     *offset = 0;
211 
212     // check whether this is a valid image sequence filename
213     std::string::size_type pos = filename.find('%');
214     if (pos != std::string::npos)
215     {
216         pos++; CV_Assert(pos < len);
217         if (filename[pos] == '0') // optional zero prefix
218         {
219             pos++; CV_Assert(pos < len);
220         }
221         if (filename[pos] >= '1' && filename[pos] <= '9') // optional numeric size (1..9) (one symbol only)
222         {
223             pos++; CV_Assert(pos < len);
224         }
225         if (filename[pos] == 'd' || filename[pos] == 'u')
226         {
227             pos++;
228             if (pos == len)
229                 return filename;  // end of string '...%5d'
230             CV_Assert(pos < len);
231             if (filename.find('%', pos) == std::string::npos)
232                 return filename;  // no more patterns
233             CV_Error_(Error::StsBadArg, ("CAP_IMAGES: invalid multiple patterns: %s", filename.c_str()));
234         }
235         CV_Error_(Error::StsBadArg, ("CAP_IMAGES: error, expected '0?[1-9][du]' pattern, got: %s", filename.c_str()));
236     }
237     else // no pattern filename was given - extract the pattern
238     {
239         pos = filename.rfind('/');
240 #ifdef _WIN32
241         if (pos == std::string::npos)
242             pos = filename.rfind('\\');
243 #endif
244         if (pos != std::string::npos)
245             pos++;
246         else
247             pos = 0;
248 
249         while (pos < len && !isdigit(filename[pos])) pos++;
250 
251         if (pos == len)
252         {
253             CV_Error_(Error::StsBadArg, ("CAP_IMAGES: can't find starting number (in the name of file): %s", filename.c_str()));
254         }
255 
256         std::string::size_type pos0 = pos;
257 
258         const int64_t max_number = 1000000000;
259         CV_Assert(max_number < INT_MAX); // offset is 'int'
260 
261         int number_str_size = 0;
262         uint64_t number = 0;
263         while (pos < len && isdigit(filename[pos]))
264         {
265             char ch = filename[pos];
266             number = (number * 10) + (uint64_t)((int)ch - (int)'0');
267             CV_Assert(number < max_number);
268             number_str_size++;
269             CV_Assert(number_str_size <= 64);  // don't allow huge zero prefixes
270             pos++;
271         }
272         CV_Assert(number_str_size > 0);
273 
274         *offset = (int)number;
275 
276         std::string result;
277         if (pos0 > 0)
278             result += filename.substr(0, pos0);
279         result += cv::format("%%0%dd", number_str_size);
280         if (pos < len)
281             result += filename.substr(pos);
282         CV_LOG_INFO(NULL, "Pattern: " << result << " @ " << number);
283         return result;
284     }
285 }
286 
287 
open(const std::string & _filename)288 bool CvCapture_Images::open(const std::string& _filename)
289 {
290     unsigned offset = 0;
291     close();
292 
293     CV_Assert(!_filename.empty());
294     filename_pattern = icvExtractPattern(_filename, &offset);
295     CV_Assert(!filename_pattern.empty());
296 
297     // determine the length of the sequence
298     for (length = 0; ;)
299     {
300         cv::String filename = cv::format(filename_pattern.c_str(), (int)(offset + length));
301         if (!utils::fs::exists(filename))
302         {
303             if (length == 0 && offset == 0) // allow starting with 0 or 1
304             {
305                 offset++;
306                 continue;
307             }
308             break;
309         }
310 
311         if(!haveImageReader(filename))
312         {
313             CV_LOG_INFO(NULL, "CAP_IMAGES: Stop scanning. Can't read image file: " << filename);
314             break;
315         }
316 
317         length++;
318     }
319 
320     if (length == 0)
321     {
322         close();
323         return false;
324     }
325 
326     firstframe = offset;
327 
328     // grab frame to enable properties retrieval
329     bool grabRes = grabFrame();
330     grabbedInOpen = true;
331     currentframe = 0;
332 
333     return grabRes;
334 }
335 
isOpened() const336 bool CvCapture_Images::isOpened() const
337 {
338     return !filename_pattern.empty();
339 }
340 
create_Images_capture(const std::string & filename)341 Ptr<IVideoCapture> create_Images_capture(const std::string &filename)
342 {
343     return makePtr<CvCapture_Images>(filename);
344 }
345 
346 //
347 //
348 // image sequence writer
349 //
350 //
351 class CvVideoWriter_Images CV_FINAL : public CvVideoWriter
352 {
353 public:
CvVideoWriter_Images()354     CvVideoWriter_Images()
355     {
356         filename_pattern.clear();
357         currentframe = 0;
358     }
~CvVideoWriter_Images()359     virtual ~CvVideoWriter_Images() { close(); }
360 
361     virtual bool open( const char* _filename );
362     virtual void close();
363     virtual bool setProperty( int, double ); // FIXIT doesn't work: IVideoWriter interface only!
364     virtual bool writeFrame( const IplImage* ) CV_OVERRIDE;
365 
getCaptureDomain() const366     int getCaptureDomain() const CV_OVERRIDE { return cv::CAP_IMAGES; }
367 protected:
368     std::string filename_pattern;
369     unsigned currentframe;
370     std::vector<int> params;
371 };
372 
writeFrame(const IplImage * image)373 bool CvVideoWriter_Images::writeFrame( const IplImage* image )
374 {
375     CV_Assert(!filename_pattern.empty());
376     cv::String filename = cv::format(filename_pattern.c_str(), (int)currentframe);
377     CV_Assert(!filename.empty());
378 
379     std::vector<int> image_params = params;
380     image_params.push_back(0); // append parameters 'stop' mark
381     image_params.push_back(0);
382 
383     cv::Mat img = cv::cvarrToMat(image);
384     bool ret = cv::imwrite(filename, img, image_params);
385 
386     currentframe++;
387 
388     return ret;
389 }
390 
close()391 void CvVideoWriter_Images::close()
392 {
393     filename_pattern.clear();
394     currentframe = 0;
395     params.clear();
396 }
397 
398 
open(const char * _filename)399 bool CvVideoWriter_Images::open( const char* _filename )
400 {
401     unsigned offset = 0;
402     close();
403 
404     CV_Assert(_filename);
405     filename_pattern = icvExtractPattern(_filename, &offset);
406     CV_Assert(!filename_pattern.empty());
407 
408     cv::String filename = cv::format(filename_pattern.c_str(), (int)currentframe);
409     if (!cv::haveImageWriter(filename))
410     {
411         close();
412         return false;
413     }
414 
415     currentframe = offset;
416     params.clear();
417     return true;
418 }
419 
420 
setProperty(int id,double value)421 bool CvVideoWriter_Images::setProperty( int id, double value )
422 {
423     if (id >= cv::CAP_PROP_IMAGES_BASE && id < cv::CAP_PROP_IMAGES_LAST)
424     {
425         params.push_back( id - cv::CAP_PROP_IMAGES_BASE );
426         params.push_back( static_cast<int>( value ) );
427         return true;
428     }
429     return false; // not supported
430 }
431 
create_Images_writer(const std::string & filename,int,double,const Size &,const cv::VideoWriterParameters &)432 Ptr<IVideoWriter> create_Images_writer(const std::string &filename, int, double, const Size &,
433     const cv::VideoWriterParameters&)
434 {
435     CvVideoWriter_Images *writer = new CvVideoWriter_Images;
436 
437     try
438     {
439         if( writer->open( filename.c_str() ))
440             return makePtr<LegacyWriter>(writer);
441         delete writer;
442     }
443     catch (...)
444     {
445         delete writer;
446         throw;
447     }
448 
449     return 0;
450 }
451 
452 } // cv::
453