r105: This commit was manufactured by cvs2svn to create tag
[cinelerra_cv/mob.git] / hvirtual / plugins / ivtc / ivtc.C
blob5be8b3557810599454d2cf7b0532f336d2b2523a
1 #include "colormodels.h"
2 #include "filexml.h"
3 #include "ivtc.h"
4 #include "ivtcwindow.h"
6 #include <stdio.h>
7 #include <string.h>
9 #include <libintl.h>
10 #define _(String) gettext(String)
11 #define gettext_noop(String) String
12 #define N_(String) gettext_noop (String)
15 REGISTER_PLUGIN(IVTCMain)
17 IVTCConfig::IVTCConfig()
19         frame_offset = 0;
20         first_field = 0;
21         automatic = 1;
22         auto_threshold = 2;
23         pattern = 0;
26 IVTCMain::IVTCMain(PluginServer *server)
27  : PluginVClient(server)
29         PLUGIN_CONSTRUCTOR_MACRO
30         engine = 0;
33 IVTCMain::~IVTCMain()
35         PLUGIN_DESTRUCTOR_MACRO
37         if(engine)
38         {
39                 if(temp_frame[0]) delete temp_frame[0];
40                 if(temp_frame[1]) delete temp_frame[1];
41                 temp_frame[0] = 0;
42                 temp_frame[1] = 0;
43                 for(int i = 0; i < (smp + 1); i++)
44                         delete engine[i];
45                 delete [] engine;
46         }
49 char* IVTCMain::plugin_title() { return _("Inverse Telecine"); }
51 int IVTCMain::is_realtime() { return 1; }
54 int IVTCMain::load_defaults()
56         char directory[BCTEXTLEN], string[BCTEXTLEN];
57 // set the default directory
58         sprintf(directory, "%sivtc.rc", BCASTDIR);
60 // load the defaults
61         defaults = new Defaults(directory);
62         defaults->load();
64         config.frame_offset = defaults->get("FRAME_OFFSET", config.frame_offset);
65         config.first_field = defaults->get("FIRST_FIELD", config.first_field);
66         config.automatic = defaults->get("AUTOMATIC", config.automatic);
67         config.auto_threshold = defaults->get("AUTO_THRESHOLD", config.auto_threshold);
68         config.pattern = defaults->get("PATTERN", config.pattern);
69         return 0;
72 int IVTCMain::save_defaults()
74         defaults->update("FRAME_OFFSET", config.frame_offset);
75         defaults->update("FIRST_FIELD", config.first_field);
76         defaults->update("AUTOMATIC", config.automatic);
77         defaults->update("AUTO_THRESHOLD", config.auto_threshold);
78         defaults->update("PATTERN", config.pattern);
79         defaults->save();
80         return 0;
83 #include "picon_png.h"
84 NEW_PICON_MACRO(IVTCMain)
85 SHOW_GUI_MACRO(IVTCMain, IVTCThread)
86 SET_STRING_MACRO(IVTCMain)
87 RAISE_WINDOW_MACRO(IVTCMain)
91 int IVTCMain::load_configuration()
93         KeyFrame *prev_keyframe;
95         prev_keyframe = get_prev_keyframe(get_source_position());
96 // Must also switch between interpolation between keyframes and using first keyframe
97         read_data(prev_keyframe);
99         return 0;
102 void IVTCMain::save_data(KeyFrame *keyframe)
104         FileXML output;
106 // cause data to be stored directly in text
107         output.set_shared_string(keyframe->data, MESSAGESIZE);
108         output.tag.set_title("IVTC");
109         output.tag.set_property("FRAME_OFFSET", config.frame_offset);
110         output.tag.set_property("FIRST_FIELD", config.first_field);
111         output.tag.set_property("AUTOMATIC", config.automatic);
112         output.tag.set_property("AUTO_THRESHOLD", config.auto_threshold);
113         output.tag.set_property("PATTERN", config.pattern);
114         output.append_tag();
115         output.terminate_string();
118 void IVTCMain::read_data(KeyFrame *keyframe)
120         FileXML input;
122         input.set_shared_string(keyframe->data, strlen(keyframe->data));
124         int result = 0;
125         float new_threshold;
127         while(!result)
128         {
129                 result = input.read_tag();
131                 if(!result)
132                 {
133                         if(input.tag.title_is("IVTC"))
134                         {
135                                 config.frame_offset = input.tag.get_property("FRAME_OFFSET", config.frame_offset);
136                                 config.first_field = input.tag.get_property("FIRST_FIELD", config.first_field);
137                                 config.automatic = input.tag.get_property("AUTOMATIC", config.automatic);
138                                 new_threshold = input.tag.get_property("AUTO_THRESHOLD", config.auto_threshold);
139                                 config.pattern = input.tag.get_property("PATTERN", config.pattern);
140                         }
141                 }
142         }
146 void IVTCMain::compare_fields(VFrame *frame1, 
147         VFrame *frame2, 
148         int64_t &field1,
149         int64_t &field2)
151         field1 = field2 = 0;
152         for(int i = 0; i < (smp + 1); i++)
153         {
154                 engine[i]->start_process_frame(frame1, frame2);
155         }
156         
157         for(int i = 0; i < (smp + 1); i++)
158         {
159                 engine[i]->wait_process_frame();
160                 field1 += engine[i]->field1;
161                 field2 += engine[i]->field2;
162         }
165 #define ABS(x) ((x) > 0 ? (x) : -(x))
167 // Pattern A B BC CD D
168 int IVTCMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
170         load_configuration();
172 //printf("IVTCMain::process_realtime 1\n");
173         if(!engine)
174         {
175                 temp_frame[0] = 0;
176                 temp_frame[1] = 0;
177                 state = 0;
178                 new_field = 0;
179                 average = 0;
180                 total_average = (int64_t)(project_frame_rate + 0.5);
181         
182                 int y1, y2, y_increment;
183                 y_increment = input_ptr->get_h() / (smp + 1);
184                 y1 = 0;
186                 engine = new IVTCEngine*[(smp + 1)];
187                 for(int i = 0; i < (smp + 1); i++)
188                 {
189                         y2 = y1 + y_increment;
190                         if(i == (PluginClient::smp + 1) - 1 && 
191                                 y2 < input_ptr->get_h() - 1) 
192                                 y2 = input_ptr->get_h() - 1;
193                         engine[i] = new IVTCEngine(this, y1, y2);
194                         engine[i]->start();
195                         y1 += y_increment;
196                 }
197         }
199 // Determine position in pattern
200         int pattern_position = (PluginClient::source_position + config.frame_offset) % 5;
202 //printf("IVTCMain::process_realtime %d %d\n", pattern_position, config.first_field);
203         if(!temp_frame[0]) temp_frame[0] = new VFrame(0,
204                 input_ptr->get_w(),
205                 input_ptr->get_h(),
206                 input_ptr->get_color_model(),
207                 -1);
208         if(!temp_frame[1]) temp_frame[1] = new VFrame(0,
209                 input_ptr->get_w(),
210                 input_ptr->get_h(),
211                 input_ptr->get_color_model(),
212                 -1);
214         int row_size = VFrame::calculate_bytes_per_pixel(input_ptr->get_color_model()) * input_ptr->get_w();
216 // Determine pattern
217         if(config.pattern == 1)
218         {
219                 temp_frame[1]->copy_from(input_ptr);
221 // Recycle previous bottom or top
222                 for(int i = 0; i < input_ptr->get_h(); i++)
223                 {
224                         if((i + config.first_field) & 1)
225                                 memcpy(output_ptr->get_rows()[i], 
226                                         input_ptr->get_rows()[i],
227                                         row_size);
228                         else
229                                 memcpy(output_ptr->get_rows()[i],
230                                         temp_frame[0]->get_rows()[i],
231                                         row_size);
232                 }
234 // Swap temp frames
235                 VFrame *temp = temp_frame[0];
236                 temp_frame[0] = temp_frame[1];
237                 temp_frame[1] = temp;
238         }
239         else
240 // Determine where in the pattern we are
241         if(config.automatic)
242         {
243                 int64_t field1;
244                 int64_t field2;
246                 compare_fields(temp_frame[0], 
247                         input_ptr, 
248                         field1,
249                         field2);
251                 int64_t field_difference = field2 - field1;
252                 int64_t threshold;
255                 temp_frame[1]->copy_from(input_ptr);
257 // Automatically generate field difference threshold using weighted average of previous frames
258                 if(average > 0)
259                         threshold = average;
260                 else
261                         threshold = ABS(field_difference);
263 //printf("IVTCMain::process_realtime 1 %d %lld %lld %lld\n", state, average, threshold, field_difference);
264 // CD
265                 if(state == 3)
266                 {
267                         state = 4;
269 // Compute new threshold for next time
270                         average = (int64_t)(average * total_average + 
271                                 ABS(field_difference)) / (total_average + 1);
273                         for(int i = 0; i < input_ptr->get_h(); i++)
274                         {
275                                 if((i + new_field) & 1)
276                                         memcpy(output_ptr->get_rows()[i], 
277                                                 input_ptr->get_rows()[i],
278                                                 row_size);
279                                 else
280                                         memcpy(output_ptr->get_rows()[i],
281                                                 temp_frame[0]->get_rows()[i],
282                                                 row_size);
283                         }
284                 }
285                 else
286 // Neither field changed enough
287 // A or B or D
288                 if(ABS(field_difference) <= threshold ||
289                         state == 4)
290                 {
291                         state = 0;
293 // Compute new threshold for next time
294                         average = (int64_t)(average * total_average + 
295                                 ABS(field_difference)) / (total_average + 1);
297                         if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
298                                 output_ptr->copy_from(input_ptr);
299                 }
300                 else
301 // Field 2 changed more than field 1
302                 if(field_difference > threshold)
303                 {
304 // BC bottom field new
305                         state = 3;
306                         new_field = 1;
308 // Compute new threshold for next time
309                         average = (int64_t)(average * total_average + 
310                                 ABS(field_difference)) / (total_average + 1);
312                         for(int i = 0; i < input_ptr->get_h(); i++)
313                         {
314                                 if(i & 1)
315                                         memcpy(output_ptr->get_rows()[i], 
316                                                 temp_frame[0]->get_rows()[i],
317                                                 row_size);
318                                 else
319                                         memcpy(output_ptr->get_rows()[i],
320                                                 input_ptr->get_rows()[i],
321                                                 row_size);
322                         }
323                 }
324                 else
325 // Field 1 changed more than field 2
326                 if(field_difference < -threshold)
327                 {
328 // BC top field new
329                         state = 3;
330                         new_field = 0;
332 // Compute new threshold for next time
333                         average = (int64_t)(average * total_average + 
334                                 ABS(field_difference)) / (total_average + 1);
336                         for(int i = 0; i < input_ptr->get_h(); i++)
337                         {
338                                 if(i & 1)
339                                         memcpy(output_ptr->get_rows()[i],
340                                                 input_ptr->get_rows()[i],
341                                                 row_size);
342                                 else
343                                         memcpy(output_ptr->get_rows()[i], 
344                                                 temp_frame[0]->get_rows()[i],
345                                                 row_size);
346                         }
347                 }
349 // Swap temp frames
350                 VFrame *temp = temp_frame[0];
351                 temp_frame[0] = temp_frame[1];
352                 temp_frame[1] = temp;
353         }
354         else
355         switch(pattern_position)
356         {
357 // Direct copy
358                 case 0:
359                 case 4:
360                         if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
361                                 output_ptr->copy_from(input_ptr);
362                         break;
364                 case 1:
365                         temp_frame[0]->copy_from(input_ptr);
366                         if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
367                                 output_ptr->copy_from(input_ptr);
368                         break;
370                 case 2:
371 // Save one field for next frame.  Reuse previous frame.
372                         temp_frame[1]->copy_from(input_ptr);
373                         output_ptr->copy_from(temp_frame[0]);
374                         break;
376                 case 3:
377 // Combine previous field with current field.
378                         for(int i = 0; i < input_ptr->get_h(); i++)
379                         {
380                                 if((i + config.first_field) & 1)
381                                         memcpy(output_ptr->get_rows()[i], 
382                                                 input_ptr->get_rows()[i],
383                                                 row_size);
384                                 else
385                                         memcpy(output_ptr->get_rows()[i], 
386                                                 temp_frame[1]->get_rows()[i],
387                                                 row_size);
388                         }
389                         break;
390         }
391 //printf("IVTCMain::process_realtime 2\n");
393         return 0;
396 void IVTCMain::update_gui()
398         if(thread)
399         {
400                 load_configuration();
401                 thread->window->lock_window();
402                 thread->window->frame_offset->update((int64_t)config.frame_offset);
403                 thread->window->first_field->update(config.first_field);
404                 thread->window->automatic->update(config.automatic);
405                 thread->window->pattern[0]->update(config.pattern == 0);
406                 thread->window->pattern[1]->update(config.pattern == 1);
407                 thread->window->unlock_window();
408         }
414 IVTCEngine::IVTCEngine(IVTCMain *plugin, int start_y, int end_y)
415  : Thread()
417         this->plugin = plugin;
418         set_synchronous(1);
419         this->start_y = start_y;
420         this->end_y = end_y;
421         input_lock.lock();
422         output_lock.lock();
423         last_frame = 0;
426 IVTCEngine::~IVTCEngine()
431 // Use all channels to get more info
432 #define COMPARE_ROWS(result, row1, row2, type, width, components) \
433 { \
434         for(int i = 0; i < width * components; i++) \
435         { \
436                 result += labs(((type*)row1)[i] - ((type*)row2)[i]); \
437         } \
440 #define COMPARE_FIELDS(rows1, rows2, type, width, height, components) \
441 { \
442         int w = width * components; \
443         int h = height; \
444          \
445         for(int i = 0; i < h; i++) \
446         { \
447                 type *row1 = (type*)(rows1)[i]; \
448                 type *row2 = (type*)(rows2)[i]; \
450                 if(i & 1) \
451                         for(int j = 0; j < w; j++) \
452                         { \
453                                 field2 += labs(row1[j] - row2[j]); \
454                         } \
455                 else \
456                         for(int j = 0; j < w; j++) \
457                         { \
458                                 field1 += labs(row1[j] - row2[j]); \
459                         } \
460         } \
463 #define COMPARE_FIELDS_YUV(rows1, rows2, type, width, height, components) \
464 { \
465         int w = width * components; \
466         int h = height; \
467          \
468         for(int i = 0; i < h; i++) \
469         { \
470                 type *row1 = (type*)(rows1)[i]; \
471                 type *row2 = (type*)(rows2)[i]; \
473                 if(i & 1) \
474                         for(int j = 0; j < w; j += components) \
475                         { \
476                                 field2 += labs(row1[j] - row2[j]); \
477                         } \
478                 else \
479                         for(int j = 0; j < w; j += components) \
480                         { \
481                                 field1 += labs(row1[j] - row2[j]); \
482                         } \
483         } \
487 void IVTCEngine::run()
489         while(1)
490         {
491                 input_lock.lock();
492                 if(last_frame)
493                 {
494                         output_lock.unlock();
495                         return;
496                 }
497                 
498                 field1 = 0;
499                 field2 = 0;
500                 switch(input->get_color_model())
501                 {
502                         case BC_RGB888:
503                                 COMPARE_FIELDS(input->get_rows() + start_y, 
504                                         output->get_rows() + start_y, 
505                                         unsigned char, 
506                                         input->get_w(),
507                                         end_y - start_y, 
508                                         3);
509                                 break;
510                         case BC_YUV888:
511                                 COMPARE_FIELDS_YUV(input->get_rows() + start_y, 
512                                         output->get_rows() + start_y, 
513                                         unsigned char, 
514                                         input->get_w(),
515                                         end_y - start_y, 
516                                         3);
517                                 break;
518                         case BC_RGBA8888:
519                                 COMPARE_FIELDS(input->get_rows() + start_y, 
520                                         output->get_rows() + start_y, 
521                                         unsigned char, 
522                                         input->get_w(),
523                                         end_y - start_y, 
524                                         4);
525                                         break;
526                         case BC_YUVA8888:
527                                 COMPARE_FIELDS_YUV(input->get_rows() + start_y, 
528                                         output->get_rows() + start_y, 
529                                         unsigned char, 
530                                         input->get_w(),
531                                         end_y - start_y, 
532                                         4);
533                                         break;
534                         case BC_RGB161616:
535                                 COMPARE_FIELDS(input->get_rows() + start_y, 
536                                         output->get_rows() + start_y, 
537                                         u_int16_t, 
538                                         input->get_w(),
539                                         end_y - start_y, 
540                                         3);
541                                 break;
542                         case BC_YUV161616:
543                                 COMPARE_FIELDS_YUV(input->get_rows() + start_y, 
544                                         output->get_rows() + start_y, 
545                                         u_int16_t, 
546                                         input->get_w(),
547                                         end_y - start_y, 
548                                         3);
549                                 break;
550                         case BC_RGBA16161616:
551                                 COMPARE_FIELDS(input->get_rows() + start_y, 
552                                         output->get_rows() + start_y, 
553                                         u_int16_t, 
554                                         input->get_w(),
555                                         end_y - start_y, 
556                                         4);
557                                 break;
558                         case BC_YUVA16161616:
559                                 COMPARE_FIELDS_YUV(input->get_rows() + start_y, 
560                                         output->get_rows() + start_y, 
561                                         u_int16_t, 
562                                         input->get_w(),
563                                         end_y - start_y, 
564                                         4);
565                                 break;
566                 }
567                 output_lock.unlock();
568         }
572 int IVTCEngine::start_process_frame(VFrame *output, VFrame *input)
574         this->output = output;
575         this->input = input;
576         input_lock.unlock();
577         return 0;
580 int IVTCEngine::wait_process_frame()
582         output_lock.lock();
583         return 0;