Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
3 | PointedEar | 1 | /*------------------------------------------------------------------------------------------*\ |
2 | This file contains material supporting chapter 10 of the cookbook: |
||
3 | Computer Vision Programming using the OpenCV Library. |
||
4 | by Robert Laganiere, Packt Publishing, 2011. |
||
5 | |||
6 | This program is free software; permission is hereby granted to use, copy, modify, |
||
7 | and distribute this source code, or portions thereof, for any purpose, without fee, |
||
8 | subject to the restriction that the copyright notice may not be removed |
||
9 | or altered from any source or altered source distribution. |
||
10 | The software is released on an as-is basis and without any warranties of any kind. |
||
11 | In particular, the software is not guaranteed to be fault-tolerant or free from failure. |
||
12 | The author disclaims all warranties with regard to this software, any use, |
||
13 | and any consequent failure, is purely the responsibility of the user. |
||
14 | |||
15 | Copyright (C) 2010-2011 Robert Laganiere, www.laganiere.name |
||
16 | \*------------------------------------------------------------------------------------------*/ |
||
17 | |||
18 | #if !defined VPROCESSOR |
||
19 | #define VPROCESSOR |
||
20 | |||
21 | #include <iostream> |
||
22 | #include <iomanip> |
||
23 | #include <sstream> |
||
24 | #include <string> |
||
25 | #include <vector> |
||
26 | #include <opencv2/core/core.hpp> |
||
27 | #include <opencv2/highgui/highgui.hpp> |
||
28 | |||
29 | // The frame processor interface |
||
30 | class FrameProcessor { |
||
31 | |||
32 | public: |
||
33 | // processing method |
||
34 | virtual void process(cv:: Mat &input, cv:: Mat &output)= 0; |
||
35 | }; |
||
36 | |||
37 | class VideoProcessor { |
||
38 | |||
39 | private: |
||
40 | |||
41 | // the OpenCV video capture object |
||
42 | cv::VideoCapture capture; |
||
43 | // the callback function to be called |
||
44 | // for the processing of each frame |
||
45 | void (*process)(cv::Mat&, cv::Mat&); |
||
46 | // the pointer to the class implementing |
||
47 | // the FrameProcessor interface |
||
48 | FrameProcessor *frameProcessor; |
||
49 | // a bool to determine if the |
||
50 | // process callback will be called |
||
51 | bool callIt; |
||
52 | // Input display window name |
||
53 | std::string windowNameInput; |
||
54 | // Output display window name |
||
55 | std::string windowNameOutput; |
||
56 | // delay between each frame processing |
||
57 | int delay; |
||
58 | // number of processed frames |
||
59 | long fnumber; |
||
60 | // stop at this frame number |
||
61 | long frameToStop; |
||
62 | // to stop the processing |
||
63 | bool stop; |
||
64 | |||
65 | // vector of image filename to be used as input |
||
66 | std::vector<std::string> images; |
||
67 | // image vector iterator |
||
68 | std::vector<std::string>::const_iterator itImg; |
||
69 | |||
70 | // the OpenCV video writer object |
||
71 | cv::VideoWriter writer; |
||
72 | // output filename |
||
73 | std::string outputFile; |
||
74 | |||
75 | // current index for output images |
||
76 | int currentIndex; |
||
77 | // number of digits in output image filename |
||
78 | int digits; |
||
79 | // extension of output images |
||
80 | std::string extension; |
||
81 | |||
82 | // to get the next frame |
||
83 | // could be: video file; camera; vector of images |
||
84 | bool readNextFrame(cv::Mat& frame) { |
||
85 | |||
86 | if (images.size()==0) |
||
87 | return capture.read(frame); |
||
88 | else { |
||
89 | |||
90 | if (itImg != images.end()) { |
||
91 | |||
92 | frame= cv::imread(*itImg); |
||
93 | itImg++; |
||
94 | return frame.data != 0; |
||
95 | } |
||
96 | } |
||
97 | } |
||
98 | |||
99 | // to write the output frame |
||
100 | // could be: video file or images |
||
101 | void writeNextFrame(cv::Mat& frame) { |
||
102 | |||
103 | if (extension.length()) { // then we write images |
||
104 | |||
105 | std::stringstream ss; |
||
106 | ss << outputFile << std::setfill('0') << std::setw(digits) << currentIndex++ << extension; |
||
107 | cv::imwrite(ss.str(),frame); |
||
108 | |||
109 | } else { // then write video file |
||
110 | |||
111 | writer.write(frame); |
||
112 | } |
||
113 | } |
||
114 | |||
115 | public: |
||
116 | |||
117 | // Constructor setting the default values |
||
118 | VideoProcessor() : callIt(false), delay(-1), |
||
119 | fnumber(0), stop(false), digits(0), frameToStop(-1), |
||
120 | process(0), frameProcessor(0) {} |
||
121 | |||
122 | // set the name of the video file |
||
123 | bool setInput(std::string filename) { |
||
124 | |||
125 | fnumber= 0; |
||
126 | // In case a resource was already |
||
127 | // associated with the VideoCapture instance |
||
128 | capture.release(); |
||
129 | images.clear(); |
||
130 | |||
131 | // Open the video file |
||
132 | return capture.open(filename); |
||
133 | } |
||
134 | |||
135 | // set the camera ID |
||
136 | bool setInput(int id) { |
||
137 | |||
138 | fnumber= 0; |
||
139 | // In case a resource was already |
||
140 | // associated with the VideoCapture instance |
||
141 | capture.release(); |
||
142 | images.clear(); |
||
143 | |||
144 | // Open the video file |
||
145 | return capture.open(id); |
||
146 | } |
||
147 | |||
148 | // set the vector of input images |
||
149 | bool setInput(const std::vector<std::string>& imgs) { |
||
150 | |||
151 | fnumber= 0; |
||
152 | // In case a resource was already |
||
153 | // associated with the VideoCapture instance |
||
154 | capture.release(); |
||
155 | |||
156 | // the input will be this vector of images |
||
157 | images= imgs; |
||
158 | itImg= images.begin(); |
||
159 | |||
160 | return true; |
||
161 | } |
||
162 | |||
163 | // set the output video file |
||
164 | // by default the same parameters than input video will be used |
||
165 | bool setOutput(const std::string &filename, int codec=0, double framerate=0.0, bool isColor=true) { |
||
166 | |||
167 | outputFile= filename; |
||
168 | extension.clear(); |
||
169 | |||
170 | if (framerate==0.0) |
||
171 | framerate= getFrameRate(); // same as input |
||
172 | |||
173 | char c[4]; |
||
174 | // use same codec as input |
||
175 | if (codec==0) { |
||
176 | codec= getCodec(c); |
||
177 | } |
||
178 | |||
179 | // Open output video |
||
180 | return writer.open(outputFile, // filename |
||
181 | codec, // codec to be used |
||
182 | framerate, // frame rate of the video |
||
183 | getFrameSize(), // frame size |
||
184 | isColor); // color video? |
||
185 | } |
||
186 | |||
187 | // set the output as a series of image files |
||
188 | // extension must be ".jpg", ".bmp" ... |
||
189 | bool setOutput(const std::string &filename, // filename prefix |
||
190 | const std::string &ext, // image file extension |
||
191 | int numberOfDigits=3, // number of digits |
||
192 | int startIndex=0) { // start index |
||
193 | |||
194 | // number of digits must be positive |
||
195 | if (numberOfDigits<0) |
||
196 | return false; |
||
197 | |||
198 | // filenames and their common extension |
||
199 | outputFile= filename; |
||
200 | extension= ext; |
||
201 | |||
202 | // number of digits in the file numbering scheme |
||
203 | digits= numberOfDigits; |
||
204 | // start numbering at this index |
||
205 | currentIndex= startIndex; |
||
206 | |||
207 | return true; |
||
208 | } |
||
209 | |||
210 | // set the callback function that will be called for each frame |
||
211 | void setFrameProcessor(void (*frameProcessingCallback)(cv::Mat&, cv::Mat&)) { |
||
212 | |||
213 | // invalidate frame processor class instance |
||
214 | frameProcessor= 0; |
||
215 | // this is the frame processor function that will be called |
||
216 | process= frameProcessingCallback; |
||
217 | callProcess(); |
||
218 | } |
||
219 | |||
220 | // set the instance of the class that implements the FrameProcessor interface |
||
221 | void setFrameProcessor(FrameProcessor* frameProcessorPtr) { |
||
222 | |||
223 | // invalidate callback function |
||
224 | process= 0; |
||
225 | // this is the frame processor instance that will be called |
||
226 | frameProcessor= frameProcessorPtr; |
||
227 | callProcess(); |
||
228 | } |
||
229 | |||
230 | // stop streaming at this frame number |
||
231 | void stopAtFrameNo(long frame) { |
||
232 | |||
233 | frameToStop= frame; |
||
234 | } |
||
235 | |||
236 | // process callback to be called |
||
237 | void callProcess() { |
||
238 | |||
239 | callIt= true; |
||
240 | } |
||
241 | |||
242 | // do not call process callback |
||
243 | void dontCallProcess() { |
||
244 | |||
245 | callIt= false; |
||
246 | } |
||
247 | |||
248 | // to display the processed frames |
||
249 | void displayInput(std::string wn) { |
||
250 | |||
251 | windowNameInput= wn; |
||
252 | cv::namedWindow(windowNameInput); |
||
253 | } |
||
254 | |||
255 | // to display the processed frames |
||
256 | void displayOutput(std::string wn) { |
||
257 | |||
258 | windowNameOutput= wn; |
||
259 | cv::namedWindow(windowNameOutput); |
||
260 | } |
||
261 | |||
262 | // do not display the processed frames |
||
263 | void dontDisplay() { |
||
264 | |||
265 | cv::destroyWindow(windowNameInput); |
||
266 | cv::destroyWindow(windowNameOutput); |
||
267 | windowNameInput.clear(); |
||
268 | windowNameOutput.clear(); |
||
269 | } |
||
270 | |||
271 | // set a delay between each frame |
||
272 | // 0 means wait at each frame |
||
273 | // negative means no delay |
||
274 | void setDelay(int d) { |
||
275 | |||
276 | delay= d; |
||
277 | } |
||
278 | |||
279 | // a count is kept of the processed frames |
||
280 | long getNumberOfProcessedFrames() { |
||
281 | |||
282 | return fnumber; |
||
283 | } |
||
284 | |||
285 | // return the size of the video frame |
||
286 | cv::Size getFrameSize() { |
||
287 | |||
288 | if (images.size()==0) { |
||
289 | |||
290 | // get size of from the capture device |
||
291 | int w= static_cast<int>(capture.get(CV_CAP_PROP_FRAME_WIDTH)); |
||
292 | int h= static_cast<int>(capture.get(CV_CAP_PROP_FRAME_HEIGHT)); |
||
293 | |||
294 | return cv::Size(w,h); |
||
295 | |||
296 | } else { // if input is vector of images |
||
297 | |||
298 | cv::Mat tmp= cv::imread(images[0]); |
||
299 | if (!tmp.data) return cv::Size(0,0); |
||
300 | else return tmp.size(); |
||
301 | } |
||
302 | } |
||
303 | |||
304 | // return the frame number of the next frame |
||
305 | long getFrameNumber() { |
||
306 | |||
307 | if (images.size()==0) { |
||
308 | |||
309 | // get info of from the capture device |
||
310 | long f= static_cast<long>(capture.get(CV_CAP_PROP_POS_FRAMES)); |
||
311 | return f; |
||
312 | |||
313 | } else { // if input is vector of images |
||
314 | |||
315 | return static_cast<long>(itImg-images.begin()); |
||
316 | } |
||
317 | } |
||
318 | |||
319 | // return the position in ms |
||
320 | double getPositionMS() { |
||
321 | |||
322 | // undefined for vector of images |
||
323 | if (images.size()!=0) return 0.0; |
||
324 | |||
325 | double t= capture.get(CV_CAP_PROP_POS_MSEC); |
||
326 | return t; |
||
327 | } |
||
328 | |||
329 | // return the frame rate |
||
330 | double getFrameRate() { |
||
331 | |||
332 | // undefined for vector of images |
||
333 | if (images.size()!=0) return 0; |
||
334 | |||
335 | double r= capture.get(CV_CAP_PROP_FPS); |
||
336 | return r; |
||
337 | } |
||
338 | |||
339 | // return the number of frames in video |
||
340 | long getTotalFrameCount() { |
||
341 | |||
342 | // for vector of images |
||
343 | if (images.size()!=0) return images.size(); |
||
344 | |||
345 | long t= capture.get(CV_CAP_PROP_FRAME_COUNT); |
||
346 | return t; |
||
347 | } |
||
348 | |||
349 | // get the codec of input video |
||
350 | int getCodec(char codec[4]) { |
||
351 | |||
352 | // undefined for vector of images |
||
353 | if (images.size()!=0) return -1; |
||
354 | |||
355 | union { |
||
356 | int value; |
||
357 | char code[4]; } returned; |
||
358 | |||
359 | returned.value= static_cast<int>(capture.get(CV_CAP_PROP_FOURCC)); |
||
360 | |||
361 | codec[0]= returned.code[0]; |
||
362 | codec[1]= returned.code[1]; |
||
363 | codec[2]= returned.code[2]; |
||
364 | codec[3]= returned.code[3]; |
||
365 | |||
366 | return returned.value; |
||
367 | } |
||
368 | |||
369 | // go to this frame number |
||
370 | bool setFrameNumber(long pos) { |
||
371 | |||
372 | // for vector of images |
||
373 | if (images.size()!=0) { |
||
374 | |||
375 | // move to position in vector |
||
376 | itImg= images.begin() + pos; |
||
377 | // is it a valid position? |
||
378 | if (pos < images.size()) |
||
379 | return true; |
||
380 | else |
||
381 | return false; |
||
382 | |||
383 | } else { // if input is a capture device |
||
384 | |||
385 | return capture.set(CV_CAP_PROP_POS_FRAMES, pos); |
||
386 | } |
||
387 | } |
||
388 | |||
389 | // go to this position |
||
390 | bool setPositionMS(double pos) { |
||
391 | |||
392 | // not defined in vector of images |
||
393 | if (images.size()!=0) |
||
394 | return false; |
||
395 | else |
||
396 | return capture.set(CV_CAP_PROP_POS_MSEC, pos); |
||
397 | } |
||
398 | |||
399 | // go to this position expressed in fraction of total film length |
||
400 | bool setRelativePosition(double pos) { |
||
401 | |||
402 | // for vector of images |
||
403 | if (images.size()!=0) { |
||
404 | |||
405 | // move to position in vector |
||
406 | long posI= static_cast<long>(pos*images.size()+0.5); |
||
407 | itImg= images.begin() + posI; |
||
408 | // is it a valid position? |
||
409 | if (posI < images.size()) |
||
410 | return true; |
||
411 | else |
||
412 | return false; |
||
413 | |||
414 | } else { // if input is a capture device |
||
415 | |||
416 | return capture.set(CV_CAP_PROP_POS_AVI_RATIO, pos); |
||
417 | } |
||
418 | } |
||
419 | |||
420 | // Stop the processing |
||
421 | void stopIt() { |
||
422 | |||
423 | stop= true; |
||
424 | } |
||
425 | |||
426 | // Is the process stopped? |
||
427 | bool isStopped() { |
||
428 | |||
429 | return stop; |
||
430 | } |
||
431 | |||
432 | // Is a capture device opened? |
||
433 | bool isOpened() { |
||
434 | |||
435 | return capture.isOpened() || !images.empty(); |
||
436 | } |
||
437 | |||
438 | // to grab (and process) the frames of the sequence |
||
439 | void run() { |
||
440 | |||
441 | // current frame |
||
442 | cv::Mat frame; |
||
443 | // output frame |
||
444 | cv::Mat output; |
||
445 | |||
446 | // if no capture device has been set |
||
447 | if (!isOpened()) |
||
448 | return; |
||
449 | |||
450 | stop= false; |
||
451 | |||
452 | while (!isStopped()) { |
||
453 | |||
454 | // read next frame if any |
||
455 | if (!readNextFrame(frame)) |
||
456 | break; |
||
457 | |||
458 | // display input frame |
||
459 | if (windowNameInput.length()!=0) |
||
460 | cv::imshow(windowNameInput,frame); |
||
461 | |||
462 | // calling the process function or method |
||
463 | if (callIt) { |
||
464 | |||
465 | // process the frame |
||
466 | if (process) |
||
467 | process(frame, output); |
||
468 | else if (frameProcessor) |
||
469 | frameProcessor->process(frame,output); |
||
470 | // increment frame number |
||
471 | fnumber++; |
||
472 | |||
473 | } else { |
||
474 | |||
475 | output= frame; |
||
476 | } |
||
477 | |||
478 | // write output sequence |
||
479 | if (outputFile.length()!=0) |
||
480 | writeNextFrame(output); |
||
481 | |||
482 | // display output frame |
||
483 | if (windowNameOutput.length()!=0) |
||
484 | cv::imshow(windowNameOutput,output); |
||
485 | |||
486 | // introduce a delay |
||
487 | if (delay>=0 && cv::waitKey(delay)>=0) |
||
488 | stopIt(); |
||
489 | |||
490 | // check if we should stop |
||
491 | if (frameToStop>=0 && getFrameNumber()==frameToStop) |
||
492 | stopIt(); |
||
493 | } |
||
494 | } |
||
495 | }; |
||
496 | |||
497 | #endif |