1de85f0a1da4a1b8c06fb2fa1fa0355874247631
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / unsharp / unsharp.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 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 #include "bcdisplayinfo.h"
23 #include "clip.h"
24 #include "bchash.h"
25 #include "bcsignals.h"
26 #include "filexml.h"
27 #include "keyframe.h"
28 #include "language.h"
29 #include "unsharp.h"
30 #include "unsharpwindow.h"
31
32
33 #include <errno.h>
34 #include <unistd.h>
35
36 REGISTER_PLUGIN(UnsharpMain)
37
38
39
40 UnsharpConfig::UnsharpConfig()
41 {
42         radius = 5;
43         amount = 0.5;
44         threshold = 0;
45 }
46
47 int UnsharpConfig::equivalent(UnsharpConfig &that)
48 {
49         return EQUIV(radius, that.radius) &&
50                 EQUIV(amount, that.amount) &&
51                 threshold == that.threshold;
52 }
53
54 void UnsharpConfig::copy_from(UnsharpConfig &that)
55 {
56         radius = that.radius;
57         amount = that.amount;
58         threshold = that.threshold;
59 }
60
61 void UnsharpConfig::interpolate(UnsharpConfig &prev,
62         UnsharpConfig &next,
63         int64_t prev_frame,
64         int64_t next_frame,
65         int64_t current_frame)
66 {
67         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
68         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
69         this->radius = prev.radius * prev_scale + next.radius * next_scale;
70         this->amount = prev.amount * prev_scale + next.amount * next_scale;
71         this->threshold = (int)(prev.threshold * prev_scale + next.threshold * next_scale);
72 }
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 UnsharpMain::UnsharpMain(PluginServer *server)
93  : PluginVClient(server)
94 {
95
96         engine = 0;
97 }
98
99 UnsharpMain::~UnsharpMain()
100 {
101
102         delete engine;
103 }
104
105 const char* UnsharpMain::plugin_title() { return N_("Unsharp"); }
106 int UnsharpMain::is_realtime() { return 1; }
107
108
109 NEW_WINDOW_MACRO(UnsharpMain, UnsharpWindow)
110
111 LOAD_CONFIGURATION_MACRO(UnsharpMain, UnsharpConfig)
112
113
114
115 void UnsharpMain::update_gui()
116 {
117         if(thread)
118         {
119                 if(load_configuration())
120                 {
121                         thread->window->lock_window("UnsharpMain::update_gui");
122                         ((UnsharpWindow*)thread->window)->update();
123                         thread->window->unlock_window();
124                 }
125         }
126 }
127
128
129
130
131 void UnsharpMain::save_data(KeyFrame *keyframe)
132 {
133         FileXML output;
134
135 // cause data to be stored directly in text
136         output.set_shared_output(keyframe->xbuf);
137         output.tag.set_title("UNSHARP");
138
139         output.tag.set_property("RADIUS", config.radius);
140         output.tag.set_property("AMOUNT", config.amount);
141         output.tag.set_property("THRESHOLD", config.threshold);
142         output.append_tag();
143         output.tag.set_title("/UNSHARP");
144         output.append_tag();
145         output.append_newline();
146         output.terminate_string();
147 }
148
149 void UnsharpMain::read_data(KeyFrame *keyframe)
150 {
151         FileXML input;
152
153         input.set_shared_input(keyframe->xbuf);
154
155         int result = 0;
156
157         while(!result)
158         {
159                 result = input.read_tag();
160
161                 if(!result)
162                 {
163                         if(input.tag.title_is("UNSHARP"))
164                         {
165                                 config.radius = input.tag.get_property("RADIUS", config.radius);
166                                 config.amount = input.tag.get_property("AMOUNT", config.amount);
167                                 config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
168                         }
169                 }
170         }
171 }
172
173
174
175
176
177
178
179 int UnsharpMain::process_buffer(VFrame *frame,
180         int64_t start_position,
181         double frame_rate)
182 {
183         /*int need_reconfigure =*/ load_configuration();
184
185         if(!engine) engine = new UnsharpEngine(this,
186                 get_project_smp() + 1, get_project_smp() + 1);
187         read_frame(frame, 0, get_source_position(), get_framerate(), 0);
188         engine->do_unsharp(frame);
189         return 0;
190 }
191
192
193
194
195
196
197
198
199
200
201
202
203 UnsharpPackage::UnsharpPackage()
204  : LoadPackage()
205 {
206 }
207
208
209
210
211
212
213 UnsharpUnit::UnsharpUnit(UnsharpEngine *server,
214         UnsharpMain *plugin)
215  : LoadClient(server)
216 {
217         this->plugin = plugin;
218         this->server = server;
219         temp = 0;
220 }
221
222 UnsharpUnit::~UnsharpUnit()
223 {
224         delete temp;
225 }
226
227
228 // Derived from the Gimp.
229 // In the original file it says
230 //
231 // Copyright (C) 1999 Winston Chang
232 //                    <winstonc@cs.wisc.edu>
233 //                    <winston@stdout.org>
234 //
235 // Adapted for Cinelerra by Heroine Virtual Ltd.
236
237 static int calculate_convolution_matrix(double radius, double **cmatrix)
238 {
239         radius = fabs(radius) + 1.0;
240         double std_dev = radius;
241         radius = std_dev * 2;
242         int matrix_length = (int)(2 * ceil(radius - 0.5) + 1);
243         matrix_length = MAX(1, matrix_length);
244 //      int matrix_midpoint = matrix_length / 2 + 1;
245         (*cmatrix) = new double[matrix_length];
246
247 // Top right of matrix
248         for(int i = matrix_length / 2 + 1; i < matrix_length; i++)
249         {
250                 double base_x = i - floor(matrix_length / 2) - 0.5;
251                 double sum = 0;
252                 for(int j = 1; j <= 50; j++)
253                 {
254                         if(base_x + 0.02 * j <= radius)
255                         {
256                                 sum += exp(-(base_x + 0.02 * j) *
257                                         (base_x + 0.02 * j) /
258                                         (2 * std_dev * std_dev));
259                         }
260                 }
261                 (*cmatrix)[i] = sum / 50;
262         }
263
264 // Top left of matrix
265         for(int i = 0; i < matrix_length / 2; i++)
266         {
267                 (*cmatrix)[i] = (*cmatrix)[matrix_length - 1 - i];
268         }
269
270 // Center value
271         double sum = 0;
272         for(int j = 0; j <= 50; j++)
273         {
274                 sum += exp(-(0.5 + 0.02 * j) *
275                         (0.5 + 0.02 * j) /
276                         (2 * std_dev * std_dev));
277         }
278         (*cmatrix)[matrix_length / 2] = sum / 51;
279
280 // Normalize
281         sum = 0;
282         for(int i = 0; i < matrix_length; i++)
283                 sum += (*cmatrix)[i];
284         for(int i = 0; i < matrix_length; i++)
285                 (*cmatrix)[i] = (*cmatrix)[i] / sum;
286
287         return matrix_length;
288 }
289
290 static double get_convolution(double *cmatrix,
291         float input,
292         int index)
293 {
294         return cmatrix[index] * input;
295 }
296
297 static void blur_pixels(double *cmatrix,
298         int cmatrix_length,
299         float *input,
300         float *output,
301         int pixels,
302         int components)
303 {
304         if(cmatrix_length > pixels)
305         {
306                 for(int pixel = 0; pixel < pixels; pixel++)
307                 {
308                         double scale = 0;
309                         for(int j = 0; j < pixels; j++)
310                         {
311                                 if((j + cmatrix_length / 2 - pixel >= 0) &&
312                                         (j + cmatrix_length / 2 - pixel < cmatrix_length))
313                                 {
314                                         scale += cmatrix[j + cmatrix_length / 2 - pixel];
315                                 }
316                         }
317
318                         for(int i = 0; i < components; i++)
319                         {
320                                 double sum = 0;
321                                 for(int j = 0; j < pixels; j++)
322                                 {
323                                         if((j >= pixel - cmatrix_length / 2) &&
324                                                 (j <= pixel + cmatrix_length / 2))
325                                         {
326                                                 sum += input[j * components + i] * cmatrix[i];
327                                         }
328                                 }
329                                 output[pixel * components + i] = sum / scale;
330                         }
331                 }
332         }
333         else
334         {
335                 int cmatrix_middle = cmatrix_length / 2;
336                 int pixel;
337                 for(pixel = 0; pixel < cmatrix_middle; pixel++)
338                 {
339                         double scale = 0;
340                         for(int j = cmatrix_middle - pixel; j < cmatrix_length;j++)
341                         {
342                                 scale += cmatrix[j];
343                         }
344
345                         for(int i = 0; i < components; i++)
346                         {
347                                 double sum = 0;
348                                 for(int j = cmatrix_middle - pixel; j < cmatrix_length; j++)
349                                 {
350                                         sum += input[(pixel + j - cmatrix_middle) * components + i] *
351                                                 cmatrix[j];
352                                 }
353                                 output[pixel * components + i] = sum / scale;
354                         }
355                 }
356
357                 float *output_ptr = output + pixel * components;
358                 for( ; pixel < pixels - cmatrix_middle; pixel++)
359                 {
360                         float *input_ptr = input + (pixel - cmatrix_middle) * components;
361                         for(int i = 0; i < components; i++)
362                         {
363                                 double sum = 0;
364                                 float *input_ptr2 = input_ptr;
365                                 for(int j = cmatrix_length; j > 0; j--)
366                                 {
367                                         sum += get_convolution(cmatrix,
368                                                 *input_ptr2,
369                                                 cmatrix_length - j);
370                                         input_ptr2 += components;
371                                 }
372                                 input_ptr++;
373                                 *output_ptr++ = sum;
374                         }
375                 }
376
377                 for( ; pixel < pixels; pixel++)
378                 {
379                         double scale = 0;
380                         for(int j = 0; j < pixels - pixel + cmatrix_middle; j++)
381                         {
382                                 scale += cmatrix[j];
383                         }
384
385                         for(int i = 0; i < components; i++)
386                         {
387                                 double sum = 0;
388                                 for(int j = 0; j < pixels - pixel + cmatrix_middle; j++)
389                                 {
390                                         sum += input[(pixel + j - cmatrix_middle) * components + i] *
391                                                 cmatrix[j];
392                                 }
393                                 output[pixel * components + i] = sum / scale;
394                         }
395                 }
396         }
397 }
398
399 #define GET_ROW(type, components) \
400 { \
401         type *in_row = (type*)src->get_rows()[row]; \
402         int pixels = src->get_w() * components; \
403         for(int i = 0; i < pixels; i++) \
404         { \
405                 dst[i] = in_row[i]; \
406         } \
407 }
408
409 static void get_row(float *dst, VFrame *src, int row)
410 {
411         switch(src->get_color_model())
412         {
413                 case BC_RGB888:
414                 case BC_YUV888:
415                         GET_ROW(unsigned char, 3);
416                         break;
417                 case BC_RGB_FLOAT:
418                         GET_ROW(float, 3);
419                         break;
420                 case BC_RGBA8888:
421                 case BC_YUVA8888:
422                         GET_ROW(unsigned char, 4);
423                         break;
424                 case BC_RGBA_FLOAT:
425                         GET_ROW(float, 4);
426                         break;
427                 case BC_YUV161616:
428                         GET_ROW(uint16_t, 3);
429                         break;
430                 case BC_YUVA16161616:
431                         GET_ROW(uint16_t, 4);
432                         break;
433         }
434 }
435
436 static void get_column(float *dst, VFrame *src, int column)
437 {
438         int components = BC_CModels::components(src->get_color_model());
439         for(int i = 0; i < src->get_h(); i++)
440         {
441                 float *input_pixel = (float*)src->get_rows()[i] + column * components;
442                 memcpy(dst, input_pixel, sizeof(float) * components);
443                 dst += components;
444         }
445 }
446
447 static void put_column(float *src, VFrame *dst, int column)
448 {
449         int components = BC_CModels::components(dst->get_color_model());
450         for(int i = 0; i < dst->get_h(); i++)
451         {
452                 float *output_pixel = (float*)dst->get_rows()[i] + column * components;
453                 memcpy(output_pixel, src, sizeof(float) * components);
454                 src += components;
455         }
456 }
457
458 void UnsharpUnit::process_package(LoadPackage *package)
459 {
460         UnsharpPackage *pkg = (UnsharpPackage*)package;
461 //      int w = server->src->get_w();
462 //      int h = server->src->get_h();
463         int color_model = server->src->get_color_model();
464         int components = BC_CModels::components(color_model);
465         double *cmatrix = 0;
466         int cmatrix_length = 0;
467         int padded_y1 = pkg->y1;
468         int padded_y2 = pkg->y2;
469
470         cmatrix_length = calculate_convolution_matrix(
471                 plugin->config.radius,
472                 &cmatrix);
473
474
475         if(padded_y2 < server->src->get_h())
476         {
477                 padded_y2 += cmatrix_length / 2;
478                 padded_y2 = MIN(server->src->get_h(), padded_y2);
479         }
480         if(padded_y1 > 0)
481         {
482                 padded_y1 -= cmatrix_length / 2;
483                 padded_y1 = MAX(0, padded_y1);
484         }
485
486         int padded_rows = padded_y2 - padded_y1;
487
488         if(!temp || temp->get_h() != padded_rows)
489         {
490                 delete temp;
491                 temp = 0;
492         }
493
494         if(!temp)
495         {
496                 temp = new VFrame;
497                 temp->set_use_shm(0);
498                 temp->reallocate(0,
499                         -1,
500                         0,
501                         0,
502                         0,
503                         server->src->get_w(),
504                         padded_rows,
505                         components == 3 ? BC_RGB_FLOAT : BC_RGBA_FLOAT,
506                         -1);
507         }
508
509         float *temp_in = new float[MAX(temp->get_w(), padded_rows) * components];
510         float *temp_out = new float[MAX(temp->get_w(), padded_rows) * components];
511
512 // Blur rows
513         for(int i = padded_y1; i < padded_y2; i++)
514         {
515                 get_row(temp_in, server->src, i);
516                 blur_pixels(cmatrix,
517                         cmatrix_length,
518                         temp_in,
519                         temp_out,
520                         temp->get_w(),
521                         components);
522 // printf("UnsharpUnit::process_package %d %p %p %p %d %d\n",
523 // __LINE__,
524 // temp,
525 // temp->get_rows()[0],
526 // temp_out,
527 // i - padded_y1,
528 // temp->get_bytes_per_line());
529                 memcpy(temp->get_rows()[i - padded_y1],
530                         temp_out,
531                         temp->get_bytes_per_line());
532         }
533
534 //Now we're 100% floating point.  Blur the columns
535         for(int i = 0; i < temp->get_w(); i++)
536         {
537                 get_column(temp_in, temp, i);
538                 blur_pixels(cmatrix,
539                         cmatrix_length,
540                         temp_in,
541                         temp_out,
542                         padded_rows,
543                         components);
544                 put_column(temp_out, temp, i);
545         }
546
547
548 //printf("%f %f %d\n", plugin->config.radius,plugin->config.amount, plugin->config.threshold);
549
550
551 #define UNSHARPEN(type, components, max) \
552 { \
553         float threshold = (float)plugin->config.threshold * max / 0xff; \
554         float amount = plugin->config.amount; \
555  \
556         for(int i = pkg->y1; i < pkg->y2; i++) \
557         { \
558                 float *blurry_row = (float*)temp->get_rows()[i - padded_y1]; \
559                 type *orig_row = (type*)server->src->get_rows()[i]; \
560                 for(int j = 0; j < server->src->get_w(); j++) \
561                 { \
562                         for(int k = 0; k < components; k++) \
563                         { \
564                                 float diff = *orig_row - *blurry_row; \
565                                 if(fabsf(2 * diff) < threshold) \
566                                         diff = 0; \
567                                 float value = *orig_row + amount * diff; \
568                                 if(sizeof(type) == 4) \
569                                         *orig_row = (type)value; \
570                                 else \
571                                         *orig_row = (type)CLIP(value, 0, max); \
572                                 blurry_row++; \
573                                 orig_row++; \
574                         } \
575                 } \
576         } \
577 }
578
579 // Apply unsharpening
580         switch(color_model)
581         {
582                 case BC_RGB888:
583                 case BC_YUV888:
584                         UNSHARPEN(unsigned char, 3, 0xff);
585                         break;
586                 case BC_RGBA8888:
587                 case BC_YUVA8888:
588                         UNSHARPEN(unsigned char, 4, 0xff);
589                         break;
590                 case BC_RGB_FLOAT:
591                         UNSHARPEN(float, 3, 1.0);
592                         break;
593                 case BC_RGBA_FLOAT:
594                         UNSHARPEN(float, 4, 1.0);
595                         break;
596                 case BC_YUV161616:
597                         UNSHARPEN(uint16_t, 3, 0xffff);
598                         break;
599                 case BC_YUVA16161616:
600                         UNSHARPEN(uint16_t, 4, 0xffff);
601                         break;
602         }
603
604         delete [] temp_in;
605         delete [] temp_out;
606         delete [] cmatrix;
607 }
608
609
610
611
612
613
614
615
616
617 UnsharpEngine::UnsharpEngine(UnsharpMain *plugin,
618         int total_clients,
619         int total_packages)
620  : LoadServer(
621 //1, 1
622 total_clients, total_packages
623 )
624 {
625         this->plugin = plugin;
626 }
627
628 UnsharpEngine::~UnsharpEngine()
629 {
630 }
631
632
633 void UnsharpEngine::init_packages()
634 {
635         for(int i = 0; i < get_total_packages(); i++)
636         {
637                 UnsharpPackage *pkg = (UnsharpPackage*)get_package(i);
638                 pkg->y1 = src->get_h() * i / get_total_packages();
639                 pkg->y2 = src->get_h() * (i + 1) / get_total_packages();
640         }
641 }
642
643 LoadClient* UnsharpEngine::new_client()
644 {
645         return new UnsharpUnit(this, plugin);
646 }
647
648 LoadPackage* UnsharpEngine::new_package()
649 {
650         return new UnsharpPackage;
651 }
652
653
654 void UnsharpEngine::do_unsharp(VFrame *src)
655 {
656         this->src = src;
657
658         process_packages();
659 }
660
661
662
663
664
665
666
667
668