Credit Andrew - fix vorbis audio which was scratchy and ensure aging plugin does...
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / denoisevideo / denoisevideo.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  * Copyright (C) 2003-2016 Cinelerra CV contributors
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22
23 #include "clip.h"
24 #include "bchash.h"
25 #include "denoisevideo.h"
26 #include "filexml.h"
27 #include "guicast.h"
28 #include "keyframe.h"
29 #include "language.h"
30 #include "vframe.h"
31
32
33
34
35
36 #include <stdint.h>
37 #include <string.h>
38
39
40
41
42 REGISTER_PLUGIN(DenoiseVideo)
43
44
45
46
47
48
49 DenoiseVideoConfig::DenoiseVideoConfig()
50 {
51         frames = 2;
52         threshold = 0.1;
53         count_changed = 0;
54         do_r = 1;
55         do_g = 1;
56         do_b = 1;
57         do_a = 1;
58 }
59
60 int DenoiseVideoConfig::equivalent(DenoiseVideoConfig &that)
61 {
62         return frames == that.frames &&
63                 EQUIV(threshold, that.threshold) &&
64                 do_r == that.do_r &&
65                 do_g == that.do_g &&
66                 do_b == that.do_b &&
67                 do_a == that.do_a &&
68                 count_changed == that.count_changed;
69 }
70
71 void DenoiseVideoConfig::copy_from(DenoiseVideoConfig &that)
72 {
73         frames = that.frames;
74         threshold = that.threshold;
75         count_changed = that.count_changed;
76         do_r = that.do_r;
77         do_g = that.do_g;
78         do_b = that.do_b;
79         do_a = that.do_a;
80 }
81
82 void DenoiseVideoConfig::interpolate(DenoiseVideoConfig &prev,
83         DenoiseVideoConfig &next,
84         long prev_frame,
85         long next_frame,
86         long current_frame)
87 {
88         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
89         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
90
91         this->frames = (int)(prev.frames * prev_scale + next.frames * next_scale);
92         this->threshold = prev.threshold * prev_scale + next.threshold * next_scale;
93         do_r = prev.do_r;
94         do_g = prev.do_g;
95         do_b = prev.do_b;
96         do_a = prev.do_a;
97         count_changed = prev.count_changed;
98 }
99
100
101
102
103
104
105 DenoiseVideoFrames::DenoiseVideoFrames(DenoiseVideo *plugin, int x, int y)
106  : BC_ISlider(x,
107         y,
108         0,
109         xS(190),
110         yS(200),
111         1,
112         256,
113         plugin->config.frames)
114 {
115         this->plugin = plugin;
116 }
117
118 int DenoiseVideoFrames::handle_event()
119 {
120         int result = get_value();
121         if(result < 1 || result > 256) result = 256;
122         plugin->config.frames = result;
123         plugin->send_configure_change();
124         return 1;
125 }
126
127
128
129
130
131
132
133 DenoiseVideoThreshold::DenoiseVideoThreshold(DenoiseVideo *plugin,
134         DenoiseVideoWindow *gui,
135         int x,
136         int y)
137  : BC_TumbleTextBox(gui,
138         plugin->config.threshold,
139         (float)0,
140         (float)1,
141         x,
142         y,
143         100)
144 {
145         this->plugin = plugin;
146         set_precision(3);
147         set_increment(0.1);
148 }
149
150 int DenoiseVideoThreshold::handle_event()
151 {
152         plugin->config.threshold = atof(get_text());
153         plugin->send_configure_change();
154         return 1;
155 }
156
157
158
159
160
161 DenoiseVideoToggle::DenoiseVideoToggle(DenoiseVideo *plugin,
162         DenoiseVideoWindow *gui,
163         int x,
164         int y,
165         int *output,
166         char *text)
167  : BC_CheckBox(x, y, *output, text)
168 {
169         this->plugin = plugin;
170         this->output = output;
171 }
172
173 int DenoiseVideoToggle::handle_event()
174 {
175         *output = get_value();
176         plugin->send_configure_change();
177         return 1;
178 }
179
180
181 DenoiseVideoCountChanged::DenoiseVideoCountChanged(DenoiseVideo *plugin,
182         DenoiseVideoWindow *gui,
183         int x,
184         int y)
185  : BC_Radial(x,
186         y,
187         plugin->config.count_changed,
188         _("Average changing pixels"))
189 {
190         this->plugin = plugin;
191         this->gui = gui;
192 }
193
194 int DenoiseVideoCountChanged::handle_event()
195 {
196         plugin->config.count_changed = 1;
197         gui->count_same->update(0);
198         plugin->send_configure_change();
199         return 1;
200 }
201
202
203
204
205
206 DenoiseVideoCountSame::DenoiseVideoCountSame(DenoiseVideo *plugin,
207         DenoiseVideoWindow *gui,
208         int x,
209         int y)
210  : BC_Radial(x,
211         y,
212         !plugin->config.count_changed,
213         _("Average similar pixels"))
214 {
215         this->plugin = plugin;
216         this->gui = gui;
217 }
218
219 int DenoiseVideoCountSame::handle_event()
220 {
221         plugin->config.count_changed = 0;
222         gui->count_changed->update(0);
223         plugin->send_configure_change();
224         return 1;
225 }
226
227
228
229
230
231
232
233
234
235
236 DenoiseVideoWindow::DenoiseVideoWindow(DenoiseVideo *plugin)
237  : PluginClientWindow(plugin,
238         xS(250),
239         yS(300),
240         xS(250),
241         yS(300),
242         0)
243 {
244         this->plugin = plugin;
245 }
246
247
248 void DenoiseVideoWindow::create_objects()
249 {
250         int xs10 = xS(10);
251         int ys5 = yS(5), ys10 = yS(10), ys30 = yS(30);
252         int x = xs10, y = ys10;
253         BC_Title *title;
254         BC_Bar *bar;
255         add_subwindow(new BC_Title(x, y, _("Frames to accumulate:")));
256         y += yS(20);
257         add_subwindow(frames = new DenoiseVideoFrames(plugin, x, y));
258         y += frames->get_h() + ys5;
259         add_subwindow(title = new BC_Title(x, y, _("Threshold:")));
260         y += title->get_h() + ys5;
261         threshold = new DenoiseVideoThreshold(plugin, this, x, y);
262         threshold->create_objects();
263         y += threshold->get_h() + ys5;
264         add_subwindow(bar = new BC_Bar(x, y, get_w() - x * 2));
265         y += bar->get_h() + ys5;
266         add_subwindow(count_changed = new DenoiseVideoCountChanged(plugin,
267                 this,
268                 x,
269                 y));
270         y += count_changed->get_h() + ys5;
271         add_subwindow(count_same = new DenoiseVideoCountSame(plugin,
272                 this,
273                 x,
274                 y));
275         y += count_same->get_h() + ys5;
276         add_subwindow(bar = new BC_Bar(x, y, get_w() - x * 2));
277         y += bar->get_h() + ys5;
278         add_subwindow(do_r = new DenoiseVideoToggle(plugin, this, x, y, &plugin->config.do_r, _("Red")));
279         y += ys30;
280         add_subwindow(do_g = new DenoiseVideoToggle(plugin, this, x, y, &plugin->config.do_g, _("Green")));
281         y += ys30;
282         add_subwindow(do_b = new DenoiseVideoToggle(plugin, this, x, y, &plugin->config.do_b, _("Blue")));
283         y += ys30;
284         add_subwindow(do_a = new DenoiseVideoToggle(plugin, this, x, y, &plugin->config.do_a, _("Alpha")));
285         show_window();
286         flush();
287 }
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303 DenoiseVideo::DenoiseVideo(PluginServer *server)
304  : PluginVClient(server)
305 {
306
307         accumulation = 0;
308 }
309
310
311 DenoiseVideo::~DenoiseVideo()
312 {
313
314
315         if(accumulation) delete [] accumulation;
316 }
317
318 int DenoiseVideo::process_realtime(VFrame *input, VFrame *output)
319 {
320         load_configuration();
321
322         int h = input->get_h();
323         int w = input->get_w();
324         int color_model = input->get_color_model();
325
326         if(!accumulation)
327         {
328                 accumulation = new float[w * h * BC_CModels::components(color_model)];
329                 bzero(accumulation, sizeof(float) * w * h * BC_CModels::components(color_model));
330         }
331
332         float *accumulation_ptr = accumulation;
333         float opacity = (float)1.0 / config.frames;
334         float transparency = 1 - opacity;
335         float threshold = (float)config.threshold *
336                 BC_CModels::calculate_max(color_model);
337         int do_it[4] = { config.do_r, config.do_g, config.do_b, config.do_a };
338
339 #define DENOISE_MACRO(type, components, max) \
340 { \
341         for(int i = 0; i < h; i++) \
342         { \
343                 type *output_row = (type*)output->get_rows()[i]; \
344                 type *input_row = (type*)input->get_rows()[i]; \
345  \
346                 for(int k = 0; k < w * components; k++) \
347                 { \
348                         if( do_it[k % components] ) \
349                         { \
350                                 float input_pixel = *input_row; \
351                                 (*accumulation_ptr) = \
352                                         transparency * (*accumulation_ptr) + \
353                                         opacity * input_pixel; \
354  \
355                                 float difference = fabs((*accumulation_ptr) - input_pixel); \
356                                 if( !config.count_changed ? \
357                                          difference > threshold : difference < threshold) \
358                                 { \
359                                         (*accumulation_ptr) = input_pixel; \
360                                         *output_row = (type)(*accumulation_ptr); \
361                                 } \
362                                 else \
363                                 if(sizeof(type) < 4) \
364                                         *output_row = (type)CLIP((*accumulation_ptr), 0, max); \
365                         } \
366                         else \
367                         { \
368                                 *output_row = *input_row; \
369                         } \
370  \
371                         output_row++; \
372                         input_row++; \
373                         accumulation_ptr++; \
374                 } \
375         } \
376 }
377
378
379
380
381
382
383         switch(color_model)
384         {
385                 case BC_RGB888:
386                 case BC_YUV888:
387                         DENOISE_MACRO(unsigned char, 3, 0xff);
388                         break;
389
390                 case BC_RGB_FLOAT:
391                         DENOISE_MACRO(float, 3, 1.0);
392                         break;
393
394                 case BC_RGBA8888:
395                 case BC_YUVA8888:
396                         DENOISE_MACRO(unsigned char, 4, 0xff);
397                         break;
398
399                 case BC_RGBA_FLOAT:
400                         DENOISE_MACRO(float, 4, 1.0);
401                         break;
402
403                 case BC_RGB161616:
404                 case BC_YUV161616:
405                         DENOISE_MACRO(uint16_t, 3, 0xffff);
406                         break;
407
408                 case BC_RGBA16161616:
409                 case BC_YUVA16161616:
410                         DENOISE_MACRO(uint16_t, 4, 0xffff);
411                         break;
412         }
413         return 0;
414 }
415
416
417 const char* DenoiseVideo::plugin_title() { return N_("Denoise video"); }
418 int DenoiseVideo::is_realtime() { return 1; }
419
420
421 NEW_WINDOW_MACRO(DenoiseVideo, DenoiseVideoWindow)
422
423 LOAD_CONFIGURATION_MACRO(DenoiseVideo, DenoiseVideoConfig)
424
425 void DenoiseVideo::update_gui()
426 {
427         if(thread)
428         {
429                 load_configuration();
430                 ((DenoiseVideoWindow*)thread->window)->lock_window();
431                 ((DenoiseVideoWindow*)thread->window)->frames->update(config.frames);
432                 ((DenoiseVideoWindow*)thread->window)->threshold->update(config.threshold);
433                 ((DenoiseVideoWindow*)thread->window)->count_changed->update(config.count_changed);
434                 ((DenoiseVideoWindow*)thread->window)->count_same->update(!config.count_changed);
435                 ((DenoiseVideoWindow*)thread->window)->unlock_window();
436         }
437 }
438
439
440
441
442 void DenoiseVideo::save_data(KeyFrame *keyframe)
443 {
444         FileXML output;
445
446 // cause data to be stored directly in text
447         output.set_shared_output(keyframe->xbuf);
448         output.tag.set_title("DENOISE_VIDEO");
449         output.tag.set_property("FRAMES", config.frames);
450         output.tag.set_property("THRESHOLD", config.threshold);
451         output.tag.set_property("DO_R", config.do_r);
452         output.tag.set_property("DO_G", config.do_g);
453         output.tag.set_property("DO_B", config.do_b);
454         output.tag.set_property("DO_A", config.do_a);
455         output.tag.set_property("COUNT_CHANGED", config.count_changed);
456         output.append_tag();
457         output.tag.set_title("/DENOISE_VIDEO");
458         output.append_tag();
459         output.append_newline();
460         output.terminate_string();
461 }
462
463 void DenoiseVideo::read_data(KeyFrame *keyframe)
464 {
465         FileXML input;
466
467         input.set_shared_input(keyframe->xbuf);
468
469         while(!input.read_tag())
470         {
471                 if(input.tag.title_is("DENOISE_VIDEO"))
472                 {
473                         config.frames = input.tag.get_property("FRAMES", config.frames);
474                         config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
475                         config.do_r = input.tag.get_property("DO_R", config.do_r);
476                         config.do_g = input.tag.get_property("DO_G", config.do_g);
477                         config.do_b = input.tag.get_property("DO_B", config.do_b);
478                         config.do_a = input.tag.get_property("DO_A", config.do_a);
479                         config.count_changed = input.tag.get_property("COUNT_CHANGED", config.count_changed);
480                 }
481         }
482 }
483