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