bc5b1878c0b1b9fd4205c39abf3bc70fb51a67b2
[experiments/opencv_trail_effect.git] / opencv_trail_effect.cpp
1 /*
2  * openvc_trail_effect - experiments about video trail effects
3  *
4  * Copyright (C) 2015  Antonio Ospite <ao2@ao2.it>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <opencv2/opencv.hpp>
21 #include <unistd.h>
22
23 #include "Frame.hpp"
24 #include "Segmentation.hpp"
25 #include "Trail.hpp"
26
27 static void usage(const char *name)
28 {
29         std::cout << "usage: " << name << " [OPTIONS]" << std::endl;
30         std::cout << "OPTIONS:" << std::endl;
31         std::cout << "\t-i <file>\tthe input file (if missing, a webcam will be tried)" << std::endl;
32         std::cout << "\t-o <file>\tthe optional output file" << std::endl;
33         std::cout << "\t-l <number>\tthe trail length in frames" << std::endl;
34         std::cout << "\t\t\tthe default is 25" << std::endl;
35         std::cout << "\t\t\tNOTES:" << std::endl;
36         std::cout << "\t\t\t  * a negative value means an 'infinite' trail" << std::endl;
37         std::cout << "\t-s <method>\tthe image segmentation method" << std::endl;
38         std::cout << "\t\t\tvalid values are:" << std::endl;
39         std::cout << "\t\t\t none, threshold, background" << std::endl;
40         std::cout << "\t\t\tthe default is 'background'" << std::endl;
41         std::cout << "\t\t\tNOTES:" << std::endl;
42         std::cout << "\t\t\t  * 'none' is only useful with '-d average'" << std::endl;
43         std::cout << "\t-b <number>\tthe number of initial frames for background learning," << std::endl;
44         std::cout << "\t\t\tthe default is 50" << std::endl;
45         std::cout << "\t\t\tNOTES:" << std::endl;
46         std::cout << "\t\t\t  * only useful with '-s background'" << std::endl;
47         std::cout << "\t-t <number>\tthe level for the threshold segmentation method," << std::endl;
48         std::cout << "\t\t\tthe default is 5" << std::endl;
49         std::cout << "\t\t\tNOTES:" << std::endl;
50         std::cout << "\t\t\t  * only useful with '-s threshold'" << std::endl;
51         std::cout << "\t-d <method>\tthe trail drawing method" << std::endl;
52         std::cout << "\t\t\tvalid values are:" << std::endl;
53         std::cout << "\t\t\t  copy, accumulate, fadecopy, fadeaccumulate, average" << std::endl;
54         std::cout << "\t\t\tthe default is 'copy'" << std::endl;
55         std::cout << "\t\t\tNOTES:" << std::endl;
56         std::cout << "\t\t\t  * 'copy' is useless with '-s none'" << std::endl;
57         std::cout << "\t\t\t  * the difference between 'fadecopy' and" << std::endl;
58         std::cout << "\t\t\t    'fadeaccumulate is cleared when using '-B'" << std::endl;
59         std::cout << "\t-r\t\treverse the trail drawing sequence" << std::endl;
60         std::cout << "\t-B\t\tshow the background behind the trail" << std::endl;
61         std::cout << "\t\t\tNOTES:" << std::endl;
62         std::cout << "\t\t\t  * only used with '-s background'" << std::endl;
63         std::cout << "\t-F\t\tredraw the current frame on top of the trail" << std::endl;
64         std::cout << "\t\t\tNOTES:" << std::endl;
65         std::cout << "\t\t\t  * noticeable with '-s average'" << std::endl;
66         std::cout << "\t\t\t  * noticeable with reverse faded trails" << std::endl;
67 }
68
69 int main(int argc, char *argv[])
70 {
71         int ret = 0;
72         int opt;
73
74         std::string input_file;
75         std::string output_file;
76         int trail_lenght = 25;
77         std::string segmentation_method("background");
78         int background_learn_frames = 50;
79         int threshold_level = 5;
80         std::string drawing_method("copy");
81         bool reverse_trail = false;
82         bool show_background = false;
83         bool redraw_current_frame = false;
84
85         while ((opt = getopt(argc, argv, "i:o:l:s:b:t:d:rBFh")) != -1) {
86                 switch (opt) {
87                 case 'i':
88                         input_file = std::string(optarg);
89                         break;
90                 case 'o':
91                         output_file = std::string(optarg);
92                         break;
93                 case 'l':
94                         trail_lenght = atoi(optarg);
95                         break;
96                 case 's':
97                         segmentation_method = std::string(optarg);
98                         break;
99                 case 'b':
100                         background_learn_frames = atoi(optarg);
101                         break;
102                 case 't':
103                         threshold_level = atoi(optarg);
104                         break;
105                 case 'd':
106                         drawing_method = std::string(optarg);
107                         break;
108                 case 'r':
109                         reverse_trail = true;
110                         break;
111                 case 'B':
112                         show_background = true;
113                         break;
114                 case 'F':
115                         redraw_current_frame = true;
116                         break;
117                 case 'h':
118                         usage(argv[0]);
119                         return 0;
120                 default: /* '?' */
121                         usage(argv[0]);
122                         return -1;
123                 }
124         }
125
126         cv::VideoCapture inputVideo;
127         cv::VideoWriter outputVideo;
128         cv::Size frame_size;
129         cv::Mat input_frame;
130
131         if (!input_file.empty()) {
132                 inputVideo.open(input_file);
133         } else {
134                 inputVideo.open(0);
135         }
136
137         if (!inputVideo.isOpened()) {
138                 std::cerr  << "Could not open the input video." << std::endl;
139                 ret = -1;
140                 goto out;
141         }
142
143         frame_size = cv::Size((int) inputVideo.get(cv::CAP_PROP_FRAME_WIDTH),
144                               (int) inputVideo.get(cv::CAP_PROP_FRAME_HEIGHT));
145
146         if (!output_file.empty()) {
147                 int fps = inputVideo.get(cv::CAP_PROP_FPS);
148                 if (fps < 0)
149                         fps = 25;
150
151                 outputVideo.open(output_file, cv::VideoWriter::fourcc('M','J','P','G'), fps, frame_size, true);
152                 if (!outputVideo.isOpened()) {
153                         std::cerr  << "Could not open the output video for write." << std::endl;
154                         ret = -1;
155                         goto out;
156                 }
157         }
158
159         Trail *trail;
160         if (reverse_trail)
161                 trail = new ReverseTrail(trail_lenght, frame_size);
162         else
163                 trail = new ForwardTrail(trail_lenght, frame_size);
164
165         trail->setRedrawCurrentFrame(redraw_current_frame);
166
167         if (trail->setDrawingMethod(drawing_method) < 0) {
168                 std::cerr  << "Invalid drawing method." << std::endl;
169                 ret = -1;
170                 goto out_delete_trail;
171         }
172
173         Segmentation *segmentation;
174         if (segmentation_method == "background") {
175                 segmentation = new MOG2Segmentation(inputVideo, background_learn_frames);
176                 if (show_background) {
177                         cv::Mat background(frame_size, inputVideo.get(cv::CAP_PROP_FORMAT));
178
179                         ((MOG2Segmentation *)segmentation)->getBackgroundImage(background);
180                         trail->setBackground(background);
181                 }
182         } else if (segmentation_method == "threshold") {
183                 segmentation = new ThresholdSegmentation(threshold_level);
184         } else if (segmentation_method == "none") {
185                 segmentation = new DummySegmentation();
186         } else {
187                 std::cerr  << "Invalid segmentation method." << std::endl;
188                 ret = -1;
189                 goto out_delete_trail;
190         }
191
192         cv::namedWindow("Frame", cv::WINDOW_NORMAL);
193
194         for (;;) {
195                 inputVideo >> input_frame;
196
197                 Frame *foreground = new Frame(input_frame,
198                                               segmentation->getForegroundMask(input_frame));
199                 trail->update(foreground);
200
201                 cv::Mat canvas = cv::Mat::zeros(input_frame.size(), input_frame.type());
202                 trail->draw(canvas);
203
204                 cv::imshow("Frame", canvas);
205                 if (cv::waitKeyEx(30) >= 0)
206                         break;
207
208                 if (outputVideo.isOpened())
209                         outputVideo << canvas;
210         }
211
212         cv::destroyWindow("Frame");
213
214         delete segmentation;
215
216 out_delete_trail:
217         delete trail;
218 out:
219         return ret;
220 }