r370: Heroine Virutal's official release 1.2.1
[cinelerra_cv/mob.git] / hvirtual / plugins / ivtc / ivtc.C
blob8f313b15043905aee4c2e2e9aaaf855f534916de
1 #include "clip.h"
2 #include "colormodels.h"
3 #include "filexml.h"
4 #include "ivtc.h"
5 #include "ivtcwindow.h"
6 #include "language.h"
8 #include <stdio.h>
9 #include <string.h>
12 REGISTER_PLUGIN(IVTCMain)
14 IVTCConfig::IVTCConfig()
16         frame_offset = 0;
17         first_field = 0;
18         automatic = 1;
19         auto_threshold = 2;
20         pattern = IVTCConfig::PULLDOWN32;
23 IVTCMain::IVTCMain(PluginServer *server)
24  : PluginVClient(server)
26         PLUGIN_CONSTRUCTOR_MACRO
27         engine = 0;
30 IVTCMain::~IVTCMain()
32         PLUGIN_DESTRUCTOR_MACRO
34         if(engine)
35         {
36                 if(temp_frame[0]) delete temp_frame[0];
37                 if(temp_frame[1]) delete temp_frame[1];
38                 temp_frame[0] = 0;
39                 temp_frame[1] = 0;
40                 delete engine;
41         }
44 char* IVTCMain::plugin_title() { return N_("Inverse Telecine"); }
45 int IVTCMain::is_realtime() { return 1; }
48 int IVTCMain::load_defaults()
50         char directory[BCTEXTLEN], string[BCTEXTLEN];
51 // set the default directory
52         sprintf(directory, "%sivtc.rc", BCASTDIR);
54 // load the defaults
55         defaults = new Defaults(directory);
56         defaults->load();
58         config.frame_offset = defaults->get("FRAME_OFFSET", config.frame_offset);
59         config.first_field = defaults->get("FIRST_FIELD", config.first_field);
60         config.automatic = defaults->get("AUTOMATIC", config.automatic);
61         config.auto_threshold = defaults->get("AUTO_THRESHOLD", config.auto_threshold);
62         config.pattern = defaults->get("PATTERN", config.pattern);
63         return 0;
66 int IVTCMain::save_defaults()
68         defaults->update("FRAME_OFFSET", config.frame_offset);
69         defaults->update("FIRST_FIELD", config.first_field);
70         defaults->update("AUTOMATIC", config.automatic);
71         defaults->update("AUTO_THRESHOLD", config.auto_threshold);
72         defaults->update("PATTERN", config.pattern);
73         defaults->save();
74         return 0;
77 #include "picon_png.h"
78 NEW_PICON_MACRO(IVTCMain)
79 SHOW_GUI_MACRO(IVTCMain, IVTCThread)
80 SET_STRING_MACRO(IVTCMain)
81 RAISE_WINDOW_MACRO(IVTCMain)
85 int IVTCMain::load_configuration()
87         KeyFrame *prev_keyframe;
89         prev_keyframe = get_prev_keyframe(get_source_position());
90 // Must also switch between interpolation between keyframes and using first keyframe
91         read_data(prev_keyframe);
93         return 0;
96 void IVTCMain::save_data(KeyFrame *keyframe)
98         FileXML output;
100 // cause data to be stored directly in text
101         output.set_shared_string(keyframe->data, MESSAGESIZE);
102         output.tag.set_title("IVTC");
103         output.tag.set_property("FRAME_OFFSET", config.frame_offset);
104         output.tag.set_property("FIRST_FIELD", config.first_field);
105         output.tag.set_property("AUTOMATIC", config.automatic);
106         output.tag.set_property("AUTO_THRESHOLD", config.auto_threshold);
107         output.tag.set_property("PATTERN", config.pattern);
108         output.append_tag();
109         output.terminate_string();
112 void IVTCMain::read_data(KeyFrame *keyframe)
114         FileXML input;
116         input.set_shared_string(keyframe->data, strlen(keyframe->data));
118         int result = 0;
119         float new_threshold;
121         while(!result)
122         {
123                 result = input.read_tag();
125                 if(!result)
126                 {
127                         if(input.tag.title_is("IVTC"))
128                         {
129                                 config.frame_offset = input.tag.get_property("FRAME_OFFSET", config.frame_offset);
130                                 config.first_field = input.tag.get_property("FIRST_FIELD", config.first_field);
131                                 config.automatic = input.tag.get_property("AUTOMATIC", config.automatic);
132                                 new_threshold = input.tag.get_property("AUTO_THRESHOLD", config.auto_threshold);
133                                 config.pattern = input.tag.get_property("PATTERN", config.pattern);
134                         }
135                 }
136         }
142 // Pattern A B BC CD D
143 int IVTCMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
145         load_configuration();
147         if(!engine)
148         {
149                 temp_frame[0] = 0;
150                 temp_frame[1] = 0;
151         
152                 engine = new IVTCEngine(this, smp + 1);
153         }
155 // Determine position in pattern
156         int pattern_position = (PluginClient::source_position + config.frame_offset) % 5;
158 //printf("IVTCMain::process_realtime %d %d\n", pattern_position, config.first_field);
159         if(!temp_frame[0]) temp_frame[0] = new VFrame(0,
160                 input_ptr->get_w(),
161                 input_ptr->get_h(),
162                 input_ptr->get_color_model(),
163                 -1);
164         if(!temp_frame[1]) temp_frame[1] = new VFrame(0,
165                 input_ptr->get_w(),
166                 input_ptr->get_h(),
167                 input_ptr->get_color_model(),
168                 -1);
170         int row_size = VFrame::calculate_bytes_per_pixel(input_ptr->get_color_model()) * input_ptr->get_w();
171         int64_t field1;
172         int64_t field2;
173         int64_t field1_sum;
174         int64_t field2_sum;
175         this->input = input_ptr;
176         this->output = output_ptr;
178 // Determine pattern
179         if(config.pattern == IVTCConfig::PULLDOWN32)
180         {
181                 switch(pattern_position)
182                 {
183 // Direct copy
184                         case 0:
185                         case 4:
186                                 if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
187                                         output_ptr->copy_from(input_ptr);
188                                 break;
190                         case 1:
191                                 temp_frame[0]->copy_from(input_ptr);
192                                 if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
193                                         output_ptr->copy_from(input_ptr);
194                                 break;
196                         case 2:
197 // Save one field for next frame.  Reuse previous frame.
198                                 temp_frame[1]->copy_from(input_ptr);
199                                 output_ptr->copy_from(temp_frame[0]);
200                                 break;
202                         case 3:
203 // Combine previous field with current field.
204                                 for(int i = 0; i < input_ptr->get_h(); i++)
205                                 {
206                                         if((i + config.first_field) & 1)
207                                                 memcpy(output_ptr->get_rows()[i], 
208                                                         input_ptr->get_rows()[i],
209                                                         row_size);
210                                         else
211                                                 memcpy(output_ptr->get_rows()[i], 
212                                                         temp_frame[1]->get_rows()[i],
213                                                         row_size);
214                                 }
215                                 break;
216                 }
217         }
218         else
219         if(config.pattern == IVTCConfig::SHIFTFIELD)
220         {
221                 temp_frame[1]->copy_from(input_ptr);
223 // Recycle previous bottom or top
224                 for(int i = 0; i < input_ptr->get_h(); i++)
225                 {
226                         if((i + config.first_field) & 1)
227                                 memcpy(output_ptr->get_rows()[i], 
228                                         input_ptr->get_rows()[i],
229                                         row_size);
230                         else
231                                 memcpy(output_ptr->get_rows()[i],
232                                         temp_frame[0]->get_rows()[i],
233                                         row_size);
234                 }
236 // Swap temp frames
237                 VFrame *temp = temp_frame[0];
238                 temp_frame[0] = temp_frame[1];
239                 temp_frame[1] = temp;
240         }
241         else
242         if(config.pattern == IVTCConfig::AUTOMATIC)
243         {
244 // Compare with original rows and with previous rows and averaged rows.
245 // Take rows which are most similar to the averaged rows.
246 // Process frame.
247                 engine->process_packages();
248 // Copy current for future use
249                 temp_frame[1]->copy_from(input_ptr);
251 // Add results
252                 even_vs_current = 0;
253                 even_vs_prev = 0;
254                 odd_vs_current = 0;
255                 odd_vs_prev = 0;
257                 for(int i = 0; i < engine->get_total_clients(); i++)
258                 {
259                         IVTCUnit *unit = (IVTCUnit*)engine->get_client(i);
260                         even_vs_current += unit->even_vs_current;
261                         even_vs_prev += unit->even_vs_prev;
262                         odd_vs_current += unit->odd_vs_current;
263                         odd_vs_prev += unit->odd_vs_prev;
264                 }
267                 int64_t min;
268                 int strategy;
269                 if(even_vs_current > even_vs_prev)
270                 {
271                         min = even_vs_prev;
272                         strategy = 0;
273                 }
274                 else
275                 {
276                         min = even_vs_current;
277                         strategy = 2;
278                 }
280                 if(min > odd_vs_prev)
281                 {
282                         min = odd_vs_prev;
283                         strategy = 1;
284                 }
286                 if(min > odd_vs_current)
287                 {
288                         min = odd_vs_current;
289                         strategy = 2;
290                 }
295 //strategy = 2;
297                 switch(strategy)
298                 {
299                         case 0:
300                                 for(int i = 0; i < input_ptr->get_h(); i++)
301                                 {
302                                         if(!(i & 1))
303                                                 memcpy(output_ptr->get_rows()[i], 
304                                                         temp_frame[0]->get_rows()[i],
305                                                         row_size);
306                                         else
307                                                 memcpy(output_ptr->get_rows()[i],
308                                                         input_ptr->get_rows()[i],
309                                                         row_size);
310                                 }
311                                 break;
312                         case 1:
313                                 for(int i = 0; i < input_ptr->get_h(); i++)
314                                 {
315                                         if(i & 1)
316                                                 memcpy(output_ptr->get_rows()[i], 
317                                                         temp_frame[0]->get_rows()[i],
318                                                         row_size);
319                                         else
320                                                 memcpy(output_ptr->get_rows()[i],
321                                                         input_ptr->get_rows()[i],
322                                                         row_size);
323                                 }
324                                 break;
325                         case 2:
326                                 output_ptr->copy_from(input_ptr);
327                                 break;
328                 }
330                 VFrame *temp = temp_frame[1];
331                 temp_frame[1] = temp_frame[0];
332                 temp_frame[0] = temp;
333         }
334         return 0;
339 void IVTCMain::update_gui()
341         if(thread)
342         {
343                 load_configuration();
344                 thread->window->lock_window();
345                 if(config.pattern == IVTCConfig::AUTOMATIC)
346                 {
347                         thread->window->frame_offset->disable();
348                         thread->window->first_field->disable();
349                 }
350                 else
351                 {
352                         thread->window->frame_offset->enable();
353                         thread->window->first_field->enable();
354                 }
355                 thread->window->frame_offset->update((int64_t)config.frame_offset);
356                 thread->window->first_field->update(config.first_field);
357 //              thread->window->automatic->update(config.automatic);
358                 for(int i = 0; i < TOTAL_PATTERNS; i++)
359                 {
360                         thread->window->pattern[i]->update(config.pattern == i);
361                 }
362                 thread->window->unlock_window();
363         }
368 // labs returns different values on x86_64 causing our accumulators to explode
369 #define ABS local_abs
372 #ifdef __x86_64__
374 static int local_abs(int value)
376         return (value < 0 ? -value : value);
379 static float local_abs(float value)
381         return (value < 0 ? -value : value);
384 #else
386 static int local_abs(int value)
388         return abs(value);
391 static float local_abs(float value)
393         return fabsf(value);
397 #endif
402 IVTCPackage::IVTCPackage()
403  : LoadPackage()
410 IVTCUnit::IVTCUnit(IVTCEngine *server, IVTCMain *plugin)
411  : LoadClient(server)
413         this->server = server;
414         this->plugin = plugin;
417 #define IVTC_MACRO(type, temp_type, components, is_yuv) \
418 { \
419         type **curr_rows = (type**)plugin->input->get_rows(); \
420         type **prev_rows = (type**)plugin->temp_frame[0]->get_rows(); \
421 /* Components to skip for YUV */ \
422         int skip = components - 1; \
424         for(int i = ptr->y1; i < ptr->y2; i++) \
425         { \
426 /* Rows to average in the input frame */ \
427                 int input_row1_number = i - 1; \
428                 int input_row2_number = i + 1; \
429                 input_row1_number = MAX(0, input_row1_number); \
430                 input_row2_number = MIN(h - 1, input_row2_number); \
431                 type *input_row1 = curr_rows[input_row1_number]; \
432                 type *input_row2 = curr_rows[input_row2_number]; \
434 /* Rows to compare the averaged rows to */ \
435                 type *current_row = curr_rows[i]; \
436                 type *prev_row = prev_rows[i]; \
438                 temp_type current_difference = 0; \
439                 temp_type prev_difference = 0; \
440                 for(int j = 0; j < w; j++) \
441                 { \
442 /* Get pixel average */ \
443                         temp_type average = ((temp_type)*input_row1 + *input_row2) / 2; \
444 /* Compare row to current */ \
445                         current_difference += ABS(average - *current_row); \
446 /* Compare row to previous */ \
447                         prev_difference += ABS(average - *prev_row); \
449 /* Do RGB channels */ \
450                         if(!is_yuv) \
451                         { \
452                                 average = ((temp_type)input_row1[1] + input_row2[1]) / 2; \
453                                 current_difference += ABS(average - current_row[1]); \
454                                 prev_difference += ABS(average - prev_row[1]); \
455                                 average = ((temp_type)input_row1[2] + input_row2[2]) / 2; \
456                                 current_difference += ABS(average - current_row[2]); \
457                                 prev_difference += ABS(average - prev_row[2]); \
458                         } \
460 /* Add to row accumulators */ \
461                         current_row += components; \
462                         prev_row += components; \
463                         input_row1 += components; \
464                         input_row2 += components; \
465                 } \
467 /* Store row differences in even or odd variables */ \
468                 if(sizeof(type) == 4) \
469                 { \
470                         if(i % 2) \
471                         { \
472                                 odd_vs_current += (int64_t)(current_difference * 0xffff); \
473                                 odd_vs_prev += (int64_t)(prev_difference); \
474                         } \
475                         else \
476                         { \
477                                 even_vs_current += (int64_t)(current_difference); \
478                                 even_vs_prev += (int64_t)(prev_difference); \
479                         } \
480                 } \
481                 else \
482                 { \
483                         if(i % 2) \
484                         { \
485                                 odd_vs_current += (int64_t)current_difference; \
486                                 odd_vs_prev += (int64_t)prev_difference; \
487                         } \
488                         else \
489                         { \
490                                 even_vs_current += (int64_t)current_difference; \
491                                 even_vs_prev += (int64_t)prev_difference; \
492                         } \
493                 } \
494         } \
497 void IVTCUnit::clear_totals()
499         even_vs_current = 0;
500         even_vs_prev = 0;
501         odd_vs_current = 0;
502         odd_vs_prev = 0;
505 void IVTCUnit::process_package(LoadPackage *package)
507         IVTCPackage *ptr = (IVTCPackage*)package;
508         int w = plugin->input->get_w();
509         int h = plugin->input->get_h();
511         switch(plugin->input->get_color_model())
512         {
513                 case BC_RGB_FLOAT:
514                         IVTC_MACRO(float, float, 3, 0);
515                         break;
516                 case BC_RGB888:
517                         IVTC_MACRO(unsigned char, int, 3, 0);
518                         break;
519                 case BC_YUV888:
520                         IVTC_MACRO(unsigned char, int, 3, 1);
521                         break;
522                 case BC_RGBA_FLOAT:
523                         IVTC_MACRO(float, float, 4, 0);
524                         break;
525                 case BC_RGBA8888:
526                         IVTC_MACRO(unsigned char, int, 4, 0);
527                         break;
528                 case BC_YUVA8888:
529                         IVTC_MACRO(unsigned char, int, 4, 1);
530                         break;
531                 case BC_RGB161616:
532                         IVTC_MACRO(uint16_t, int, 3, 0);
533                         break;
534                 case BC_YUV161616:
535                         IVTC_MACRO(uint16_t, int, 3, 1);
536                         break;
537                 case BC_RGBA16161616:
538                         IVTC_MACRO(uint16_t, int, 4, 0);
539                         break;
540                 case BC_YUVA16161616:
541                         IVTC_MACRO(uint16_t, int, 4, 1);
542                         break;
543         }
544         
551 IVTCEngine::IVTCEngine(IVTCMain *plugin, int cpus)
552  : LoadServer(cpus, cpus)
554         this->plugin = plugin;
557 IVTCEngine::~IVTCEngine()
561 void IVTCEngine::init_packages()
563         int increment = plugin->input->get_h() / get_total_packages();
564         increment /= 2;
565         increment *= 2;
566         if(!increment) increment = 2;
567         int y1 = 0;
568         for(int i = 0; i < get_total_packages(); i++)
569         {
570                 IVTCPackage *package = (IVTCPackage*)get_package(i);
571                 package->y1 = y1;
572                 y1 += increment;
573                 if(y1 > plugin->input->get_h()) y1 = plugin->input->get_h();
574                 package->y2 = y1;
575         }
576         for(int i = 0; i < get_total_clients(); i++)
577         {
578                 IVTCUnit *unit = (IVTCUnit*)get_client(i);
579                 unit->clear_totals();
580         }
583 LoadClient* IVTCEngine::new_client()
585         return new IVTCUnit(this, plugin);
588 LoadPackage* IVTCEngine::new_package()
590         return new IVTCPackage;