version update
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / timelapsehelper / timelapsehelper.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2021 Adam Williams <broadcast at earthling dot net>
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, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22
23 // output 1 frame for each block of frames
24 // take the most similar frame in the current block of frames to the previous frame
25 // this is for 3D printers where you want the bed in the same position
26
27
28 #include "bcdisplayinfo.h"
29 #include "clip.h"
30 #include "bchash.h"
31 #include "filexml.h"
32 #include "guicast.h"
33 #include "keyframe.h"
34 #include "language.h"
35 #include "transportque.inc"
36 #include "pluginvclient.h"
37 #include "theme.h"
38 #include "vframe.h"
39
40 #include <string.h>
41 #include <stdint.h>
42
43
44 class TimelapseHelper;
45 class TimelapseHelperWindow;
46
47
48 class TimelapseHelperConfig
49 {
50 public:
51         TimelapseHelperConfig();
52         void copy_from(TimelapseHelperConfig &config);
53         int equivalent(TimelapseHelperConfig &config);
54     void interpolate(TimelapseHelperConfig &prev, 
55             TimelapseHelperConfig &next, 
56             int64_t prev_frame, 
57             int64_t next_frame, 
58             int64_t current_frame);
59
60         int block_size;
61 };
62
63
64
65
66 class TimelapseHelperSize : public BC_TumbleTextBox
67 {
68 public:
69         TimelapseHelperSize(TimelapseHelper *plugin,
70                 TimelapseHelperWindow *gui, 
71                 int x,
72                 int y,
73         int w);
74         int handle_event();
75         TimelapseHelper *plugin;
76         TimelapseHelperWindow *gui;
77 };
78
79
80 class TimelapseHelperWindow : public PluginClientWindow
81 {
82 public:
83         TimelapseHelperWindow(TimelapseHelper *plugin);
84         ~TimelapseHelperWindow();
85
86         void create_objects();
87
88         TimelapseHelper *plugin;
89         TimelapseHelperSize *size;
90 };
91
92
93
94
95
96 class TimelapseHelper : public PluginVClient
97 {
98 public:
99         TimelapseHelper(PluginServer *server);
100         ~TimelapseHelper();
101
102         PLUGIN_CLASS_MEMBERS(TimelapseHelperConfig)
103
104         int process_buffer(VFrame *frame,
105                 int64_t start_position,
106                 double frame_rate);
107         int is_realtime();
108         void save_data(KeyFrame *keyframe);
109         void read_data(KeyFrame *keyframe);
110         void update_gui();
111
112         int64_t calculate_difference(VFrame *frame1, VFrame *frame2);
113
114
115 // frame used from the last block
116         VFrame *ref;
117 };
118
119
120
121
122
123
124
125
126
127
128
129
130 TimelapseHelperConfig::TimelapseHelperConfig()
131 {
132         block_size = 10;
133 }
134
135 void TimelapseHelperConfig::copy_from(TimelapseHelperConfig &config)
136 {
137         this->block_size = config.block_size;
138 }
139
140 int TimelapseHelperConfig::equivalent(TimelapseHelperConfig &config)
141 {
142         return this->block_size == config.block_size;
143 }
144
145 void TimelapseHelperConfig::interpolate(TimelapseHelperConfig &prev, 
146         TimelapseHelperConfig &next, 
147         int64_t prev_frame, 
148         int64_t next_frame, 
149         int64_t current_frame)
150 {
151         this->block_size = next.block_size;
152 }
153
154
155
156
157
158
159
160
161 TimelapseHelperWindow::TimelapseHelperWindow(TimelapseHelper *plugin)
162  : PluginClientWindow(plugin,
163         xS(230),
164         yS(60),
165         xS(230),
166         yS(60),
167         0)
168 {
169         this->plugin = plugin;
170 }
171
172 TimelapseHelperWindow::~TimelapseHelperWindow()
173 {
174 }
175
176 void TimelapseHelperWindow::create_objects()
177 {
178     int margin = client->get_theme()->widget_border;
179         int x = margin, y = margin;
180
181         BC_Title *title;
182         add_subwindow(title = new BC_Title(x, y, _("Number of frames per block:")));
183         y += title->get_h() + margin;
184         size = new TimelapseHelperSize(plugin,
185                 this,
186                 x,
187                 y,
188         get_w() - x - margin - xS(20) );
189     size->create_objects();
190         show_window();
191 }
192
193
194
195
196
197
198
199
200
201
202
203
204
205 TimelapseHelperSize::TimelapseHelperSize(TimelapseHelper *plugin,
206         TimelapseHelperWindow *gui, 
207         int x,
208         int y,
209     int w)
210  : BC_TumbleTextBox(gui,
211     plugin->config.block_size, 
212     1,
213     1000,
214     x,
215         y,
216         w)
217 {
218         this->plugin = plugin;
219         this->gui = gui;
220 }
221
222 int TimelapseHelperSize::handle_event()
223 {
224         plugin->config.block_size = atoi(get_text());
225         plugin->send_configure_change();
226         return 1;
227 }
228
229
230
231
232
233
234
235
236
237
238 REGISTER_PLUGIN(TimelapseHelper)
239
240
241
242
243
244
245 TimelapseHelper::TimelapseHelper(PluginServer *server)
246  : PluginVClient(server)
247 {
248         ref = 0;
249 }
250
251
252 TimelapseHelper::~TimelapseHelper()
253 {
254         if(ref)
255     {
256         delete ref;
257     }
258 }
259
260 #define DIFFERENCE_MACRO(type, temp_type, components) \
261 { \
262         temp_type result2 = 0; \
263         for(int i = 0; i < h; i++) \
264         { \
265                 type *row1 = (type*)frame1->get_rows()[i]; \
266                 type *row2 = (type*)frame2->get_rows()[i]; \
267                 for(int j = 0; j < w * components; j++) \
268                 { \
269                         temp_type temp = *row1 - *row2; \
270                         result2 += (temp > 0 ? temp : -temp); \
271                         row1++; \
272                         row2++; \
273                 } \
274         } \
275         result = (int64_t)result2; \
276 }
277
278 int64_t TimelapseHelper::calculate_difference(VFrame *frame1, VFrame *frame2)
279 {
280         int w = frame1->get_w();
281         int h = frame1->get_h();
282         int64_t result = 0;
283         switch(frame1->get_color_model())
284         {
285                 case BC_RGB888:
286                 case BC_YUV888:
287                         DIFFERENCE_MACRO(unsigned char, int64_t, 3);
288                         break;
289                 case BC_RGB_FLOAT:
290                         DIFFERENCE_MACRO(float, double, 3);
291                         break;
292                 case BC_RGBA8888:
293                 case BC_YUVA8888:
294                         DIFFERENCE_MACRO(unsigned char, int64_t, 4);
295                         break;
296                 case BC_RGBA_FLOAT:
297                         DIFFERENCE_MACRO(float, double, 4);
298                         break;
299                 case BC_RGB161616:
300                 case BC_YUV161616:
301                         DIFFERENCE_MACRO(uint16_t, int64_t, 3);
302                         break;
303                 case BC_RGBA16161616:
304                 case BC_YUVA16161616:
305                         DIFFERENCE_MACRO(uint16_t, int64_t, 4);
306                         break;
307         }
308         return result;
309 }
310
311
312
313 int TimelapseHelper::process_buffer(VFrame *frame,
314         int64_t start_position,
315         double frame_rate)
316 {
317         load_configuration();
318
319 // calculate frame positions
320     int64_t ref_position = get_source_start();
321     int64_t block_start;
322     int64_t block_end;
323     block_start = ref_position + (start_position - ref_position) * config.block_size;
324     block_end = block_start + config.block_size;
325
326 // printf("TimelapseHelper::process_buffer %d current_position=%ld ref_position=%ld block_start=%ld block_end=%ld\n",
327 // __LINE__, 
328 //start_position,
329 //ref_position,
330 //block_start,
331 //block_end);
332
333 // load initial reference frame from plugin start
334         if(!ref)
335         {
336                 ref = new VFrame(0,
337                         -1,
338                         frame->get_w(),
339                         frame->get_h(),
340                         frame->get_color_model(),
341                         -1);
342         read_frame(ref, 
343                         0, 
344                         ref_position, 
345                         frame_rate,
346                         0);
347         frame->copy_from(ref);
348         }
349     else
350 // compare next block of frames to reference frame
351     {
352         VFrame *temp = new_temp(frame->get_w(),
353                         frame->get_h(),
354                         frame->get_color_model());
355         int64_t best = 0x7fffffffffffffffLL;
356         for(int64_t i = block_start; i < block_end; i++)
357         {
358
359             if(get_direction() == PLAY_FORWARD)
360             {
361                 read_frame(temp, 
362                                 0, 
363                                 i, 
364                                 frame_rate,
365                                 0);
366             }
367             else
368             {
369                 read_frame(temp, 
370                                 0, 
371                                 i + 1, 
372                                 frame_rate,
373                                 0);
374             }
375             
376             int64_t diff = calculate_difference(temp, 
377                                         ref);
378             if(diff < best)
379             {
380                 best = diff;
381                 frame->copy_from(temp);
382             }
383         }
384
385 // replace reference frame with best match
386         ref->copy_from(frame);
387     }
388
389         return 0;
390 }
391
392
393
394 const char* TimelapseHelper::plugin_title() { return N_("Timelapse Helper"); }
395 int TimelapseHelper::is_realtime() { return 1; }
396
397 NEW_WINDOW_MACRO(TimelapseHelper, TimelapseHelperWindow)
398
399 LOAD_CONFIGURATION_MACRO(TimelapseHelper, TimelapseHelperConfig)
400
401
402 void TimelapseHelper::save_data(KeyFrame *keyframe)
403 {
404         FileXML output;
405
406 // cause data to be stored directly in text
407         output.set_shared_output(keyframe->xbuf);
408         output.tag.set_title("TIMELAPSEHELPER");
409         output.tag.set_property("BLOCK_SIZE", config.block_size);
410         output.append_tag();
411         output.tag.set_title("/TIMELAPSEHELPER");
412         output.append_tag();
413         output.append_newline();
414         output.terminate_string();
415 }
416
417 void TimelapseHelper::read_data(KeyFrame *keyframe)
418 {
419         FileXML input;
420
421         input.set_shared_input(keyframe->xbuf);
422
423         while(!input.read_tag())
424         {
425                 if(input.tag.title_is("TIMELAPSEHELPER"))
426                 {
427                         config.block_size = input.tag.get_property("BLOCK_SIZE", config.block_size);
428         }
429         }
430 }
431
432 void TimelapseHelper::update_gui()
433 {
434         if(thread)
435         {
436                 if(load_configuration())
437                 {
438                         ((TimelapseHelperWindow*)thread->window)->lock_window("TimelapseHelper::update_gui");
439                         ((TimelapseHelperWindow*)thread->window)->size->update((int64_t)config.block_size);
440                         ((TimelapseHelperWindow*)thread->window)->unlock_window();
441                 }
442         }
443 }
444
445
446
447