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