r286: Heroine Virutal's official release 1.2.0
[cinelerra_cv/ct.git] / hvirtual / plugins / motion / motion.C
blob4cbb4122dff7888f77932b7d7517330cca85d758
1 #include "bcdisplayinfo.h"
2 #include "clip.h"
3 #include "filexml.h"
4 #include "keyframe.h"
5 #include "motion.h"
6 #include "overlayframe.h"
7 #include "picon_png.h"
10 #include <libintl.h>
11 #define _(String) gettext(String)
12 #define gettext_noop(String) String
13 #define N_(String) gettext_noop (String)
19 REGISTER_PLUGIN(MotionMain)
23 MotionConfig::MotionConfig()
25         search_radius = 32;
26         block_size = 16;
27         magnitude = 128;
28         return_speed = 128;
29         mode = STABILIZE;
32 int MotionConfig::equivalent(MotionConfig &that)
34         return search_radius == that.search_radius &&
35                 mode == that.mode &&
36                 block_size == that.block_size &&
37                 magnitude == that.magnitude &&
38                 return_speed == that.return_speed;
41 void MotionConfig::copy_from(MotionConfig &that)
43         search_radius = that.search_radius;
44         mode = that.mode;
45         block_size = that.block_size;
46         magnitude = that.magnitude;
47         return_speed = that.return_speed;
50 void MotionConfig::interpolate(MotionConfig &prev, 
51         MotionConfig &next, 
52         int64_t prev_frame, 
53         int64_t next_frame, 
54         int64_t current_frame)
56         copy_from(prev);
67 PLUGIN_THREAD_OBJECT(MotionMain, MotionThread, MotionWindow)
71 MotionWindow::MotionWindow(MotionMain *plugin, int x, int y)
72  : BC_Window(plugin->gui_string, 
73         x,
74         y,
75         290, 
76         250, 
77         290,
78         250,
79         0, 
80         1)
82         this->plugin = plugin; 
85 MotionWindow::~MotionWindow()
89 int MotionWindow::create_objects()
91         int x = 10, y = 10;
93         add_subwindow(new BC_Title(x, y, _("Search radius:")));
94         add_subwindow(radius = new MotionSearchRadius(plugin, 
95                 x + 190, 
96                 y));
98         y += 30;
99         add_subwindow(new BC_Title(x, y, _("Block size:")));
100         add_subwindow(block_size = new MotionBlockSize(plugin, 
101                 x + 230, 
102                 y));
104         y += 30;
105         add_subwindow(new BC_Title(x, y, _("Maximum absolute offset:")));
106         add_subwindow(magnitude = new MotionMagnitude(plugin, 
107                 x + 190, 
108                 y));
110         y += 30;
111         add_subwindow(new BC_Title(x, y, _("Stabilizer settling time:")));
112         add_subwindow(return_speed = new MotionReturnSpeed(plugin,
113                 x + 230, 
114                 y));
116         y += 30;
117         add_subwindow(new BC_Title(x, y, _("Mode:")));
118         y += 20;
119         add_subwindow(track = new MotionTrack(plugin, 
120                 this,
121                 x, 
122                 y));
123         y += 30;
124         add_subwindow(stabilize = new MotionStabilize(plugin, 
125                 this,
126                 x, 
127                 y));
129         y += 30;
130         add_subwindow(vectors = new MotionDrawVectors(plugin,
131                 this,
132                 x,
133                 y));
135         show_window();
136         flush();
137         return 0;
140 void MotionWindow::update_mode()
142         stabilize->update(plugin->config.mode == MotionConfig::STABILIZE);
143         track->update(plugin->config.mode == MotionConfig::TRACK);
144         vectors->update(plugin->config.mode == MotionConfig::DRAW_VECTORS);
148 WINDOW_CLOSE_EVENT(MotionWindow)
159 MotionSearchRadius::MotionSearchRadius(MotionMain *plugin, 
160         int x, 
161         int y)
162  : BC_IPot(x, 
163                 y, 
164                 (int64_t)plugin->config.search_radius,
165                 (int64_t)0,
166                 (int64_t)128)
168         this->plugin = plugin;
172 int MotionSearchRadius::handle_event()
174         plugin->config.search_radius = get_value();
175         plugin->send_configure_change();
176         return 1;
185 MotionBlockSize::MotionBlockSize(MotionMain *plugin, 
186         int x, 
187         int y)
188  : BC_IPot(x, 
189                 y, 
190                 (int64_t)plugin->config.block_size,
191                 (int64_t)0,
192                 (int64_t)128)
194         this->plugin = plugin;
197 int MotionBlockSize::handle_event()
199         plugin->config.block_size =get_value();
200         plugin->send_configure_change();
201         return 1;
205 MotionMagnitude::MotionMagnitude(MotionMain *plugin, 
206         int x, 
207         int y)
208  : BC_IPot(x, 
209                 y, 
210                 (int64_t)plugin->config.magnitude,
211                 (int64_t)0,
212                 (int64_t)480)
214         this->plugin = plugin;
217 int MotionMagnitude::handle_event()
219         plugin->config.magnitude = get_value();
220         plugin->send_configure_change();
221         return 1;
225 MotionReturnSpeed::MotionReturnSpeed(MotionMain *plugin, 
226         int x, 
227         int y)
228  : BC_IPot(x, 
229                 y, 
230                 (int64_t)plugin->config.return_speed,
231                 (int64_t)0,
232                 (int64_t)128)
234         this->plugin = plugin;
237 int MotionReturnSpeed::handle_event()
239         plugin->config.return_speed = get_value();
240         plugin->send_configure_change();
241         return 1;
249 MotionStabilize::MotionStabilize(MotionMain *plugin, 
250         MotionWindow *gui,
251         int x, 
252         int y)
253  : BC_Radial(x, 
254         y, 
255         plugin->config.mode == MotionConfig::STABILIZE,
256         _("Stabilize"))
258         this->gui = gui;
259         this->plugin = plugin;
262 int MotionStabilize::handle_event()
264         plugin->config.mode = MotionConfig::STABILIZE;
265         gui->update_mode();
266         plugin->send_configure_change();
267         return 1;
274 MotionTrack::MotionTrack(MotionMain *plugin, 
275         MotionWindow *gui,
276         int x, 
277         int y)
278  : BC_Radial(x,
279         y, 
280         plugin->config.mode == MotionConfig::TRACK,
281         _("Track"))
283         this->gui = gui;
284         this->plugin = plugin;
287 int MotionTrack::handle_event()
289         plugin->config.mode = MotionConfig::TRACK;
290         gui->update_mode();
291         plugin->send_configure_change();
292         return 1;
299 MotionDrawVectors::MotionDrawVectors(MotionMain *plugin, 
300         MotionWindow *gui,
301         int x, 
302         int y)
303  : BC_Radial(x,
304         y, 
305         plugin->config.mode == MotionConfig::DRAW_VECTORS,
306         _("Draw vectors"))
308         this->gui = gui;
309         this->plugin = plugin;
312 int MotionDrawVectors::handle_event()
314         plugin->config.mode = MotionConfig::DRAW_VECTORS;
315         gui->update_mode();
316         plugin->send_configure_change();
317         return 1;
331 MotionMain::MotionMain(PluginServer *server)
332  : PluginVClient(server)
334         PLUGIN_CONSTRUCTOR_MACRO
335         engine = 0;
336         prev_frame = 0;
337         macroblocks = 0;
338         total_macroblocks = 0;
339         current_dx = 0;
340         current_dy = 0;
341         overlayer = 0;
342         search_area = 0;
343         search_size = 0;
344         temp_frame = 0;
347 MotionMain::~MotionMain()
349         PLUGIN_DESTRUCTOR_MACRO
350         if(engine) delete engine;
351         if(prev_frame) delete prev_frame;
352         if(macroblocks) delete [] macroblocks;
353         if(overlayer) delete overlayer;
354         if(search_area) delete [] search_area;
355         if(temp_frame) delete temp_frame;
358 char* MotionMain::plugin_title() { return N_("Motion"); }
359 int MotionMain::is_realtime() { return 1; }
360 int MotionMain::is_multichannel() { return 1; }
362 NEW_PICON_MACRO(MotionMain)
364 SHOW_GUI_MACRO(MotionMain, MotionThread)
366 SET_STRING_MACRO(MotionMain)
368 RAISE_WINDOW_MACRO(MotionMain)
370 LOAD_CONFIGURATION_MACRO(MotionMain, MotionConfig)
374 void MotionMain::update_gui()
376         if(thread)
377         {
378                 thread->window->lock_window();
379                 load_configuration();
380                 thread->window->radius->update(config.search_radius);
381                 thread->window->block_size->update(config.block_size);
382                 thread->window->magnitude->update(config.magnitude);
383                 thread->window->return_speed->update(config.return_speed);
384                 thread->window->update_mode();
385                 thread->window->unlock_window();
386         }
390 int MotionMain::load_defaults()
392         char directory[1024], string[1024];
393 // set the default directory
394         sprintf(directory, "%smotion.rc", BCASTDIR);
396 // load the defaults
397         defaults = new Defaults(directory);
398         defaults->load();
400         config.block_size = defaults->get("BLOCK_SIZE", config.block_size);
401         config.search_radius = defaults->get("SEARCH_RADIUS", config.search_radius);
402         config.magnitude = defaults->get("MAGNITUDE", config.magnitude);
403         config.return_speed = defaults->get("RETURN_SPEED", config.return_speed);
404         config.mode = defaults->get("MODE", config.mode);
405         return 0;
409 int MotionMain::save_defaults()
411         defaults->update("BLOCK_SIZE", config.block_size);
412         defaults->update("SEARCH_RADIUS", config.search_radius);
413         defaults->update("MAGNITUDE", config.magnitude);
414         defaults->update("RETURN_SPEED", config.return_speed);
415         defaults->update("MODE", config.mode);
416         defaults->save();
417         return 0;
422 void MotionMain::save_data(KeyFrame *keyframe)
424         FileXML output;
426 // cause data to be stored directly in text
427         output.set_shared_string(keyframe->data, MESSAGESIZE);
428         output.tag.set_title("MOTION");
430         output.tag.set_property("BLOCK_SIZE", config.block_size);
431         output.tag.set_property("SEARCH_RADIUS", config.search_radius);
432         output.tag.set_property("MAGNITUDE", config.magnitude);
433         output.tag.set_property("RETURN_SPEED", config.return_speed);
434         output.tag.set_property("MODE", config.mode);
435         output.append_tag();
436         output.terminate_string();
439 void MotionMain::read_data(KeyFrame *keyframe)
441         FileXML input;
443         input.set_shared_string(keyframe->data, strlen(keyframe->data));
445         int result = 0;
447         while(!result)
448         {
449                 result = input.read_tag();
451                 if(!result)
452                 {
453                         if(input.tag.title_is("MOTION"))
454                         {
455                                 config.block_size = input.tag.get_property("BLOCK_SIZE", config.block_size);
456                                 config.search_radius = input.tag.get_property("SEARCH_RADIUS", config.search_radius);
457                                 config.magnitude = input.tag.get_property("MAGNITUDE", config.magnitude);
458                                 config.return_speed = input.tag.get_property("RETURN_SPEED", config.return_speed);
459                                 config.mode = input.tag.get_property("MODE", config.mode);
460                         }
461                 }
462         }
468 void MotionMain::oversample(int32_t *dst, 
469         VFrame *src,
470         int x1,
471         int x2,
472         int y1,
473         int y2,
474         int dst_stride)
477 #define DO_OVERSAMPLE(components, type) \
478 { \
479         int oversample_w = (x2 - x1) * OVERSAMPLE; \
480         int oversample_h = (y2 - y1) * OVERSAMPLE; \
481         type **src_rows = (type**)src->get_rows(); \
483         for(int i = 0; i < oversample_h; i++) \
484         { \
485                 int row2_factor = i % OVERSAMPLE; \
486                 int row1_factor = OVERSAMPLE - row2_factor; \
487                 int row1_number = i / OVERSAMPLE + y1; \
488                 int row2_number = row1_number + 1; \
489                 row2_number = MIN(row2_number, y2 - 1); \
491                 type *row1 = src_rows[row1_number]; \
492                 type *row2 = src_rows[row2_number]; \
493                 int32_t *dst_row = dst + i * dst_stride; \
495                 for(int j = 0; j < oversample_w; j++) \
496                 { \
497                         int pixel2_factor = j % OVERSAMPLE; \
498                         int pixel1_factor = OVERSAMPLE - pixel2_factor; \
499                         int pixel1_number = j / OVERSAMPLE + x1; \
500                         int pixel2_number = pixel1_number + 1; \
501                         pixel2_number = MIN(pixel2_number, x2 - 1); \
503                         type *pixel1 = row1 + pixel1_number * components; \
504                         type *pixel2 = row1 + pixel2_number * components; \
505                         type *pixel3 = row2 + pixel1_number * components; \
506                         type *pixel4 = row2 + pixel2_number * components; \
507                         int32_t dst_pixel = 0; \
508                         for(int k = 0; k < components; k++) \
509                         { \
510                                 dst_pixel += *pixel1++ * pixel1_factor * row1_factor; \
511                                 dst_pixel += *pixel2++ * pixel2_factor * row1_factor; \
512                                 dst_pixel += *pixel3++ * pixel1_factor * row2_factor; \
513                                 dst_pixel += *pixel4++ * pixel2_factor * row2_factor; \
514                         } \
515                         *dst_row++ = dst_pixel; \
516                 } \
517         } \
520         switch(src->get_color_model())
521         {
522                 case BC_RGB888:
523                 case BC_YUV888:
524                         DO_OVERSAMPLE(3, unsigned char);
525                         break;
526                 case BC_RGB161616:
527                 case BC_YUV161616:
528                         DO_OVERSAMPLE(3, uint16_t);
529                         break;
530                 case BC_RGBA8888:
531                 case BC_YUVA8888:
532                         DO_OVERSAMPLE(4, unsigned char);
533                         break;
534                 case BC_RGBA16161616:
535                 case BC_YUVA16161616:
536                         DO_OVERSAMPLE(4, uint16_t);
537                         break;
538         }
542 void MotionMain::draw_pixel(VFrame *frame, int x, int y)
544         if(!(x >= 0 && y >= 0 && x < frame->get_w() && y < frame->get_h())) return;
545 #define DRAW_PIXEL(x, y, components, do_yuv, max, type) \
546 { \
547         type **rows = (type**)frame->get_rows(); \
548         rows[y][x * components] = max; \
549         if(!do_yuv) \
550         { \
551                 rows[y][x * components + 1] = max; \
552                 rows[y][x * components + 2] = max; \
553         } \
554         if(components == 4) \
555                 rows[y][x * components + 3] = max; \
559         switch(frame->get_color_model())
560         {
561                 case BC_RGB888:
562                         DRAW_PIXEL(x, y, 3, 0, 0xff, unsigned char);
563                         break;
564                 case BC_YUV888:
565                         DRAW_PIXEL(x, y, 3, 1, 0xff, unsigned char);
566                         break;
567                 case BC_RGBA8888:
568                         DRAW_PIXEL(x, y, 4, 0, 0xff, unsigned char);
569                         break;
570                 case BC_YUVA8888:
571                         DRAW_PIXEL(x, y, 4, 1, 0xff, unsigned char);
572                         break;
573                 case BC_RGB161616:
574                         DRAW_PIXEL(x, y, 3, 0, 0xffff, uint16_t);
575                         break;
576                 case BC_YUV161616:
577                         DRAW_PIXEL(x, y, 3, 1, 0xffff, uint16_t);
578                         break;
579                 case BC_RGBA16161616:
580                         DRAW_PIXEL(x, y, 4, 0, 0xffff, uint16_t);
581                         break;
582                 case BC_YUVA16161616:
583                         DRAW_PIXEL(x, y, 4, 1, 0xffff, uint16_t);
584                         break;
585         }
589 void MotionMain::draw_line(VFrame *frame, int x1, int y1, int x2, int y2)
591         int w = labs(x2 - x1);
592         int h = labs(y2 - y1);
593 //printf("MotionMain::draw_line %d %d %d %d\n", x1, y1, x2, y2);
595         if(!w && !h)
596         {
597                 draw_pixel(frame, x1, y1);
598         }
599         else
600         if(w > h)
601         {
602 // Flip coordinates so x1 < x2
603                 if(x2 < x1)
604                 {
605                         y2 ^= y1;
606                         y1 ^= y2;
607                         y2 ^= y1;
608                         x1 ^= x2;
609                         x2 ^= x1;
610                         x1 ^= x2;
611                 }
612                 int numerator = y2 - y1;
613                 int denominator = x2 - x1;
614                 for(int i = x1; i < x2; i++)
615                 {
616                         int y = y1 + (i - x1) * numerator / denominator;
617                         draw_pixel(frame, i, y);
618                 }
619         }
620         else
621         {
622 // Flip coordinates so y1 < y2
623                 if(y2 < y1)
624                 {
625                         y2 ^= y1;
626                         y1 ^= y2;
627                         y2 ^= y1;
628                         x1 ^= x2;
629                         x2 ^= x1;
630                         x1 ^= x2;
631                 }
632                 int numerator = x2 - x1;
633                 int denominator = y2 - y1;
634                 for(int i = y1; i < y2; i++)
635                 {
636                         int x = x1 + (i - y1) * numerator / denominator;
637                         draw_pixel(frame, x, i);
638                 }
639         }
642 int MotionMain::process_realtime(VFrame **input_ptr, VFrame **output_ptr)
644         int need_reconfigure = load_configuration();
645         int bottom_layer = PluginClient::total_in_buffers - 1;
646         current_frame = input_ptr[bottom_layer];
647         int w = current_frame->get_w();
648         int h = current_frame->get_h();
649         int color_model = current_frame->get_color_model();
650         int skip_current = 0;
651         VFrame *input = input_ptr[0];
652         VFrame *output = output_ptr[0];
654 printf("MotionMain::process_realtime 1\n");
656         if(!temp_frame)
657                 temp_frame = new VFrame(0, 
658                         input->get_w(), 
659                         input->get_h(), 
660                         input->get_color_model());
663         if(!engine) engine = new MotionEngine(this,
664                 PluginClient::get_project_smp() + 1,
665                 PluginClient::get_project_smp() + 1);
667         if(need_reconfigure) skip_current = 1;
668         if(!prev_frame)
669         {
670                 prev_frame = new VFrame(0, w, h, color_model);
671                 skip_current = 1;
672         }
674         int new_total = (w / config.block_size) * (h / config.block_size);
675         if(total_macroblocks != new_total) 
676         {
677                 delete [] macroblocks;
678                 macroblocks = 0;
679         }
681         if(!macroblocks)
682         {
683                 total_macroblocks = new_total;
684                 macroblocks = new macroblock_t[total_macroblocks];
685         }
686         bzero(macroblocks, sizeof(macroblock_t) * total_macroblocks);
687         int per_row = w / config.block_size;
688         for(int i = 0; i < total_macroblocks; i++)
689         {
690                 macroblock_t *macroblock = &macroblocks[i];
691                 macroblock->x = config.block_size * (i % per_row);
692                 macroblock->y = config.block_size * (i / per_row);
693         }
696 // Skip processing
697         if(skip_current)
698         {
699                 prev_frame->copy_from(input_ptr[PluginClient::total_in_buffers - 1]);
700                 for(int i = 0; i < PluginClient::total_in_buffers; i++)
701                 {
702                         if(!output_ptr[i]->equals(input_ptr[i]))
703                                 output_ptr[i]->copy_from(input_ptr[i]);
704                 }
705         }
706         else
707         {
708 // Create oversampled buffers
709                 int new_search_size = current_frame->get_w() * 
710                         OVERSAMPLE * 
711                         current_frame->get_h() * 
712                         OVERSAMPLE;
713                 if(new_search_size != search_size && search_area)
714                 {
715                         delete [] search_area;
716                         search_area = 0;
717                 }
719                 if(!search_area)
720                 {
721                         search_area = new int32_t[new_search_size];
722                         search_size = new_search_size;
723                 }
725                 MotionMain::oversample(search_area, 
726                         current_frame,
727                         0,
728                         current_frame->get_w(),
729                         0,
730                         current_frame->get_h(),
731                         current_frame->get_w() * OVERSAMPLE);
733 // Get the motion vectors
734 //printf("MotionMain::process_realtime 1\n");
735                 engine->set_package_count(total_macroblocks);
736 //printf("MotionMain::process_realtime 1\n");
737                 engine->process_packages();
738 //printf("MotionMain::process_realtime 1\n");
740 // Put the current frame in the temporary for the next run
741                 prev_frame->copy_from(current_frame);
743 //printf("MotionMain::process_realtime 1\n");
744 // Get average of all motion vectors
745                 int avg_dy = 0;
746                 int avg_dx = 0;
747                 int total_valid = 0;
748                 for(int i = 0; i < total_macroblocks; i++)
749                 {
750                         if(macroblocks[i].valid)
751                         {
752                                 avg_dy += macroblocks[i].dy_oversampled;
753                                 avg_dx += macroblocks[i].dx_oversampled;
754                                 total_valid++;
755                         }
756                 }
758 //printf("MotionMain::process_realtime 1 %d %d %d\n", avg_dx, avg_dy, total_valid);
759                 if(total_valid)
760                 {
761                         avg_dx /= total_valid;
762                         avg_dy /= total_valid;
763                 }
765                 if(!overlayer) overlayer = new OverlayFrame(
766                         PluginClient::get_project_smp() + 1);
767                 if(config.mode == MotionConfig::TRACK)
768                 {
769 // Match top layer to bottom layer's motion
770                         current_dx += avg_dx;
771                         current_dy += avg_dy;
773 // Clamp to absolute limit
774                         CLAMP(current_dx, -config.magnitude * OVERSAMPLE, config.magnitude * OVERSAMPLE);
775                         CLAMP(current_dy, -config.magnitude * OVERSAMPLE, config.magnitude * OVERSAMPLE);
777 // Translate top layer
778                         temp_frame->copy_from(input);
779                         overlayer->overlay(output, 
780                                 temp_frame, 
781                                 0, 
782                                 0, 
783                                 input->get_w(), 
784                                 input->get_h(), 
785                                 (float)current_dx / OVERSAMPLE, 
786                                 (float)current_dy / OVERSAMPLE, 
787                                 output->get_w() + (float)current_dx / OVERSAMPLE, 
788                                 output->get_h() + (float)current_dy / OVERSAMPLE, 
789                                 1.0, 
790                                 TRANSFER_REPLACE,
791                                 CUBIC_LINEAR);
792 printf("MotionMain::process_realtime 10 %d %d\n", current_dx, current_dy);
793                 }
794                 else
795                 if(config.mode == MotionConfig::STABILIZE)
796                 {
797 // Move top layer opposite to bottom layer's motion
798                         current_dx -= avg_dx;
799                         current_dy -= avg_dy;
801 // Clamp to absolute limit
802                         CLAMP(current_dx, -config.magnitude * OVERSAMPLE, config.magnitude * OVERSAMPLE);
803                         CLAMP(current_dy, -config.magnitude * OVERSAMPLE, config.magnitude * OVERSAMPLE);
804 printf("MotionMain::process_realtime 100 %d %d %d %d\n", avg_dx, avg_dy, current_dx, current_dy);
806 // for(int i = 0; i < output->get_h(); i++)
807 // {
808 //      for(int j = 0; j < output->get_w(); j++)
809 //      {
810 //              output->get_rows()[i][j * 3] = search_area[(i * 4) * w * OVERSAMPLE + j * 4] >> 4;
811 //              output->get_rows()[i][j * 3 + 1] = search_area[(i * 4) * w * OVERSAMPLE + j * 4] >> 4;
812 //              output->get_rows()[i][j * 3 + 2] = search_area[(i * 4) * w * OVERSAMPLE + j * 4] >> 4;
813 //      }
814 // }
816 // Translate top layer
817                         temp_frame->copy_from(input);
818                         overlayer->overlay(output, 
819                                 temp_frame, 
820                                 0, 
821                                 0, 
822                                 input->get_w(), 
823                                 input->get_h(), 
824                                 (float)current_dx / OVERSAMPLE, 
825                                 (float)current_dy / OVERSAMPLE, 
826                                 output->get_w() + (float)current_dx / OVERSAMPLE, 
827                                 output->get_h() + (float)current_dy / OVERSAMPLE, 
828                                 1.0, 
829                                 TRANSFER_REPLACE,
830                                 CUBIC_LINEAR);
831                 }
832                 else
833                 if(config.mode == MotionConfig::DRAW_VECTORS)
834                 {
835                         if(!output->equals(input))
836                                 output->copy_from(input);
837 printf("MotionMain::process_realtime 110 %d %d %d %d\n", avg_dx, avg_dy, current_dx, current_dy);
838                         for(int i = 0; i < total_macroblocks; i++)
839                         {
840                                 if(macroblocks[i].valid)
841                                 {
842                                         int x1 = macroblocks[i].x + config.block_size / 2;
843                                         int y1 = macroblocks[i].y + config.block_size / 2;
844                                         int x2 = x1 + macroblocks[i].dx_oversampled / OVERSAMPLE;
845                                         int y2 = y1 + macroblocks[i].dy_oversampled / OVERSAMPLE;
846 //printf("MotionMain::process_realtime 10 %d %d %d %d\n", x1, y1, x2, y2);
847                                         draw_line(output, 
848                                                 x1,
849                                                 y1,
850                                                 x2,
851                                                 y2);
852                                 }
853                         }
854                 }
855         }
857         return 1;
871 MotionPackage::MotionPackage()
872  : LoadPackage()
879 MotionUnit::MotionUnit(MotionEngine *server, 
880         MotionMain *plugin)
881  : LoadClient(server)
883         this->plugin = plugin;
884         this->server = server;
885         block_area = 0;
886         block_size = 0;
887         scan_result = 0;
888         scan_result_size = 0;
891 MotionUnit::~MotionUnit()
893         if(block_area) delete [] block_area;
894         if(scan_result) delete [] scan_result;
899 int64_t MotionUnit::abs_diff(int32_t *search_pixel,
900         int32_t *block_pixel,
901         int search_side,
902         int block_side)
904         int64_t result = 0;
905         for(int i = 0; i < block_side; i++)
906         {
907                 for(int j = 0; j < block_side; j++)
908                 {
909                         int64_t difference;
910                         difference = search_pixel[j] - block_pixel[j];
911                         if(difference < 0)
912                                 result -= difference;
913                         else
914                                 result += difference;
915                 }
916                 search_pixel += search_side;
917                 block_pixel += block_side;
918         }
919         return result;
922 void MotionUnit::process_package(LoadPackage *package)
924         MotionPackage *pkg = (MotionPackage*)package;
925         macroblock_t *macroblock = pkg->macroblock;
926         int w = plugin->current_frame->get_w();
927         int h = plugin->current_frame->get_h();
928         int search_w = plugin->config.search_radius * 2;
929         int search_h = plugin->config.search_radius * 2;
930         int block_w = plugin->config.block_size;
931         int block_h = plugin->config.block_size; 
932         int new_block_size = block_w * OVERSAMPLE * block_h * OVERSAMPLE;
935         if(new_block_size != block_size && block_area)
936         {
937                 delete [] block_area;
938                 block_area = 0;
939         }
941         if(!block_area)
942         {
943                 block_area = new int32_t[new_block_size];
944                 block_size = new_block_size;
945         }
947         int new_result_size = search_w * OVERSAMPLE * search_h * OVERSAMPLE;
948         if(new_result_size != scan_result_size && scan_result)
949         {
950                 delete [] scan_result;
951                 scan_result = 0;
952         }
953         if(!scan_result)
954         {
955                 scan_result = new int64_t[new_result_size];
956                 scan_result_size = new_result_size;
957         }
959         for(int i = 0; i < scan_result_size; i++)
960                 scan_result[i] = -1;
962 // Get coordinates in current frame for search data
963         int search_x1 = macroblock->x + 
964                 plugin->config.block_size / 2 - 
965                 plugin->config.search_radius;
966         int search_x2 = search_x1 + 
967                 search_w - 
968                 plugin->config.block_size;
969         int search_y1 = macroblock->y + 
970                 plugin->config.block_size / 2 - 
971                 plugin->config.search_radius;
972         int search_y2 = search_y1 + 
973                 search_h - 
974                 plugin->config.block_size;
975         search_x1 = MAX(search_x1, 0);
976         search_x2 = MIN(search_x2, w);
977         search_y1 = MAX(search_y1, 0);
978         search_y2 = MIN(search_y2, h);
979         int search_w_oversampled = (search_x2 - search_x1) * OVERSAMPLE;
980         int search_h_oversampled = (search_y2 - search_y1) * OVERSAMPLE;
981         int block_side_oversampled = plugin->config.block_size * OVERSAMPLE;
982 //printf("MotionUnit::process_package 1\n");
984 // Transfer images to temporaries with oversampling
986         MotionMain::oversample(block_area,
987                 plugin->prev_frame,
988                 macroblock->x,
989                 macroblock->x + plugin->config.block_size,
990                 macroblock->y,
991                 macroblock->y + plugin->config.block_size,
992                 block_side_oversampled);
995         int64_t least_difference = -1;
996         int64_t most_difference = -1;
997         int least_x = (macroblock->x - search_x1) * OVERSAMPLE;
998         int least_y = (macroblock->y - search_y1) * OVERSAMPLE;
1002 #if 0
1003 // Zigzag motion estimation in the oversampled area.
1004 // Not practical
1005         for(int i = 0; i < search_h_oversampled - block_side_oversampled; i++)
1006         {
1007                 for(int j = 0; j < search_w_oversampled - block_side_oversampled; j++)
1008                 {
1009                         int32_t *search_pixel = search_area + 
1010                                 i * search_w_oversampled +
1011                                 j;
1012                         int32_t *block_pixel = block_area;
1013                         int64_t current_difference = abs_diff(search_pixel, 
1014                                 block_pixel,
1015                                 search_w_oversampled,
1016                                 block_side_oversampled);
1017                         if(least_difference < 0 || current_difference < least_difference)
1018                         {
1019 //printf("MotionUnit::process_package %lld %d %d\n", current_difference, j, i);
1020                                 least_difference = current_difference;
1021                                 least_x = j;
1022                                 least_y = i;
1023                         }
1024                 }
1025         }
1026 #endif
1029 // Log motion estimation in the oversampled area
1030 // Get motion estimation range
1031         int scan_x1 = search_x1 * OVERSAMPLE;
1032         int scan_x2 = search_x2 * OVERSAMPLE - block_side_oversampled;
1033         int scan_y1 = search_y1 * OVERSAMPLE;
1034         int scan_y2 = search_y2 * OVERSAMPLE - block_side_oversampled;
1035 #define INIT_SCAN_STEPS 4
1036         int scan_step_x = block_side_oversampled / INIT_SCAN_STEPS;
1037         int scan_step_y = block_side_oversampled / INIT_SCAN_STEPS;
1038         if(scan_step_x > (scan_x2 - scan_x1) / INIT_SCAN_STEPS) scan_step_x = (scan_x2 - scan_x1) / INIT_SCAN_STEPS;
1039         if(scan_step_y > (scan_y2 - scan_y1) / INIT_SCAN_STEPS) scan_step_y = (scan_y2 - scan_y1) / INIT_SCAN_STEPS;
1040         scan_step_x = MAX(1, scan_step_x);
1041         scan_step_y = MAX(1, scan_step_y);
1043 // Do original position first
1044         int i = macroblock->y * OVERSAMPLE;
1045         int j = macroblock->x * OVERSAMPLE;
1046         int32_t *search_pixel = plugin->search_area + 
1047                 i * OVERSAMPLE * w +
1048                 j;
1049 // Top left of macroblock
1050         int64_t *scan_result_ptr = &scan_result[(i - search_y1 * OVERSAMPLE) * search_w * OVERSAMPLE + j - search_x1 * OVERSAMPLE];
1051         int32_t *block_pixel = block_area;
1052         int64_t current_difference;
1053         least_difference = current_difference = *scan_result_ptr = 
1054                 abs_diff(search_pixel, 
1055                         block_pixel,
1056                         w * OVERSAMPLE,
1057                         block_side_oversampled);
1058         least_x = j;
1059         least_y = i;
1062 // Scan logarithmically
1063         while(1)
1064         {
1066 //printf("MotionUnit::process_package 20 %d %d\n", scan_step_x, scan_step_y);
1067 // Scan top to bottom
1068                 for(int i = scan_y1; i < scan_y2; i += scan_step_y)
1069                 {
1070 // Scan left to right
1071                         for(int j = scan_x1; j < scan_x2; j += scan_step_x)
1072                         {
1073 // printf("MotionUnit::process_package 10 %d %d %d %d\n", 
1074 // j, 
1075 // i,
1076 // search_w_oversampled - block_side_oversampled,
1077 // search_h_oversampled - block_side_oversampled);
1078                                 scan_result_ptr = 
1079                                         &scan_result[(i - search_y1 * OVERSAMPLE) * search_w * OVERSAMPLE + j - search_x1 * OVERSAMPLE];
1080                                 if(*scan_result_ptr >= 0)
1081                                 {
1082                                         current_difference = *scan_result_ptr;
1083                                 }
1084                                 else
1085                                 {
1086 // Top left of search area
1087                                         search_pixel = plugin->search_area + 
1088                                                 i * w * OVERSAMPLE +
1089                                                 j;
1090 // Top left of macroblock
1091                                         block_pixel = block_area;
1092                                         current_difference = abs_diff(search_pixel, 
1093                                                 block_pixel,
1094                                                 w * OVERSAMPLE,
1095                                                 block_side_oversampled);
1096                                         *scan_result_ptr = current_difference;
1097                                 }
1099                                 if(least_difference < 0 || current_difference < least_difference)
1100                                 {
1101 // printf("%d, %d %lld\n", 
1102 // j - macroblock->x * OVERSAMPLE, 
1103 // i - macroblock->y * OVERSAMPLE, 
1104 // current_difference);
1105                                         least_difference = current_difference;
1106                                         least_x = j;
1107                                         least_y = i;
1108                                 }
1110                                 if(most_difference < 0 || current_difference > most_difference)
1111                                         most_difference = current_difference;
1112                         }
1113                 }
1115                 if(scan_step_x == 1 && scan_step_y == 1) break;
1117                 scan_step_x >>= 1;
1118                 scan_step_y >>= 1;
1119                 scan_step_x = MAX(scan_step_x, 1);
1120                 scan_step_y = MAX(scan_step_y, 1);
1122 // Reduce scan range around hot zone
1123                 int new_x_range = (scan_x2 - scan_x1) >> 1;
1124                 int new_y_range = (scan_y2 - scan_y1) >> 1;
1126                 new_x_range = MAX(new_x_range, 1);
1127                 new_y_range = MAX(new_y_range, 1);
1128                 scan_x1 = least_x - new_x_range / 2;
1129                 scan_x2 = scan_x1 + new_x_range;
1130                 scan_y1 = least_y - new_y_range / 2;
1131                 scan_y2 = scan_y1 + new_y_range;
1132                 CLAMP(scan_x1, search_x1 * OVERSAMPLE, search_x2 * OVERSAMPLE - block_side_oversampled);
1133                 CLAMP(scan_y1, search_y1 * OVERSAMPLE, search_y2 * OVERSAMPLE - block_side_oversampled);
1134                 CLAMP(scan_x2, search_x1 * OVERSAMPLE, search_x2 * OVERSAMPLE - block_side_oversampled);
1135                 CLAMP(scan_y2, search_y1 * OVERSAMPLE, search_y2 * OVERSAMPLE - block_side_oversampled);
1136         }
1137 //printf("MotionUnit::process_package 50\n");
1140         if(least_difference < most_difference / 4 && 
1141                 most_difference > block_side_oversampled * block_side_oversampled)
1142         {
1143                 macroblock->dx_oversampled = 
1144                         least_x - 
1145                         macroblock->x * OVERSAMPLE;
1146                 macroblock->dy_oversampled = 
1147                         least_y - 
1148                         macroblock->y * OVERSAMPLE;
1149                 macroblock->valid = 1;
1150         }
1151         else
1152         {
1153                 macroblock->dx_oversampled = 0;
1154                 macroblock->dy_oversampled = 0;
1155         }
1156 // printf("MotionUnit::process_package 100 %lld %lld %d %d %d %d\n",
1157 // least_difference,
1158 // most_difference,
1159 // search_x1,
1160 // search_x2,
1161 // search_y1,
1162 // search_y2);
1171 MotionEngine::MotionEngine(MotionMain *plugin, 
1172         int total_clients,
1173         int total_packages)
1174  : LoadServer(
1175 //1, 1 
1176 total_clients, total_packages 
1179         this->plugin = plugin;
1182 void MotionEngine::init_packages()
1184         for(int i = 0; i < get_total_packages(); i++)
1185         {
1186                 MotionPackage *package = (MotionPackage*)get_package(i);
1187                 package->macroblock = &plugin->macroblocks[i];
1188         }
1191 LoadClient* MotionEngine::new_client()
1193         return new MotionUnit(this, plugin);
1196 LoadPackage* MotionEngine::new_package()
1198         return new MotionPackage;