apply sge motion plugin mods
authorGood Guy <good1.2guy@gmail.com>
Thu, 17 Sep 2020 19:23:08 +0000 (13:23 -0600)
committerGood Guy <good1.2guy@gmail.com>
Thu, 17 Sep 2020 19:23:08 +0000 (13:23 -0600)
cinelerra-5.1/plugins/motion/motion.C
cinelerra-5.1/plugins/motion/motion.h
cinelerra-5.1/plugins/motion/motionscan.C
cinelerra-5.1/plugins/motion/motionscan.h
cinelerra-5.1/plugins/motion/motionwindow.C
cinelerra-5.1/plugins/motion/motionwindow.h
cinelerra-5.1/plugins/motion/opencvwrapper.C [deleted file]
cinelerra-5.1/plugins/motion/opencvwrapper.h [deleted file]
cinelerra-5.1/plugins/motion/opencvwrapper.inc [deleted file]

index b8da2dca0b9ab7a527f9a24c0ddc1ead23e6c92a..d35d4cc553f47e7a6681caef5204c80016ef9b8a 100644 (file)
@@ -56,6 +56,8 @@ MotionConfig::MotionConfig()
        global_block_h = 33; //MIN_BLOCK;
        block_x = 50;
        block_y = 50;
+       noise_level = 0;
+       noise_rotation = 0;
        global_positions = 256;
        rotate_positions = 8; // 4;
        magnitude = 100;
@@ -65,6 +67,7 @@ MotionConfig::MotionConfig()
        action_type = MotionScan::STABILIZE;
        global = 1;
        rotate = 1;
+       twopass = 0;
        addtrackedframeoffset = 0;
        strcpy(tracking_file, TRACKING_FILE);
        tracking_type = MotionScan::SAVE; //MotionScan::NO_CALCULATE;
@@ -96,6 +99,7 @@ int MotionConfig::equivalent(MotionConfig &that)
                rotation_center == that.rotation_center &&
                action_type == that.action_type &&
                global == that.global && rotate == that.rotate &&
+               twopass == that.twopass &&
                addtrackedframeoffset == that.addtrackedframeoffset &&
                draw_vectors == that.draw_vectors &&
                block_count == that.block_count &&
@@ -103,6 +107,8 @@ int MotionConfig::equivalent(MotionConfig &that)
                global_block_h == that.global_block_h &&
                EQUIV(block_x, that.block_x) &&
                EQUIV(block_y, that.block_y) &&
+               noise_level == that.noise_level &&
+               noise_rotation == that.noise_rotation &&
                global_positions == that.global_positions &&
                rotate_positions == that.rotate_positions &&
                magnitude == that.magnitude &&
@@ -125,12 +131,15 @@ void MotionConfig::copy_from(MotionConfig &that)
        action_type = that.action_type;
        global = that.global;
        rotate = that.rotate;
+       twopass = that.twopass;
        addtrackedframeoffset = that.addtrackedframeoffset;
        tracking_type = that.tracking_type;
        draw_vectors = that.draw_vectors;
        block_count = that.block_count;
        block_x = that.block_x;
        block_y = that.block_y;
+       noise_level = that.noise_level;
+       noise_rotation = that.noise_rotation;
        global_positions = that.global_positions;
        rotate_positions = that.rotate_positions;
        global_block_w = that.global_block_w;
@@ -178,6 +187,7 @@ MotionMain::MotionMain(PluginServer *server)
        cache_line[0] = 0;
        cache_key = active_key = -1;
        dx_offset = dy_offset = 0;
+       dt_offset = 0;
        load_ok = 0;
        save_dx = load_dx = 0;
        save_dy = load_dy = 0;
@@ -242,6 +252,10 @@ void MotionMain::update_gui()
        window->block_y->update(config.block_y);
        window->block_x_text->update((float)config.block_x);
        window->block_y_text->update((float)config.block_y);
+       window->noise_level->update(config.noise_level);
+       window->noise_level_text->update((float)config.noise_level);
+       window->noise_rotation->update(config.noise_rotation);
+       window->noise_rotation_text->update((float)config.noise_rotation);
        window->magnitude->update(config.magnitude);
        window->return_speed->update(config.return_speed);
        window->rotate_magnitude->update(config.rotate_magnitude);
@@ -255,9 +269,15 @@ void MotionMain::update_gui()
        window->track_previous->update(config.tracking_object == MotionScan::TRACK_PREVIOUS);
        window->previous_same->update(config.tracking_object == MotionScan::PREVIOUS_SAME_BLOCK);
        if( config.tracking_object != MotionScan::TRACK_SINGLE )
+       {
                window->track_frame_number->disable();
+               window->frame_current->disable();
+       }
        else
+       {
                window->track_frame_number->enable();
+               window->frame_current->enable();
+       }
 
        window->action_type->set_text(
                ActionType::to_text(config.action_type));
@@ -298,9 +318,12 @@ void MotionMain::save_data(KeyFrame *keyframe)
        output.tag.set_property("RETURN_SPEED", config.return_speed);
        output.tag.set_property("ROTATE_MAGNITUDE", config.rotate_magnitude);
        output.tag.set_property("ROTATE_RETURN_SPEED", config.rotate_return_speed);
+       output.tag.set_property("NOISE_LEVEL", config.noise_level);
+       output.tag.set_property("NOISE_ROTATION", config.noise_rotation);
        output.tag.set_property("ACTION_TYPE", config.action_type);
        output.tag.set_property("GLOBAL", config.global);
        output.tag.set_property("ROTATE", config.rotate);
+       output.tag.set_property("TWOPASS", config.twopass);
        output.tag.set_property("ADDTRACKEDFRAMEOFFSET", config.addtrackedframeoffset);
        output.tag.set_property("TRACKING_FILE", config.tracking_file);
        output.tag.set_property("TRACKING_TYPE", config.tracking_type);
@@ -339,9 +362,12 @@ void MotionMain::read_data(KeyFrame *keyframe)
                        config.return_speed = input.tag.get_property("RETURN_SPEED", config.return_speed);
                        config.rotate_magnitude = input.tag.get_property("ROTATE_MAGNITUDE", config.rotate_magnitude);
                        config.rotate_return_speed = input.tag.get_property("ROTATE_RETURN_SPEED", config.rotate_return_speed);
+                       config.noise_level = input.tag.get_property("NOISE_LEVEL", config.noise_level);
+                       config.noise_rotation = input.tag.get_property("NOISE_ROTATION", config.noise_rotation);
                        config.action_type = input.tag.get_property("ACTION_TYPE", config.action_type);
                        config.global = input.tag.get_property("GLOBAL", config.global);
                        config.rotate = input.tag.get_property("ROTATE", config.rotate);
+                       config.twopass = input.tag.get_property("TWOPASS", config.twopass);
                        config.addtrackedframeoffset = input.tag.get_property("ADDTRACKEDFRAMEOFFSET", config.addtrackedframeoffset);
                        input.tag.get_property("TRACKING_FILE", config.tracking_file);
                        config.tracking_type = input.tag.get_property("TRACKING_TYPE", config.tracking_type);
@@ -370,10 +396,12 @@ void MotionMain::allocate_temp(int w, int h, int color_model)
 void MotionMain::process_global()
 {
 
-       if( !engine ) engine = new MotionScan(PluginClient::get_project_smp() + 1,
+       if( !engine ) engine = new MotionScan(this,
+               PluginClient::get_project_smp() + 1,
                PluginClient::get_project_smp() + 1);
 
-// Determine if frames changed
+// Determine if frames changed, either single pass or pass 1
+// Attention, prev_global_ref and current_global_ref are interchanged
        engine->scan_frame(current_global_ref, prev_global_ref,
                config.global_range_w, config.global_range_h,
                config.global_block_w, config.global_block_h,
@@ -382,14 +410,14 @@ void MotionMain::process_global()
                config.action_type, config.horizontal_only,
                config.vertical_only, get_source_position(),
                config.global_positions, total_dx, total_dy,
-               0, 0, load_ok, load_dx, load_dy);
+               0, 0, config.twopass, load_ok, load_dx, load_dy);
        current_dx = (engine->dx_result += dx_offset);
        current_dy = (engine->dy_result += dy_offset);
 
-// Write results
+// Save results
        if( config.tracking_type == MotionScan::SAVE ) {
-               save_dx = engine->dx_result;
-               save_dy = engine->dy_result;
+               save_dx = current_dx;
+               save_dy = current_dy;
        }
 
 // Add current motion vector to accumulation vector.
@@ -410,11 +438,11 @@ void MotionMain::process_global()
 
 // Clamp accumulation vector
        if( config.magnitude < 100 ) {
-               int block_x_orig = (int64_t)(config.block_x * current_global_ref->get_w() / 100);
-               int block_y_orig = (int64_t)(config.block_y * current_global_ref->get_h() / 100);
-               int max_block_x = (int64_t)(current_global_ref->get_w() - block_x_orig)
+               int block_x_orig = lrint(config.block_x * prev_global_ref->get_w() / 100);
+               int block_y_orig = lrint(config.block_y * prev_global_ref->get_h() / 100);
+               int max_block_x = (int64_t)(prev_global_ref->get_w() - block_x_orig)
                        * OVERSAMPLE * config.magnitude / 100;
-               int max_block_y = (int64_t)(current_global_ref->get_h() - block_y_orig)
+               int max_block_y = (int64_t)(prev_global_ref->get_h() - block_y_orig)
                        * OVERSAMPLE * config.magnitude / 100;
                int min_block_x = (int64_t)-block_x_orig
                        * OVERSAMPLE * config.magnitude / 100;
@@ -430,6 +458,186 @@ printf("MotionMain::process_global 2 total_dx=%.02f total_dy=%.02f\n",
   (float)total_dx / OVERSAMPLE, (float)total_dy / OVERSAMPLE);
 #endif
 
+// If there will be 2nd pass, target will be transformed then
+       if(!config.twopass || load_ok)
+       {
+               if( config.tracking_object != MotionScan::TRACK_SINGLE && !config.rotate ) {
+// Transfer current reference frame to previous reference frame and update
+// counter.  Must wait for rotate to compare.
+                       prev_global_ref->copy_from(current_global_ref);
+                       previous_frame_number = get_source_position();
+               }
+
+// No 2nd pass, decide here what to do with target based on requested operation
+               int interpolation = NEAREST_NEIGHBOR;
+               float dx = 0., dy = 0.;
+               switch(config.action_type) {
+               case MotionScan::NOTHING:
+                       global_target_dst->copy_from(global_target_src);
+                       break;
+               case MotionScan::TRACK_PIXEL:
+                       interpolation = NEAREST_NEIGHBOR;
+                       dx = rint((float)total_dx / OVERSAMPLE);
+                       dy = rint((float)total_dy / OVERSAMPLE);
+                       break;
+               case MotionScan::STABILIZE_PIXEL:
+                       interpolation = NEAREST_NEIGHBOR;
+                       dx = -rint((float)total_dx / OVERSAMPLE);
+                       dy = -rint((float)total_dy / OVERSAMPLE);
+                       break;
+               case MotionScan::TRACK:
+                       interpolation = CUBIC_LINEAR;
+                       dx = (float)total_dx / OVERSAMPLE;
+                       dy = (float)total_dy / OVERSAMPLE;
+                       break;
+               case MotionScan::STABILIZE:
+                       interpolation = CUBIC_LINEAR;
+                       dx = -(float)total_dx / OVERSAMPLE;
+                       dy = -(float)total_dy / OVERSAMPLE;
+                       break;
+               }
+
+
+               if( config.action_type != MotionScan::NOTHING ) {
+                       if( !overlayer )
+                               overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
+                       global_target_dst->clear_frame();
+                       overlayer->overlay(global_target_dst, global_target_src,
+                               0, 0, global_target_src->get_w(), global_target_src->get_h(),
+                               dx, dy,
+                               (float)global_target_src->get_w() + dx,
+                               (float)global_target_src->get_h() + dy,
+                               1, TRANSFER_REPLACE, interpolation);
+               }
+       }
+}
+
+void MotionMain::refine_global()
+{
+       int block_x, block_y;
+       double tmp_x, tmp_y;
+       float dx = 0., dy = 0.;
+
+// Use temp_frame instead of current_global_ref for refined translation search
+       allocate_temp(w, h, current_global_ref->get_color_model());
+       temp_frame->clear_frame();
+
+       if(config.rotate)
+       {
+// Here we have to rotate current_rotate_ref into temp_frame
+// backwards because prev_global_ref and current_global_ref are interchanged
+               if(!rotate_engine)
+                       rotate_engine = new AffineEngine(
+                               PluginClient::get_project_smp() + 1,
+                               PluginClient::get_project_smp() + 1);
+
+               float angle;
+               if(config.tracking_object == MotionScan::TRACK_SINGLE)
+               {
+                       angle = total_angle;
+               }
+               else
+               {
+                       angle = current_angle;
+               }
+
+// Pivot need not be very accurate, it is attached to the previous frame
+// while current frame is moved and rotated
+// Nevertheless compute it in floating point and round to int afterwards
+               tmp_x = current_rotate_ref->get_w() * config.block_x / 100;
+               tmp_y = current_rotate_ref->get_h() * config.block_y / 100;
+               if(config.tracking_object == MotionScan::TRACK_PREVIOUS)
+               {
+// Pivot is moved along the previous frame
+                       tmp_x += (double)(total_dx-current_dx) / OVERSAMPLE;
+                       tmp_y += (double)(total_dy-current_dy) / OVERSAMPLE;
+               }
+               block_x = lrint (tmp_x);
+               block_y = lrint (tmp_y);
+               rotate_engine->set_in_pivot(block_x, block_y);
+               rotate_engine->set_out_pivot(block_x, block_y);
+
+               rotate_engine->rotate(temp_frame, current_rotate_ref, -angle);
+       }
+       else
+       {
+// Here we have to translate current_global_ref into temp_frame
+// backwards because prev_global_ref and current_global_ref are interchanged
+               if(!overlayer) 
+                       overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
+               if(config.tracking_object == MotionScan::TRACK_SINGLE)
+               {
+                       dx = (float)total_dx / OVERSAMPLE;
+                       dy = (float)total_dy / OVERSAMPLE;
+               }
+               else
+               {
+                       dx = (float)current_dx / OVERSAMPLE;
+                       dy = (float)current_dy / OVERSAMPLE;
+               }
+
+               overlayer->overlay(temp_frame,
+                       current_global_ref,
+                       0,
+                       0,
+                       current_global_ref->get_w(),
+                       current_global_ref->get_h(),
+                       -dx,
+                       -dy,
+                       (float)current_global_ref->get_w() - dx,
+                       (float)current_global_ref->get_h() - dy,
+                       1,
+                       TRANSFER_REPLACE,
+                       CUBIC_LINEAR);
+       }
+
+// Determine additional translation, pass 2
+// Attention, prev_global_ref and current_global_ref are interchanged
+// Engine must have been created already by process_global()
+       engine->scan_frame(temp_frame, prev_global_ref,
+               config.global_range_w, config.global_range_h,
+               config.global_block_w, config.global_block_h,
+               config.block_x, config.block_y,
+               config.tracking_object, config.tracking_type,
+               config.action_type, config.horizontal_only,
+               config.vertical_only, get_source_position(),
+               config.global_positions, total_dx, total_dy,
+               0, 0, 2, load_ok, load_dx, load_dy);
+
+// Translation correction is to be added to motion vector
+       current_dx += engine->dx_result;
+       current_dy += engine->dy_result;
+       total_dx   += engine->dx_result;
+       total_dy   += engine->dy_result;
+
+// Refine saved results
+       if( config.tracking_type == MotionScan::SAVE ) {
+               save_dx = current_dx;
+               save_dy = current_dy;
+       }
+
+// Clamp accumulation vector
+       if( config.magnitude < 100 ) {
+               int block_x_orig = lrint(config.block_x * prev_global_ref->get_w() / 100);
+               int block_y_orig = lrint(config.block_y * prev_global_ref->get_h() / 100);
+               int max_block_x = (int64_t)(prev_global_ref->get_w() - block_x_orig)
+                       * OVERSAMPLE * config.magnitude / 100;
+               int max_block_y = (int64_t)(prev_global_ref->get_h() - block_y_orig)
+                       * OVERSAMPLE * config.magnitude / 100;
+               int min_block_x = (int64_t)-block_x_orig
+                       * OVERSAMPLE * config.magnitude / 100;
+               int min_block_y = (int64_t)-block_y_orig
+                       * OVERSAMPLE * config.magnitude / 100;
+
+               CLAMP(total_dx, min_block_x, max_block_x);
+               CLAMP(total_dy, min_block_y, max_block_y);
+       }
+
+#ifdef DEBUG
+printf("MotionMain::refine_global 2 total_dx=%.02f total_dy=%.02f\n",
+  (float)total_dx / OVERSAMPLE, (float)total_dy / OVERSAMPLE);
+#endif
+
        if( config.tracking_object != MotionScan::TRACK_SINGLE && !config.rotate ) {
 // Transfer current reference frame to previous reference frame and update
 // counter.  Must wait for rotate to compare.
@@ -439,20 +647,20 @@ printf("MotionMain::process_global 2 total_dx=%.02f total_dy=%.02f\n",
 
 // Decide what to do with target based on requested operation
        int interpolation = NEAREST_NEIGHBOR;
-       float dx = 0., dy = 0.;
+       dx = dy = 0.;
        switch(config.action_type) {
        case MotionScan::NOTHING:
                global_target_dst->copy_from(global_target_src);
                break;
        case MotionScan::TRACK_PIXEL:
                interpolation = NEAREST_NEIGHBOR;
-               dx = (int)(total_dx / OVERSAMPLE);
-               dy = (int)(total_dy / OVERSAMPLE);
+               dx = rint((float)total_dx / OVERSAMPLE);
+               dy = rint((float)total_dy / OVERSAMPLE);
                break;
        case MotionScan::STABILIZE_PIXEL:
                interpolation = NEAREST_NEIGHBOR;
-               dx = -(int)(total_dx / OVERSAMPLE);
-               dy = -(int)(total_dy / OVERSAMPLE);
+               dx = -rint((float)total_dx / OVERSAMPLE);
+               dy = -rint((float)total_dy / OVERSAMPLE);
                break;
        case MotionScan::TRACK:
                interpolation = CUBIC_LINEAR;
@@ -468,6 +676,7 @@ printf("MotionMain::process_global 2 total_dx=%.02f total_dy=%.02f\n",
 
 
        if( config.action_type != MotionScan::NOTHING ) {
+// Should be already created elsewhere but try it here just for safety
                if( !overlayer )
                        overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
                global_target_dst->clear_frame();
@@ -485,8 +694,11 @@ printf("MotionMain::process_global 2 total_dx=%.02f total_dy=%.02f\n",
 void MotionMain::process_rotation()
 {
        int block_x, block_y;
+       double tmp_x, tmp_y;
 
-// Convert the previous global reference into the previous rotation reference.
+// Here we have to translate current_global_ref into current_rotate_ref
+// backwards because prev_rotate_ref and current_rotate_ref are interchanged.
+// Also copy prev_global_ref into prev_rotate_ref for comparing.
 // Convert global target destination into rotation target source.
        if( config.global ) {
                if( !overlayer )
@@ -501,41 +713,54 @@ void MotionMain::process_rotation()
                        dy = (float)current_dy / OVERSAMPLE;
                }
 
-               prev_rotate_ref->clear_frame();
-               overlayer->overlay(prev_rotate_ref, prev_global_ref,
-                       0, 0, prev_global_ref->get_w(), prev_global_ref->get_h(),
-                       dx, dy,
-                       (float)prev_global_ref->get_w() + dx,
-                       (float)prev_global_ref->get_h() + dy,
+               prev_rotate_ref->copy_from(prev_global_ref);
+               current_rotate_ref->clear_frame();
+               overlayer->overlay(current_rotate_ref, current_global_ref,
+                       0, 0, current_global_ref->get_w(), current_global_ref->get_h(),
+                       -dx, -dy,
+                       (float)current_global_ref->get_w() - dx,
+                       (float)current_global_ref->get_h() - dy,
                        1, TRANSFER_REPLACE, CUBIC_LINEAR);
-// Pivot is destination global position
-               block_x = (int)(prev_rotate_ref->get_w() *
-                       config.block_x / 100 + (float)total_dx / OVERSAMPLE);
-               block_y = (int)(prev_rotate_ref->get_h() *
-                       config.block_y / 100 + (float)total_dy / OVERSAMPLE);
+// Pivot need not be very accurate, it is attached to the previous frame
+// while current frame is moved and rotated
+// Nevertheless compute it in floating point and round to int afterwards
+               tmp_x = prev_rotate_ref->get_w() * config.block_x / 100;
+               tmp_y = prev_rotate_ref->get_h() * config.block_y / 100;
+               if(config.tracking_object == MotionScan::TRACK_PREVIOUS)
+               {
+// Pivot is moved along the previous frame
+                       tmp_x += (double)(total_dx-current_dx) / OVERSAMPLE;
+                       tmp_y += (double)(total_dy-current_dy) / OVERSAMPLE;
+               }
 // Use the global target output as the rotation target input
                rotate_target_src->copy_from(global_target_dst);
 // Transfer current reference frame to previous reference frame for global.
-               if( config.tracking_object != MotionScan::TRACK_SINGLE ) {
+               if(config.tracking_object != MotionScan::TRACK_SINGLE &&
+                       (!config.twopass || load_ok))
+               {
                        prev_global_ref->copy_from(current_global_ref);
                        previous_frame_number = get_source_position();
                }
        }
        else {
-// Pivot is fixed
-               block_x = (int)(prev_rotate_ref->get_w() * config.block_x / 100);
-               block_y = (int)(prev_rotate_ref->get_h() * config.block_y / 100);
+// Pivot is fixed as translation switched off
+               tmp_x = prev_rotate_ref->get_w() * config.block_x / 100;
+               tmp_y = prev_rotate_ref->get_h() * config.block_y / 100;
        }
+       block_x = lrint (tmp_x);
+       block_y = lrint (tmp_y);
 
-// Get rotation
+// Get rotation, either single pass or pass 1
        if( !motion_rotate )
                motion_rotate = new RotateScan(this,
                        get_project_smp() + 1, get_project_smp() + 1);
 
+// Attention, prev_rotate_ref and current_rotate_ref are interchanged
        current_angle = motion_rotate->
-               scan_frame(prev_rotate_ref, current_rotate_ref, block_x, block_y);
+               scan_frame(current_rotate_ref, prev_rotate_ref, block_x, block_y, config.twopass);
+       current_angle += dt_offset;
 
-// Write results
+// Save result
        if( config.tracking_type == MotionScan::SAVE ) {
                save_dt = current_angle;
        }
@@ -551,30 +776,201 @@ void MotionMain::process_rotation()
                if( config.rotate_magnitude < 90 ) {
                        CLAMP(total_angle, -config.rotate_magnitude, config.rotate_magnitude);
                }
+       }
+       else {
+               total_angle = current_angle;
+       }
 
-               if( !config.global ) {
+#ifdef DEBUG
+printf("MotionMain::process_rotation total_angle=%f\n", total_angle);
+#endif
+
+// If there will be 2nd pass, target will be transformed then
+       if(!config.twopass || load_ok)
+       {
+               if( config.tracking_object != MotionScan::TRACK_SINGLE && !config.global ) {
 // Transfer current reference frame to previous reference frame and update counter.
                        prev_rotate_ref->copy_from(current_rotate_ref);
                        previous_frame_number = get_source_position();
                }
+
+// No 2nd pass, calculate rotation parameters based on requested operation
+// Use origin of global stabilize operation for pivot by default
+// Compute it in floating point for accuracy and round to int afterwards
+               tmp_x = rotate_target_src->get_w() * config.block_x / 100;
+               tmp_y = rotate_target_src->get_h() * config.block_y / 100;
+
+               float angle = 0.;
+               switch(config.action_type) {
+               case MotionScan::NOTHING:
+                       rotate_target_dst->copy_from(rotate_target_src);
+                       break;
+               case MotionScan::TRACK:
+               case MotionScan::TRACK_PIXEL:
+                       if(config.global)
+                       {
+// Use destination of global tracking for pivot.
+                               tmp_x += (double)total_dx / OVERSAMPLE;
+                               tmp_y += (double)total_dy / OVERSAMPLE;
+                       }
+                       angle = total_angle;
+                       break;
+               case MotionScan::STABILIZE:
+               case MotionScan::STABILIZE_PIXEL:
+                       angle = -total_angle;
+                       break;
+               }
+               block_x = lrint (tmp_x);
+               block_y = lrint (tmp_y);
+
+               if( config.action_type != MotionScan::NOTHING ) {
+                       if( !rotate_engine )
+                               rotate_engine = new AffineEngine(
+                                       PluginClient::get_project_smp() + 1,
+                                       PluginClient::get_project_smp() + 1);
+
+                       rotate_target_dst->clear_frame();
+
+                       rotate_engine->set_in_pivot(block_x, block_y);
+                       rotate_engine->set_out_pivot(block_x, block_y);
+
+                       rotate_engine->rotate(rotate_target_dst, rotate_target_src, angle);
+               }
+       }
+}
+
+void MotionMain::refine_rotation()
+{
+       int block_x, block_y;
+       double tmp_x, tmp_y;
+       float angle;
+
+// Use temp_frame instead of current_rotate_ref for refined rotation search
+       allocate_temp(w, h, current_rotate_ref->get_color_model());
+       temp_frame->clear_frame();
+
+       if( config.global ) {
+// Here we have to translate current_global_ref into current_rotate_ref
+// backwards because prev_rotate_ref and current_rotate_ref are interchanged
+// prev_rotate_ref must have been copied already by process_rotation()
+               if( !overlayer )
+                       overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
+               float dx, dy;
+               if( config.tracking_object == MotionScan::TRACK_SINGLE ) {
+                       dx = (float)total_dx / OVERSAMPLE;
+                       dy = (float)total_dy / OVERSAMPLE;
+               }
+               else {
+                       dx = (float)current_dx / OVERSAMPLE;
+                       dy = (float)current_dy / OVERSAMPLE;
+               }
+
+               current_rotate_ref->clear_frame();
+               overlayer->overlay(current_rotate_ref, current_global_ref,
+                       0, 0, current_global_ref->get_w(), current_global_ref->get_h(),
+                       -dx, -dy,
+                       (float)current_global_ref->get_w() - dx,
+                       (float)current_global_ref->get_h() - dy,
+                       1, TRANSFER_REPLACE, CUBIC_LINEAR);
+
+// Pivot need not be very accurate, it is attached to the previous frame
+// while current frame is moved and rotated
+// Nevertheless compute it in floating point and round to int afterwards
+               tmp_x = current_rotate_ref->get_w() * config.block_x / 100;
+               tmp_y = current_rotate_ref->get_h() * config.block_y / 100;
+               if(config.tracking_object == MotionScan::TRACK_PREVIOUS)
+               {
+// Pivot is moved along the previous frame
+                       tmp_x += (double)(total_dx-current_dx) / OVERSAMPLE;
+                       tmp_y += (double)(total_dy-current_dy) / OVERSAMPLE;
+               }
        }
        else {
-               total_angle = current_angle;
+// Pivot is fixed as translation switched off
+               tmp_x = current_rotate_ref->get_w() * config.block_x / 100;
+               tmp_y = current_rotate_ref->get_h() * config.block_y / 100;
+       }
+       block_x = lrint (tmp_x);
+       block_y = lrint (tmp_y);
+
+// Now rotate current_rotate_ref into temp_frame
+// backwards because prev_global_ref and current_global_ref are interchanged
+       if(!rotate_engine)
+               rotate_engine = new AffineEngine(
+                       PluginClient::get_project_smp() + 1,
+                       PluginClient::get_project_smp() + 1);
+
+       if(config.tracking_object == MotionScan::TRACK_SINGLE)
+       {
+               angle = total_angle;
+       }
+       else
+       {
+               angle = current_angle;
+       }
+
+       rotate_engine->set_in_pivot(block_x, block_y);
+       rotate_engine->set_out_pivot(block_x, block_y);
+
+       rotate_engine->rotate(temp_frame, current_rotate_ref, -angle);
+
+// Determine additional rotation, pass 2
+// Attention, prev_rotate_ref and current_rotate_ref are interchanged
+// Engine must have been created already by process_rotation()
+       angle = motion_rotate->
+               scan_frame(temp_frame, prev_rotate_ref, block_x, block_y, 2);
+
+// Rotation correction is to be added to accumulated angle
+       current_angle += angle;
+       total_angle   += angle;
+
+// Refine saved result
+       if( config.tracking_type == MotionScan::SAVE ) {
+               save_dt = current_angle;
+       }
+
+// Clamp rotation accumulation
+       if( config.rotate_magnitude < 90 ) {
+               CLAMP(total_angle, -config.rotate_magnitude, config.rotate_magnitude);
        }
 
 #ifdef DEBUG
 printf("MotionMain::process_rotation total_angle=%f\n", total_angle);
 #endif
 
+       if(config.tracking_object != MotionScan::TRACK_SINGLE)
+       {
+// Transfer current reference frame to previous reference frame and update
+// counter.
+               if(config.global)
+               {
+                       prev_global_ref->copy_from(current_global_ref);
+               }
+               else
+               {
+                       prev_rotate_ref->copy_from(current_rotate_ref);
+               }
+               previous_frame_number = get_source_position();
+       }
 
 // Calculate rotation parameters based on requested operation
-       float angle = 0.;
+// Use origin of global stabilize operation for pivot by default
+// Compute it in floating point for accuracy and round to int afterwards
+       tmp_x = rotate_target_src->get_w() * config.block_x / 100;
+       tmp_y = rotate_target_src->get_h() * config.block_y / 100;
+
        switch(config.action_type) {
        case MotionScan::NOTHING:
                rotate_target_dst->copy_from(rotate_target_src);
                break;
        case MotionScan::TRACK:
        case MotionScan::TRACK_PIXEL:
+               if(config.global)
+               {
+// Use destination of global tracking for pivot.
+                       tmp_x += (double)total_dx / OVERSAMPLE;
+                       tmp_y += (double)total_dy / OVERSAMPLE;
+               }
                angle = total_angle;
                break;
        case MotionScan::STABILIZE:
@@ -582,8 +978,11 @@ printf("MotionMain::process_rotation total_angle=%f\n", total_angle);
                angle = -total_angle;
                break;
        }
+       block_x = lrint (tmp_x);
+       block_y = lrint (tmp_y);
 
        if( config.action_type != MotionScan::NOTHING ) {
+// Should be already created elsewhere but try it here just for safety
                if( !rotate_engine )
                        rotate_engine = new AffineEngine(
                                PluginClient::get_project_smp() + 1,
@@ -591,50 +990,16 @@ printf("MotionMain::process_rotation total_angle=%f\n", total_angle);
 
                rotate_target_dst->clear_frame();
 
-// Determine pivot based on a number of factors.
-               switch(config.action_type) {
-               case MotionScan::TRACK:
-               case MotionScan::TRACK_PIXEL:
-// Use destination of global tracking.
-                       rotate_engine->set_in_pivot(block_x, block_y);
-                       rotate_engine->set_out_pivot(block_x, block_y);
-                       break;
-
-               case MotionScan::STABILIZE:
-               case MotionScan::STABILIZE_PIXEL:
-                       if( config.global ) {
-// Use origin of global stabilize operation
-                               rotate_engine->set_in_pivot(
-                                       (int)(rotate_target_dst->get_w() * config.block_x / 100),
-                                       (int)(rotate_target_dst->get_h() * config.block_y / 100));
-                               rotate_engine->set_out_pivot(
-                                       (int)(rotate_target_dst->get_w() * config.block_x / 100),
-                                       (int)(rotate_target_dst->get_h() * config.block_y / 100));
-                               }
-                               else {
-// Use origin
-                                       rotate_engine->set_in_pivot(block_x, block_y);
-                                       rotate_engine->set_out_pivot(block_x, block_y);
-                               }
-                               break;
-               }
+               rotate_engine->set_in_pivot(block_x, block_y);
+               rotate_engine->set_out_pivot(block_x, block_y);
 
                rotate_engine->rotate(rotate_target_dst, rotate_target_src, angle);
-// overlayer->overlay(rotate_target_dst, prev_rotate_ref,
-//     0, 0, prev_rotate_ref->get_w(), prev_rotate_ref->get_h(),
-//     0, 0, prev_rotate_ref->get_w(), prev_rotate_ref->get_h(),
-//     1, TRANSFER_NORMAL, CUBIC_LINEAR);
-// overlayer->overlay(rotate_target_dst, current_rotate_ref,
-//     0, 0, prev_rotate_ref->get_w(), prev_rotate_ref->get_h(),
-//     0, 0, prev_rotate_ref->get_w(), prev_rotate_ref->get_h(),
-//     1, TRANSFER_NORMAL, //  CUBIC_LINEAR);
        }
 }
 
 
 int MotionMain::process_buffer(VFrame **frame, int64_t start_position, double frame_rate)
 {
-       int prev_config_tracking_type = config.tracking_type;
        int need_reconfigure = load_configuration();
        int color_model = frame[0]->get_color_model();
        w = frame[0]->get_w();
@@ -697,18 +1062,11 @@ printf("MotionMain::process_buffer %d start_position=%jd\n", __LINE__, start_pos
 
 //printf("process_realtime: %jd %d %jd %jd\n", start_position,
 // skip_current, previous_frame_number, actual_previous_number);
-       if( prev_config_tracking_type != MotionScan::SAVE &&
-           config.tracking_type == MotionScan::SAVE ) {
-               reset_cache_file();
-               char save_file[BCTEXTLEN];
-               snprintf(save_file, sizeof(save_file), "%s.bak", config.tracking_file);
-#ifdef DEBUG
-printf("MotionMain::process_buffer 2 rename tracking file: %s to %s\n",
- config.tracking_file, save_file);
-#endif
-               ::rename(config.tracking_file, save_file);
-       }
-       else if( !cache_file[0] || active_key > start_position )
+       if( ((config.tracking_type != MotionScan::LOAD &&
+             config.tracking_type != MotionScan::SAVE) && cache_fp) ||
+           ((config.tracking_type == MotionScan::LOAD ||
+             config.tracking_type == MotionScan::SAVE) && !cache_fp) ||
+           !cache_file[0] || (active_fp && active_key > start_position) )
                reset_cache_file();
 
 // Load match frame and reset vectors
@@ -807,7 +1165,6 @@ printf("MotionMain::process_buffer 2 rename tracking file: %s to %s\n",
                        start_position, frame_rate, 0);
        }
 
-       dx_offset = 0; dy_offset = 0;
        if( config.tracking_type == MotionScan::LOAD ) {
                if( config.addtrackedframeoffset ) {
                        if( config.track_frame != tracking_frame ) {
@@ -815,7 +1172,8 @@ printf("MotionMain::process_buffer 2 rename tracking file: %s to %s\n",
                                int64_t no;  int dx, dy;  float dt;
                                if( !get_cache_line(tracking_frame) &&
                                    sscanf(cache_line, "%jd %d %d %f", &no, &dx, &dy, &dt) == 4 ) {
-                                       dx_offset = dx; dy_offset = dy;
+                                       dx_offset += dx; dy_offset += dy;
+                                       dt_offset += dt;
                                }
                                else {
                                        eprintf("no offset data frame %jd\n", tracking_frame);
@@ -823,7 +1181,19 @@ printf("MotionMain::process_buffer 2 rename tracking file: %s to %s\n",
                        }
                }
                else
+               {
+                       dx_offset = 0;
+                       dy_offset = 0;
+                       dt_offset = 0;
                        tracking_frame = -1;
+               }
+       }
+       else
+       {
+               dx_offset = 0;
+               dy_offset = 0;
+               dt_offset = 0;
+               tracking_frame = -1;
        }
 
        if( !skip_current ) {
@@ -852,6 +1222,12 @@ printf("MotionMain::process_buffer: no tracking data frame %jd\n", frame_no);
                        process_rotation();
 //frame[target_layer]->copy_from(prev_rotate_ref);
 //frame[target_layer]->copy_from(current_rotate_ref);
+               if(config.twopass && !load_ok)
+// Second pass not needed if coords loaded from cache
+               {
+                       if(config.global) refine_global();
+                       if(config.rotate) refine_rotation();
+               }
 
 // write results to disk
                if( config.tracking_type == MotionScan::SAVE ) {
@@ -896,40 +1272,56 @@ void MotionMain::draw_vectors(VFrame *frame)
        int block_x3, block_y3, block_x4, block_y4;
        int search_x1, search_y1, search_x2, search_y2;
        int search_w, search_h;
+       double tmp_x1, tmp_y1;
+       double tmp_x2, tmp_y2;
 
 
        if( config.global ) {
-// Get vector
-// Start of vector is center of previous block.
-// End of vector is total accumulation.
+// Get vector as double and round the result for more regular behavior
                if( config.tracking_object == MotionScan::TRACK_SINGLE ) {
-                       global_x1 = (int64_t)(config.block_x * w / 100);
-                       global_y1 = (int64_t)(config.block_y * h / 100);
-                       global_x2 = global_x1 + total_dx / OVERSAMPLE;
-                       global_y2 = global_y1 + total_dy / OVERSAMPLE;
-//printf("MotionMain::draw_vectors %d %d %d %d %d %d\n", total_dx, total_dy, global_x1, global_y1, global_x2, global_y2);
+// Start of vector is center of original block.
+// Length of vector is total accumulation.
+                       tmp_x1 = config.block_x * w / 100;
+                       tmp_y1 = config.block_y * h / 100;
+                       tmp_x2 = tmp_x1 + (double)total_dx / OVERSAMPLE;
+                       tmp_y2 = tmp_y1 + (double)total_dy / OVERSAMPLE;
                }
-// Start of vector is center of previous block.
-// End of vector is current change.
                else if( config.tracking_object == MotionScan::PREVIOUS_SAME_BLOCK ) {
-                       global_x1 = (int64_t)(config.block_x * w / 100);
-                       global_y1 = (int64_t)(config.block_y * h / 100);
-                       global_x2 = global_x1 + current_dx / OVERSAMPLE;
-                       global_y2 = global_y1 + current_dy / OVERSAMPLE;
+// Start of vector is center of original block.
+// Length of vector is current change.
+                       tmp_x1 = config.block_x * w / 100;
+                       tmp_y1 = config.block_y * h / 100;
+                       tmp_x2 = tmp_x1 + (double)current_dx / OVERSAMPLE;
+                       tmp_y2 = tmp_y1 + (double)current_dy / OVERSAMPLE;
                }
                else {
-                       global_x1 = (int64_t)(config.block_x * w / 100
-                               + (total_dx - current_dx) / OVERSAMPLE);
-                       global_y1 = (int64_t)(config.block_y * h / 100
-                               + (total_dy - current_dy) / OVERSAMPLE);
-                       global_x2 = (int64_t)(config.block_x * w / 100
-                               + total_dx / OVERSAMPLE);
-                       global_y2 = (int64_t)(config.block_y * h / 100
-                               + total_dy / OVERSAMPLE);
+// Start of vector is center of current block.
+// Length of vector is current change.
+                       tmp_x1 = config.block_x * w / 100 + 
+                               (double)(total_dx - current_dx) / OVERSAMPLE;
+                       tmp_y1 = config.block_y * h / 100 +
+                               (double)(total_dy - current_dy) / OVERSAMPLE;
+                       tmp_x2 = config.block_x * w / 100 + 
+                               (double)total_dx / OVERSAMPLE;
+                       tmp_y2 = config.block_y * h / 100 +
+                               (double)total_dy / OVERSAMPLE;
                }
+               global_x1 = lrint (tmp_x1);
+               global_y1 = lrint (tmp_y1);
+               global_x2 = lrint (tmp_x2);
+               global_y2 = lrint (tmp_y2);
+//printf("MotionMain::draw_vectors %d %d %d %d %d %d\n", total_dx, total_dy, global_x1, global_y1, global_x2, global_y2);
 
                block_x = global_x1;
                block_y = global_y1;
+               if((config.tracking_object == MotionScan::TRACK_SINGLE ||
+                   config.tracking_object == MotionScan::TRACK_PREVIOUS) &&
+                  !config.rotate)
+               {
+// Show block at endpoint of motion if no rotation shown
+                       block_x = global_x2;
+                       block_y = global_y2;
+               }
                block_w = config.global_block_w * w / 100;
                block_h = config.global_block_h * h / 100;
                block_x1 = block_x - block_w / 2;
@@ -973,8 +1365,8 @@ void MotionMain::draw_vectors(VFrame *frame)
                }
        }
        else {
-               block_x = (int64_t)(config.block_x * w / 100);
-               block_y = (int64_t)(config.block_y * h / 100);
+               block_x = lrint (config.block_x * w / 100);
+               block_y = lrint (config.block_y * h / 100);
        }
 
        block_w = config.global_block_w * w / 100;
@@ -986,14 +1378,14 @@ void MotionMain::draw_vectors(VFrame *frame)
                double target_angle1 = base_angle1 + angle;
                double target_angle2 = base_angle2 + angle;
                double radius = sqrt(block_w * block_w + block_h * block_h) / 2;
-               block_x1 = (int)(block_x - cos(target_angle1) * radius);
-               block_y1 = (int)(block_y - sin(target_angle1) * radius);
-               block_x2 = (int)(block_x + sin(target_angle2) * radius);
-               block_y2 = (int)(block_y - cos(target_angle2) * radius);
-               block_x3 = (int)(block_x - sin(target_angle2) * radius);
-               block_y3 = (int)(block_y + cos(target_angle2) * radius);
-               block_x4 = (int)(block_x + cos(target_angle1) * radius);
-               block_y4 = (int)(block_y + sin(target_angle1) * radius);
+               block_x1 = lrint(block_x - cos(target_angle1) * radius);
+               block_y1 = lrint(block_y - sin(target_angle1) * radius);
+               block_x2 = lrint(block_x + sin(target_angle2) * radius);
+               block_y2 = lrint(block_y - cos(target_angle2) * radius);
+               block_x3 = lrint(block_x - sin(target_angle2) * radius);
+               block_y3 = lrint(block_y + cos(target_angle2) * radius);
+               block_x4 = lrint(block_x + cos(target_angle1) * radius);
+               block_y4 = lrint(block_y + sin(target_angle1) * radius);
 
                draw_line(frame, block_x1, block_y1, block_x2, block_y2);
                draw_line(frame, block_x2, block_y2, block_x4, block_y4);
@@ -1043,22 +1435,13 @@ void MotionMain::draw_line(VFrame *frame, int x1, int y1, int x2, int y2)
 #define ARROW_SIZE 10
 void MotionMain::draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2)
 {
-       double angle = atan((float)(y2 - y1) / (float)(x2 - x1));
-       double angle1 = angle + (float)145 / 360 * 2 * 3.14159265;
-       double angle2 = angle - (float)145 / 360 * 2 * 3.14159265;
-       int x3, y3, x4, y4;
-       if( x2 < x1 ) {
-               x3 = x2 - (int)(ARROW_SIZE * cos(angle1));
-               y3 = y2 - (int)(ARROW_SIZE * sin(angle1));
-               x4 = x2 - (int)(ARROW_SIZE * cos(angle2));
-               y4 = y2 - (int)(ARROW_SIZE * sin(angle2));
-       }
-       else {
-               x3 = x2 + (int)(ARROW_SIZE * cos(angle1));
-               y3 = y2 + (int)(ARROW_SIZE * sin(angle1));
-               x4 = x2 + (int)(ARROW_SIZE * cos(angle2));
-               y4 = y2 + (int)(ARROW_SIZE * sin(angle2));
-       }
+       double angle = atan2((double)(y2 - y1), (double)(x2 - x1));
+       double angle1 = angle + (float)145 / 360 * 2 * M_PI;
+       double angle2 = angle - (float)145 / 360 * 2 * M_PI;
+       int x3 = x2 + (int)(ARROW_SIZE * cos(angle1));
+       int y3 = y2 + (int)(ARROW_SIZE * sin(angle1));
+       int x4 = x2 + (int)(ARROW_SIZE * cos(angle2));
+       int y4 = y2 + (int)(ARROW_SIZE * sin(angle2));
 
 // Main vector
        draw_line(frame, x1, y1, x2, y2);
@@ -1139,6 +1522,7 @@ int MotionMain::put_cache_line(const char *line)
        if( !active_fp ) {
                close_cache_file();
                snprintf(cache_file, sizeof(cache_file), "%s.bak", config.tracking_file);
+               ::remove(cache_file);
                ::rename(config.tracking_file, cache_file);
                if( !(active_fp = fopen(config.tracking_file, "w")) ) {
                        perror(config.tracking_file);
@@ -1177,6 +1561,7 @@ void MotionMain::reset_cache_file()
        else
                close_cache_file();
        strcpy(cache_file, config.tracking_file);
+       if (!cache_file[0]) strcpy(cache_file, TRACKING_FILE);
 }
 
 
@@ -1209,6 +1594,12 @@ void RotateScanUnit::process_package(LoadPackage *package)
                int color_model = server->previous_frame->get_color_model();
                int pixel_size = BC_CModels::calculate_pixelsize(color_model);
                int row_bytes = server->previous_frame->get_bytes_per_line();
+               float angle = pkg->angle;
+// Ensure a tiny displacement if angle is nearly exact zero
+// As angle is in degree and MIN_ANGLE is in radian,
+// displacement of 1/57th of the smallest possible angle can be discarded
+// This trick is needed to trigger interpolation
+               if (fabs (angle) < MIN_ANGLE) angle = MIN_ANGLE;
 
                if( !rotater )
                        rotater = new AffineEngine(1, 1);
@@ -1231,7 +1622,7 @@ void RotateScanUnit::process_package(LoadPackage *package)
                rotater->set_in_pivot(server->block_x, server->block_y);
                rotater->set_out_pivot(server->block_x, server->block_y);
 //printf("RotateScanUnit::process_package %d\n", __LINE__);
-               rotater->rotate(temp, server->previous_frame, pkg->angle);
+               rotater->rotate(temp, server->previous_frame, angle);
 
 // Scan reduced block size
 //plugin->output_frame->copy_from(server->current_frame);
@@ -1256,6 +1647,14 @@ void RotateScanUnit::process_package(LoadPackage *package)
                                row_bytes, x2 - x1, y2 - y1, color_model);
 //printf("RotateScanUnit::process_package %d\n", __LINE__);
                        server->put_cache(pkg->angle, pkg->difference);
+// Dumping rotated frame for debugging
+//                     temp->write_ppm(temp, "/tmp/a%06ld-a%f.ppm",
+//                                     plugin->get_source_position(),
+//                                     pkg->angle);
+//                     if (pkg->angle == 0)
+//                       temp->write_ppm(server->previous_frame,
+//                                       "/tmp/a%06ld-t.ppm",
+//                                       plugin->get_source_position());
                }
 #if 0
        VFrame png(x2-x1, y2-y1, BC_RGB888, -1);
@@ -1293,7 +1692,7 @@ void RotateScan::init_packages()
        for( int i = 0; i < get_total_packages(); i++ ) {
                RotateScanPackage *pkg = (RotateScanPackage*)get_package(i);
                pkg->angle = scan_angle1 +
-                       i * (scan_angle2 - scan_angle1) / (total_steps - 1);
+                       i * (scan_angle2 - scan_angle1) / total_steps;
        }
 }
 
@@ -1309,16 +1708,35 @@ LoadPackage* RotateScan::new_package()
 
 
 float RotateScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
-       int block_x, int block_y)
+       int block_x, int block_y, int passno)
 {
+// Attention, process_buffer feeds previous_frame and current_frame interchanged
+// Preinitialize sane rotation results
        skip = 0;
        this->block_x = block_x;
        this->block_y = block_y;
 
-//printf("RotateScan::scan_frame %d\n", __LINE__);
+// passno == 0: single pass tracking
+// passno == 1: 1st pass of two-pass tracking (reduce accuracy)
+// passno == 2: 2nd pass of two-pass tracking (reduce angle range)
+// Save may be needed for 2nd pass
+//     float result_saved = 0;
+       if (passno == 2)
+       {
+// result_saved needed for some debug printing only
+//             result_saved = result;
+               result = 0;
+       }
+       else
+       {
+               result = plugin->config.rotation_center;
+       }
+
+//printf("RotateScan::scan_frame %d frame=%ld passno=%d\n", __LINE__, plugin->get_source_position(), passno);
        switch(plugin->config.tracking_type) {
        case MotionScan::NO_CALCULATE:
                result = plugin->config.rotation_center;
+               if (passno == 2) result = 0;
                skip = 1;
                break;
 
@@ -1326,9 +1744,17 @@ float RotateScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
        case MotionScan::SAVE:
                if( plugin->load_ok ) {
                        result = plugin->load_dt;
+                       if (passno == 2) result = 0;
                        skip = 1;
                }
                break;
+
+// Scan from scratch with sane rotation results
+       default:
+               result = plugin->config.rotation_center;
+               if (passno == 2) result = 0;
+               skip = 0;
+               break;
        }
 
        this->previous_frame = previous_frame;
@@ -1414,10 +1840,13 @@ float RotateScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
 // Determine min angle from size of block
        double angle1 = atan((double)block_h / block_w);
        double angle2 = atan((double)(block_h - 1) / (block_w + 1));
+// Attention we get min_angle and MIN_ANGLE in radian, but elsewhere use degree
        double min_angle = fabs(angle2 - angle1) / OVERSAMPLE;
        min_angle = MAX(min_angle, MIN_ANGLE);
+// Convert min_angle to degree for convenience
+       min_angle *= 180 / M_PI;
 
-//printf("RotateScan::scan_frame %d min_angle=%f\n", __LINE__, min_angle * 360 / 2 / M_PI);
+//printf("RotateScan::scan_frame %d min_angle=%f\n", __LINE__, min_angle);
 
        cache.remove_all_objects();
 
@@ -1426,6 +1855,7 @@ float RotateScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
                if( previous_frame->data_matches(current_frame) ) {
 //printf("RotateScan::scan_frame: frames match.  Skipping.\n");
                        result = plugin->config.rotation_center;
+                       if (passno == 2) result = 0;
                        skip = 1;
                }
        }
@@ -1434,37 +1864,123 @@ float RotateScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
 // Initial search range
                float angle_range = max_angle;
                result = plugin->config.rotation_center;
-               total_steps = plugin->config.rotate_positions;
+               if (passno == 2) result = 0;
+
+               if (passno == 1)
+               {
+// Evtl stop search earlier for 1st pass to gain speed
+                       if (angle_range > 16 && min_angle < 1) min_angle = 1;
+                       else if (angle_range > 4 && min_angle < 0.25) min_angle = angle_range/16;
+                       else if (angle_range > min_angle*4) min_angle *= 4;
+               }
+
+               if (passno == 2)
+               {
+// Evtl reduce angle_range for refinement pass to gain speed
+                       if (angle_range > 16) angle_range /= 4;
+                       else if (angle_range > 4) angle_range = 4;
+                       if (angle_range < min_angle*4) angle_range = min_angle*4;
+                       if (angle_range > max_angle) angle_range = max_angle;
+               }
 
+// Pre-negate result as previous_frame and current_frame have been interchanged
+               result = -result;
 
-               while( angle_range >= min_angle * total_steps ) {
+               while( angle_range >= min_angle ) {
                        scan_angle1 = result - angle_range;
                        scan_angle2 = result + angle_range;
+// Find number of required steps, even and no more than configured at once
+                       total_steps = (int)ceil(angle_range*2/min_angle);
+                       if (total_steps & 1) total_steps ++;
+                       if (total_steps > plugin->config.rotate_positions) total_steps = plugin->config.rotate_positions;
+
+//printf("RotateScan::scan_frame angle_range=%f from=%f to=%f steps=%d\n", angle_range, scan_angle1, scan_angle2, total_steps);
 
-                       set_package_count(total_steps);
+// Use odd number of samples to ensure that rotation center be always included
+                       set_package_count(total_steps+1);
 //set_package_count(1);
                        process_packages();
 
-                       int64_t min_difference = -1;
+                       int64_t min_difference = -1, max_difference = -1, noiselev = -1;
                        for( int i = 0; i < get_total_packages(); i++ ) {
                                RotateScanPackage *pkg = (RotateScanPackage*)get_package(i);
                                if( pkg->difference < min_difference || min_difference == -1 ) {
                                        min_difference = pkg->difference;
                                        result = pkg->angle;
                                }
+                               if( pkg->difference > max_difference || max_difference == -1 ) {
+                                       max_difference = pkg->difference;
+                               }
+//printf("RotateScan::scan_frame pkg=%d angle=%f diff=%ld min_diff=%ld max_diff=%ld\n", i, pkg->angle, pkg->difference, min_difference, max_difference);
 //break;
                        }
-
-                       angle_range /= 2;
-
+// Determine noise level (not active on pass 2)
+                       noiselev = min_difference+(max_difference-min_difference)*plugin->config.noise_rotation/100;
+                       if (passno == 2) noiselev = min_difference;
+//printf("RotateScan::scan_frame min_diff=%ld max_diff=%ld noiselev=%ld\n", min_difference, max_difference, noiselev);
+                       for(int i = 0; i < get_total_packages(); i++)
+                       {
+                               RotateScanPackage *pkg = (RotateScanPackage*)get_package(i);
+// Already found as the best sample, not necessary to memorize
+                               if(result == pkg->angle) continue;
+// Above noise level - a definitely bad sample, skip
+                               if(pkg->difference > noiselev) continue;
+// Below noise level but farther from rotation center, skip
+                               if(fabs(pkg->angle-plugin->config.rotation_center) > fabs(result-plugin->config.rotation_center)) continue;
+// Below noise level and nearer to rotation center, memorize
+                               if(fabs(pkg->angle-plugin->config.rotation_center) < fabs(result-plugin->config.rotation_center))
+                               {
+                                       min_difference = pkg->difference;
+                                       result = pkg->angle;
+//printf("RotateScan::scan_frame angle override=%d angle=%f diff=%ld min_diff=%ld\n", i, pkg->angle, pkg->difference, min_difference);
+                                       continue;
+                               }
+// Equal distances to rotation center, memorize sample with min difference
+                               if(pkg->difference < min_difference)
+                               {
+                                       min_difference = pkg->difference;
+                                       result = pkg->angle;
+//printf("RotateScan::scan_frame difference override=%d angle=%f diff=%ld min_diff=%ld\n", i, pkg->angle, pkg->difference, min_difference);
+                                       continue;
+                               }
+                       }
+                       float new_range = 0, angle_diff = 0;
+                       for(int i = 0; i < get_total_packages(); i++)
+                       {
+                               RotateScanPackage *pkg = (RotateScanPackage*)get_package(i);
+// Above noise level - skip this angle
+                               if(pkg->difference > noiselev) continue;
+// Below noise level - measure max difference from the best angle
+                               if(angle_diff < fabs(result-pkg->angle))
+                               {
+                                       angle_diff = fabs(result-pkg->angle);
+//printf("RotateScan::scan_frame angle diff override=%d angle=%f diff=%f\n", i, pkg->angle, angle_diff);
+                               }
+                       }
+// Optimum new angle range might be +/- one search step from the best angle
+                       new_range = angle_range * 2 / total_steps;
+// Evtl expand angle range to +/- two search steps if some samples below noise
+                       if (angle_diff > 0) new_range = angle_range * 4 / total_steps;
+// Evtl expand angle range to include samples below noise level
+                       if (new_range < angle_diff) new_range = angle_diff;
+// But always reduce angle range at least twice
+                       if (new_range > angle_range / 2) new_range = angle_range / 2;
+                       angle_range = new_range;
 //break;
                }
+// Negate result as previous_frame and current_frame have been interchanged
+// and get rid of negative zeros in coord files
+               result = -result;
+               if (fabs (result) < MIN_ANGLE) result = 0;
        }
 
-       if( plugin->config.tracking_type == MotionScan::SAVE ) {
-               plugin->save_dt = result;
-       }
-//printf("RotateScan::scan_frame %d angle=%f\n", __LINE__, result);
+// Dumping compared frames for debugging
+//             current_frame->write_ppm(previous_frame, "/tmp/a%06ld-p.ppm",
+//                                      plugin->get_source_position());
+//             current_frame->write_ppm(current_frame, "/tmp/a%06ld-c.ppm",
+//                                      plugin->get_source_position());
+
+//printf("RotateScan::scan_frame %d passno=%d saved angle=%f measured angle=%f twopass angle=%f\n", __LINE__, passno, result_saved, result, result_saved+result);
        return result;
 }
 
@@ -1474,7 +1990,8 @@ int64_t RotateScan::get_cache(float angle)
        cache_lock->lock("RotateScan::get_cache");
        for( int i = 0; i < cache.total; i++ ) {
                RotateScanCache *ptr = cache.values[i];
-               if( fabs(ptr->angle - angle) <= MIN_ANGLE ) {
+// Attention, MIN_ANGLE in radian, while angle and ptr->angle in degree !
+               if( fabs(ptr->angle - angle) <= MIN_ANGLE * 90 / M_PI ) {
                        result = ptr->difference;
                        break;
                }
index 1ce29e50c5d56e481ce860f28fb4f7971ba6ad16..56390bad34b206fe6c25d9a155e9e6b840d22115 100644 (file)
@@ -59,7 +59,7 @@ class RotateScan;
 #define MIN_BLOCKS 1
 #define MAX_BLOCKS 200
 
-// Precision of rotation
+// Precision of rotation (in radian!)
 #define MIN_ANGLE 0.0001
 
 #define TRACKING_FILE "/tmp/motion"
@@ -98,6 +98,9 @@ public:
 // Block position in percentage 0 - 100
        double block_x;
        double block_y;
+// Noise levels to ignore frame differences as 0-100% of (maxdiff-mindiff)
+       double noise_level;
+       double noise_rotation;
 
        int horizontal_only;
        int vertical_only;
@@ -111,6 +114,8 @@ public:
        int tracking_type;
 // Track a single frame, previous frame, or previous frame same block
        int tracking_object;
+// 1 == two pass tracking
+       int twopass;
 
 // Number of single frame to track relative to timeline start
        int64_t track_frame;
@@ -128,6 +133,8 @@ public:
        int process_buffer(VFrame **frame, int64_t start_position, double frame_rate);
        void process_global();
        void process_rotation();
+       void refine_global();
+       void refine_rotation();
        void draw_vectors(VFrame *frame);
        int is_multichannel();
        int is_realtime();
@@ -180,6 +187,7 @@ public:
        int64_t cache_key, active_key;
 // add constant frame offset values
        int dx_offset, dy_offset;
+       float dt_offset;
        int64_t tracking_frame;
 // save/load result values
        int load_ok;
@@ -276,7 +284,8 @@ public:
                VFrame *current_frame,
 // Pivot
                int block_x,
-               int block_y);
+               int block_y,
+               int passno);
        int64_t get_cache(float angle);
        void put_cache(float angle, int64_t difference);
 
index 0685b6562a9f51ebc15b7efb1d9de503b04f6ef3..f5d536539cda984fa88be21341ca850ccb6b1e87 100644 (file)
@@ -21,7 +21,7 @@
 
 #include "clip.h"
 //#include "../downsample/downsampleengine.h"
-//#include "motion.h"
+#include "motion.h"
 #include "motionscan.h"
 #include "mutex.h"
 #include "vframe.h"
@@ -78,7 +78,7 @@ void MotionScanUnit::process_package(LoadPackage *package)
                                pkg->block_x2 - pkg->block_x1, pkg->block_y2 - pkg->block_y1,
                                color_model);
 
-// printf("MotionScanUnit::process_package %d search_x=%d search_y=%d diff=%lld\n",
+// printf("MotionScanUnit::process_package %d search_x=%d search_y=%d diff=%ld\n",
 // __LINE__, server->block_x1 - pkg->search_x, server->block_y1 - pkg->search_y, pkg->difference1);
                        server->put_cache(pkg->search_x, pkg->search_y, pkg->difference1);
                }
@@ -129,10 +129,11 @@ void MotionScanUnit::put_cache(int x, int y, int64_t difference)
        cache_lock->unlock();
 }
 
-MotionScan::MotionScan(int total_clients, int total_packages)
+MotionScan::MotionScan(MotionMain *plugin, int total_clients, int total_packages)
  : LoadServer( //1, 1
                total_clients, total_packages)
 {
+       this->plugin = plugin;
        test_match = 1;
        cache_lock = new Mutex("MotionScan::cache_lock");
        downsampled_previous = 0;
@@ -164,21 +165,37 @@ void MotionScan::init_packages()
                pkg->dx = pkg->dy = 0;
 
                if( !subpixel ) {
-                       pkg->search_x = pkg->scan_x1 +
-                               (pkg->step % x_steps) * (scan_x2 - scan_x1) / x_steps;
-                       pkg->search_y = pkg->scan_y1 +
-                               (pkg->step / x_steps) * (scan_y2 - scan_y1) / y_steps;
                        pkg->sub_x = pkg->sub_y = 0;
+                       int ipkg = pkg->step;
+                       if (ipkg == 0)
+                       {
+// First package additionally checks position nearest to the best found so far
+                               pkg->search_x = x_result;
+                               pkg->search_y = y_result;
+                               if (pkg->search_x < pkg->scan_x1) pkg->search_x = pkg->scan_x1;
+                               if (pkg->search_x >= pkg->scan_x2) pkg->search_x = pkg->scan_x2-1;
+                               if (pkg->search_y < pkg->scan_y1) pkg->search_y = pkg->scan_y1;
+                               if (pkg->search_y >= pkg->scan_y2) pkg->search_y = pkg->scan_y2-1;
+                       }
+                       else
+                       {
+                               ipkg --;
+                               pkg->search_x = pkg->scan_x1 + (ipkg % x_steps) *
+                                       (scan_x2 - scan_x1) / x_steps;
+                               pkg->search_y = pkg->scan_y1 + (ipkg / x_steps) *
+                                       (scan_y2 - scan_y1) / y_steps;
+                       }
                }
                else {
-                       pkg->sub_x = pkg->step % (OVERSAMPLE * 2);
-                       pkg->sub_y = pkg->step / (OVERSAMPLE * 2);
+                       pkg->sub_x = pkg->step % x_steps;
+                       pkg->sub_y = pkg->step / x_steps;
 
-                       if( horizontal_only ) pkg->sub_y = 0;
-                       if( vertical_only ) pkg->sub_x = 0;
+// Unidirectional cases already taken into account in x_steps and y_steps
+//                     if( horizontal_only ) pkg->sub_y = 0;
+//                     if( vertical_only ) pkg->sub_x = 0;
 
-                       pkg->search_x = pkg->scan_x1 + pkg->sub_x / OVERSAMPLE + 1;
-                       pkg->search_y = pkg->scan_y1 + pkg->sub_y / OVERSAMPLE + 1;
+                       pkg->search_x = pkg->scan_x1 + pkg->sub_x / OVERSAMPLE;
+                       pkg->search_y = pkg->scan_y1 + pkg->sub_y / OVERSAMPLE;
                        pkg->sub_x %= OVERSAMPLE;
                        pkg->sub_y %= OVERSAMPLE;
 
@@ -214,7 +231,7 @@ void MotionScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
                int horizontal_only, int vertical_only,
                int source_position, int total_steps, int total_dx,
                int total_dy, int global_origin_x, int global_origin_y,
-               int load_ok, int load_dx, int load_dy)
+               int passno, int load_ok, int load_dx, int load_dy)
 {
        this->previous_frame_arg = previous_frame;
        this->current_frame_arg = current_frame;
@@ -238,23 +255,56 @@ void MotionScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
        int block_w = w * global_block_w / 100;
        int block_h = h * global_block_h / 100;
 
+// passno == 0: single pass tracking
+// passno == 1: 1st pass of two-pass tracking (skip subpixel)
+// passno == 2: 2nd pass of two-pass tracking (reduce search area)
+// Save may be needed for 2nd pass
+       int dx_saved = 0;
+       int dy_saved = 0;
+       if (passno == 2)
+       {
+               dx_saved = dx_result;
+               dy_saved = dy_result;
+       }
+
 // Location of block in previous frame
-       block_x1 = (int)(w * block_x / 100 - block_w / 2);
-       block_y1 = (int)(h * block_y / 100 - block_h / 2);
-       block_x2 = (int)(w * block_x / 100 + block_w / 2);
-       block_y2 = (int)(h * block_y / 100 + block_h / 2);
+       double tmp_x1, tmp_y1;
+       double tmp_x2, tmp_y2;
+       tmp_x1 = w * block_x / 100 - block_w / 2;
+       tmp_y1 = h * block_y / 100 - block_h / 2;
+       tmp_x2 = w * block_x / 100 + block_w / 2;
+       tmp_y2 = h * block_y / 100 + block_h / 2;
+
+// Attention, process_buffer feeds previous_frame and current_frame interchanged
 
 // Offset to location of previous block.  This offset needn't be very accurate
-// since it's the offset of the previous image and current image we want.
+// since it's the offset on the previous image while current image is moved.
+// Nevertheless compute it in floating point and round to int afterwards.
        if( frame_type == MotionScan::TRACK_PREVIOUS ) {
-               block_x1 += total_dx / OVERSAMPLE;
-               block_y1 += total_dy / OVERSAMPLE;
-               block_x2 += total_dx / OVERSAMPLE;
-               block_y2 += total_dy / OVERSAMPLE;
+               tmp_x1 += (double)total_dx / OVERSAMPLE;
+               tmp_y1 += (double)total_dy / OVERSAMPLE;
+               tmp_x2 += (double)total_dx / OVERSAMPLE;
+               tmp_y2 += (double)total_dy / OVERSAMPLE;
+// Compensate displacement computed in the 1st pass
+               if (passno == 2)
+               {
+                       tmp_x1 -= (double)dx_saved / OVERSAMPLE;
+                       tmp_y1 -= (double)dy_saved / OVERSAMPLE;
+                       tmp_x2 -= (double)dx_saved / OVERSAMPLE;
+                       tmp_y2 -= (double)dy_saved / OVERSAMPLE;
+               }
        }
-
+       block_x1 = lrint (tmp_x1);
+       block_y1 = lrint (tmp_y1);
+       block_x2 = lrint (tmp_x2);
+       block_y2 = lrint (tmp_y2);
+
+// Preinitialize sane translation results
+       dx_result = 0;
+       dy_result = 0;
        skip = 0;
 
+//printf("MotionScan::scan_frame %d frame=%ld passno=%d\n", __LINE__, plugin->get_source_position(), passno);
        switch( tracking_type ) {
 // Don't calculate
        case MotionScan::NO_CALCULATE:
@@ -267,19 +317,22 @@ void MotionScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
                if( load_ok ) {
                        dx_result = load_dx;
                        dy_result = load_dy;
+                       if (passno == 2) dx_result = dy_result = 0;
                        skip = 1;
                }
                break;
 
-// Scan from scratch
+// Scan from scratch with sane translation results
        default:
+               dx_result = 0;
+               dy_result = 0;
                skip = 0;
                break;
        }
 
        if( !skip && test_match ) {
                if( previous_frame->data_matches(current_frame) ) {
-                       printf("MotionScan::scan_frame: data matches. skipping.\n");
+//                     printf("MotionScan::scan_frame: data matches. skipping.\n");
                        dx_result = dy_result = 0;
                        skip = 1;
                }
@@ -290,13 +343,25 @@ void MotionScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
 // Location of block in current frame
                int origin_offset_x = this->global_origin_x * w / 100;
                int origin_offset_y = this->global_origin_y * h / 100;
-               int x_result = block_x1 + origin_offset_x;
-               int y_result = block_y1 + origin_offset_y;
+               x_result = block_x1 + origin_offset_x;
+               y_result = block_y1 + origin_offset_y;
+               dx_result = 0;
+               dy_result = 0;
 
 //printf("MotionScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
 // block_x1 + block_w / 2, block_y1 + block_h / 2,
 // block_w, block_h, block_x1, block_y1, block_x2, block_y2);
 
+               if (passno == 2)
+               {
+// Evtl reduce scan area for refinement pass to gain speed
+// For 1st pass subpixel search will be skipped
+                       if (scan_w > 64) scan_w /= 4;
+                       else if (scan_w > 16) scan_w = 16;
+                       if (scan_h > 64) scan_h /= 4;
+                       else if (scan_h > 16) scan_h = 16;
+               }
+
                while(1) {
 // Cache needs to be cleared if downsampling is used because the sums of
 // different downsamplings can't be compared.
@@ -306,6 +371,8 @@ void MotionScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
                        scan_y1 = y_result - scan_h / 2;
                        scan_x2 = x_result + scan_w / 2;
                        scan_y2 = y_result + scan_h / 2;
+                       if (scan_x2 <= scan_x1) scan_x2 = scan_x1 + 1;
+                       if (scan_y2 <= scan_y1) scan_y2 = scan_y1 + 1;
 
 // Zero out requested values
                        if( horizontal_only ) {
@@ -325,7 +392,7 @@ void MotionScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
 // printf("MotionScan::scan_frame 1 %d block_x1=%d block_y1=%d block_x2=%d block_y2=%d\n"
 //  "    scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d\n"
 //  "    x_result=%d y_result=%d\n", __LINE__, block_x1, block_y1, block_x2, block_y2,
-//      scan_x1, scan_y1, scan_x2, scan_y2, x_result, y_result);
+//       scan_x1, scan_y1, scan_x2, scan_y2, x_result, y_result);
 
 // Give up if invalid coords.
                        if (scan_y2 <= scan_y1 || scan_x2 <= scan_x1 ||
@@ -336,23 +403,25 @@ void MotionScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
                        if( subpixel ) {
 
 //printf("MotionScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result);
-// Scan every subpixel in a 2 pixel * 2 pixel square
-                               total_pixels = (2 * OVERSAMPLE) * (2 * OVERSAMPLE);
-
+// Scan every subpixel in a scan_w * scan_h pixel square
+                               x_steps = (scan_x2 - scan_x1) * OVERSAMPLE;
+                               y_steps = (scan_y2 - scan_y1) * OVERSAMPLE;
+                               if(horizontal_only) y_steps = 1;
+                               if(vertical_only)   x_steps = 1;
+                               if (x_steps < 1) x_steps = 1;
+                               if (y_steps < 1) y_steps = 1;
+
+                               total_pixels = x_steps * y_steps;
                                this->total_steps = total_pixels;
-// These aren't used in subpixel
-                               this->x_steps = OVERSAMPLE * 2;
-                               this->y_steps = OVERSAMPLE * 2;
 
                                set_package_count(this->total_steps);
                                process_packages();
 
 // Get least difference
-                               int64_t min_difference = -1;
+                               int64_t min_difference = -1, max_difference = -1, noiselev = -1;
+                               double radius, best_radius;
                                for( int i = 0; i < get_total_packages(); i++ ) {
                                        MotionScanPackage *pkg = (MotionScanPackage *)get_package(i);
-//printf("MotionScan::scan_frame %d search_x=%d search_y=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n",
-//__LINE__, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, pkg->difference1, pkg->difference2);
                                        if( pkg->difference1 < min_difference ||
                                            min_difference == -1 ) {
                                                min_difference = pkg->difference1;
@@ -363,9 +432,13 @@ void MotionScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
 // Fill in results
                                                dx_result = block_x1 * OVERSAMPLE - x_result;
                                                dy_result = block_y1 * OVERSAMPLE - y_result;
-//printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n",
+//printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff1=%ld\n",
 //__LINE__, dx_result, dy_result, min_difference);
                                        }
+                                       if(pkg->difference1 > max_difference || max_difference == -1)
+                                       {
+                                               max_difference = pkg->difference1;
+                                       }
 
                                        if( pkg->difference2 < min_difference ) {
                                                min_difference = pkg->difference2;
@@ -375,9 +448,63 @@ void MotionScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
 
                                                dx_result = block_x1 * OVERSAMPLE - x_result;
                                                dy_result = block_y1 * OVERSAMPLE - y_result;
-//printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n",
+//printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff2=%ld\n",
 //__LINE__, dx_result, dy_result, min_difference);
                                        }
+                                       if(pkg->difference2 > max_difference)
+                                       {
+                                               max_difference = pkg->difference2;
+                                       }
+//printf("MotionScan::scan_frame %d pkg=%d search_x=%d search_y=%d sub_x=%d sub_y=%d diff1=%ld diff2=%ld min_diff=%ld max_diff=%ld\n",
+//__LINE__, i, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, pkg->difference1, pkg->difference2, min_difference, max_difference);
+                               }
+// Determine noise level (not active on pass 2)
+                               noiselev = min_difference+(max_difference-min_difference)*plugin->config.noise_level/100;
+                               if (passno == 2) noiselev = min_difference;
+//printf("MotionScan::scan_frame min_diff=%ld max_diff=%ld noiselev=%ld\n", min_difference, max_difference, noiselev);
+                               for(int i = 0; i < get_total_packages(); i++)
+                               {
+                                       MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
+                                       if(pkg->difference1 <= noiselev)
+                                       {
+// Below noise level - check subpixel translation radius
+                                               radius = hypot(
+                                                 (double)((block_x1-pkg->search_x)*OVERSAMPLE-pkg->sub_x),
+                                                 (double)((block_y1-pkg->search_y)*OVERSAMPLE-pkg->sub_y));
+                                               best_radius = hypot((double)dx_result,(double)dy_result);
+                                               if(radius < best_radius ||
+                                                  (radius == best_radius && pkg->difference1 < min_difference))
+                                               {
+// Below noise level and smaller translation, memorize
+                                                       min_difference = pkg->difference1;
+                                                       x_result = pkg->search_x * OVERSAMPLE + pkg->sub_x;
+                                                       y_result = pkg->search_y * OVERSAMPLE + pkg->sub_y;
+                                                       dx_result = block_x1 * OVERSAMPLE - x_result;
+                                                       dy_result = block_y1 * OVERSAMPLE - y_result;
+//printf("MotionScan::scan_frame %d diff1 override=%d search_x=%d search_y=%d sub_x=%d sub_y=%d dx_result=%d dy_result=%d diff1=%ld min_diff=%ld\n", 
+//__LINE__, i, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, dx_result, dy_result, pkg->difference1, min_difference);
+                                               }
+                                       }
+                                       if(pkg->difference2 <= noiselev)
+                                       {
+// Below noise level - check subpixel translation radius
+                                               radius = hypot(
+                                                 (double)((block_x1-pkg->search_x)*OVERSAMPLE+pkg->sub_x),
+                                                 (double)((block_y1-pkg->search_y)*OVERSAMPLE+pkg->sub_y));
+                                               best_radius = hypot((double)dx_result,(double)dy_result);
+                                               if(radius < best_radius ||
+                                                  (radius == best_radius && pkg->difference2 < min_difference))
+                                               {
+// Below noise level and smaller translation, memorize
+                                                       min_difference = pkg->difference2;
+                                                       x_result = pkg->search_x * OVERSAMPLE - pkg->sub_x;
+                                                       y_result = pkg->search_y * OVERSAMPLE - pkg->sub_y;
+                                                       dx_result = block_x1 * OVERSAMPLE - x_result;
+                                                       dy_result = block_y1 * OVERSAMPLE - y_result;
+//printf("MotionScan::scan_frame %d diff2 override=%d search_x=%d search_y=%d sub_x=%d sub_y=%d dx_result=%d dy_result=%d diff2=%ld min_diff=%ld\n", 
+//__LINE__, i, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, dx_result, dy_result, pkg->difference2, min_difference);
+                                               }
+                                       }
                                }
 
                                break;
@@ -397,6 +524,11 @@ void MotionScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
                                        x_steps = (int)sqrt(this->total_steps);
                                        y_steps = (int)sqrt(this->total_steps);
                                }
+                               if(horizontal_only) y_steps = 1;
+                               if(vertical_only)   x_steps = 1;
+                               if (x_steps < 1) x_steps = 1;
+                               if (y_steps < 1) y_steps = 1;
+                               this->total_steps = x_steps * y_steps;
 
 // Use downsampled images
 //                             if( scan_x2 - scan_x1 > x_steps * 4 ||
@@ -445,37 +577,107 @@ void MotionScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
 // printf("MotionScan::scan_frame %d this->total_steps=%d\n",
 // __LINE__, this->total_steps);
 
-                               set_package_count(this->total_steps);
+// One extra step for the position with zero translation
+                               set_package_count(this->total_steps+1);
                                process_packages();
 
 // Get least difference
-                               int64_t min_difference = -1;
+                               int64_t min_difference = -1, max_difference = -1, noiselev = -1;
+                               double radius, best_radius;
                                for( int i = 0; i < get_total_packages(); i++ ) {
                                        MotionScanPackage *pkg = (MotionScanPackage *) get_package(i);
-//printf("MotionScan::scan_frame %d search_x=%d search_y=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n",
-// __LINE__, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, pkg->difference1, pkg->difference2);
                                        if (pkg->difference1 < min_difference
                                            || min_difference == -1) {
                                                min_difference = pkg->difference1;
                                                x_result = pkg->search_x;
                                                y_result = pkg->search_y;
-                                               x_result *= OVERSAMPLE;
-                                               y_result *= OVERSAMPLE;
-//printf("MotionScan::scan_frame %d x_result=%d y_result=%d diff=%lld\n",
-//__LINE__, block_x1 * OVERSAMPLE - x_result, block_y1 * OVERSAMPLE - y_result, pkg->difference1);
+                                       }
+                                       if(pkg->difference1 > max_difference || max_difference == -1)
+                                       {
+                                               max_difference = pkg->difference1;
+                                       }
+//printf("MotionScan::scan_frame %d pkg=%d search_x=%d search_y=%d diff=%ld min_diff=%ld max_diff=%ld\n",
+//__LINE__, i, pkg->search_x, pkg->search_y, pkg->difference1, min_difference, max_difference);
+                               }
+// Determine noise level (not active on pass 2)
+                               noiselev = min_difference+(max_difference-min_difference)*plugin->config.noise_level/100;
+                               if (passno == 2) noiselev = min_difference;
+//printf("MotionScan::scan_frame min_diff=%ld max_diff=%ld noiselev=%ld\n", min_difference, max_difference, noiselev);
+                               for(int i = 0; i < get_total_packages(); i++)
+                               {
+                                       MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
+// Already found as the best search, not necessary to memorize
+                                       if(x_result == pkg->search_x && y_result == pkg->search_y) continue;
+// Above noise level - a definitely bad search, skip
+                                       if(pkg->difference1 > noiselev) continue;
+                                       radius = hypot((double)(block_x1-pkg->search_x),(double)(block_y1-pkg->search_y));
+                                       best_radius = hypot((double)(block_x1-x_result),(double)(block_y1-y_result));
+// Below noise level but bigger translation, skip
+                                       if(radius > best_radius) continue;
+// Below noise level and smaller translation, memorize
+                                       if(radius < best_radius)
+                                       {
+                                               min_difference = pkg->difference1;
+                                               x_result = pkg->search_x;
+                                               y_result = pkg->search_y;
+//printf("MotionScan::scan_frame %d search override=%d search_x=%d search_y=%d diff=%ld min_diff=%ld\n", 
+//__LINE__, i, pkg->search_x, pkg->search_y, pkg->difference1, min_difference);
+                                               continue;
+                                       }
+// Equal translations, memorize search with min difference
+                                       if(pkg->difference1 < min_difference)
+                                       {
+                                               min_difference = pkg->difference1;
+                                               x_result = pkg->search_x;
+                                               y_result = pkg->search_y;
+//printf("MotionScan::scan_frame %d difference override=%d search_x=%d search_y=%d diff=%ld min_diff=%ld\n", 
+//__LINE__, i, pkg->search_x, pkg->search_y, pkg->difference1, min_difference);
+                                               continue;
+                                       }
+                               }
+                               int rad_x = 0, rad_y = 0;
+                               for(int i = 0; i < get_total_packages(); i++)
+                               {
+                                       MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
+// Above noise level - skip this radius
+                                       if(pkg->difference1 > noiselev) continue;
+// Below noise level - measure max distance from the best position
+                                       if(rad_x < abs(x_result-pkg->search_x))
+                                       {
+                                               rad_x = abs(x_result-pkg->search_x);
+//printf("MotionScan::scan_frame %d X-radius override=%d search_x=%d radius=%d\n", 
+//__LINE__, i, pkg->search_x, rad_x);
+                                       }
+                                       if(rad_y < abs(y_result-pkg->search_y))
+                                       {
+                                               rad_y = abs(y_result-pkg->search_y);
+//printf("MotionScan::scan_frame %d Y-radius override=%d search_y=%d radius=%d\n", 
+//__LINE__, i, pkg->search_y, rad_y);
                                        }
                                }
+                               x_result *= OVERSAMPLE;
+                               y_result *= OVERSAMPLE;
+//printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff=%ld\n", 
+//__LINE__, block_x1 * OVERSAMPLE - x_result, block_y1 * OVERSAMPLE - y_result, min_difference);
 
 // If a new search is required, rescale results back to pixels.
                                if( this->total_steps >= total_pixels ) {
 // Single pixel accuracy reached.  Now do exhaustive subpixel search.
-                                       if( action_type == MotionScan::STABILIZE ||
-                                               action_type == MotionScan::TRACK ||
-                                               action_type == MotionScan::NOTHING ) {
+// Subpixel search skipped on the 1st pass of a two-pass search.
+                                       if((action_type == MotionScan::STABILIZE ||
+                                           action_type == MotionScan::TRACK ||
+                                           action_type == MotionScan::NOTHING) &&
+                                          passno != 1)
+                                       {
 //printf("MotionScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result);
+// Subpixel scan area might be +/- one pixel from the best position
+                                               scan_w = 2;
+                                               scan_h = 2;
+// Evtl expand scan area to +/- two pixels if some samples below noise
+                                               if (rad_x > 0) scan_w = 4;
+                                               if (rad_y > 0) scan_h = 4;
                                                x_result /= OVERSAMPLE;
                                                y_result /= OVERSAMPLE;
-                                               scan_w = scan_h = 2;
                                                subpixel = 1;
                                        }
 // Fill in results and quit
@@ -488,13 +690,27 @@ void MotionScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
                                }
 // Reduce scan area and try again
                                else {
-                                       scan_w = (scan_x2 - scan_x1) / 2;
-                                       scan_h = (scan_y2 - scan_y1) / 2;
+// Optimum new scan area might be +/- one search step from the best position
+                                       scan_w = (scan_x2 - scan_x1) * 2 / x_steps;
+                                       scan_h = (scan_y2 - scan_y1) * 2 / y_steps;
+// Evtl expand scan area to +/- two search steps if some samples below noise
+                                       if (rad_x > 0) scan_w = (scan_x2 - scan_x1) * 4 / x_steps;
+                                       if (rad_y > 0) scan_h = (scan_y2 - scan_y1) * 4 / y_steps;
+// Evtl expand scan area to include samples below noise level
+                                       if (scan_w < rad_x * 2) scan_w = rad_x * 2;
+                                       if (scan_h < rad_y * 2) scan_h = rad_y * 2;
+// Always reduce scan area at least twice
+                                       if (scan_w > (scan_x2 - scan_x1) / 2) scan_w = (scan_x2 - scan_x1) / 2;
+                                       if (scan_h > (scan_y2 - scan_y1) / 2) scan_h = (scan_y2 - scan_y1) / 2;
+// But retain scan area at least one pixel in size
+                                       if (scan_w < 1) scan_w = 1;
+                                       if (scan_h < 1) scan_h = 1;
                                        x_result /= OVERSAMPLE;
                                        y_result /= OVERSAMPLE;
                                }
                        }
                }
+// Negate results as previous_frame and current_frame have been interchanged
                dx_result = -dx_result;
                dy_result = -dy_result;
        }
@@ -503,8 +719,14 @@ void MotionScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
        if( vertical_only ) dx_result = 0;
        if( horizontal_only ) dy_result = 0;
 
-//printf("MotionScan::scan_frame %d dx=%.2f dy=%.2f\n",
-// __LINE__, (float)this->dx_result / OVERSAMPLE, (float)this->dy_result / OVERSAMPLE);
+// printf("MotionScan::scan_frame %d passno=%d saved dx=%.2f dy=%.2f measured dx=%.2f dy=%.2f twopass dx=%.2f dy=%.2f\n", 
+// __LINE__, passno,
+// (float)dx_saved / OVERSAMPLE,
+// (float)dy_saved / OVERSAMPLE,
+// (float)this->dx_result / OVERSAMPLE,
+// (float)this->dy_result / OVERSAMPLE,
+// (float)(this->dx_result + dx_saved) / OVERSAMPLE,
+// (float)(this->dy_result + dy_saved) / OVERSAMPLE);
 }
 
 int64_t MotionScan::get_cache(int x, int y)
index 32aeb5aa765ab81c85b3d8ad84d55b72659a6a1b..7ba8843920eda0c9fe15a66be48b89e99414650c 100644 (file)
@@ -89,7 +89,8 @@ public:
 class MotionScan : public LoadServer
 {
 public:
-       MotionScan(int total_clients,
+       MotionScan(MotionMain *plugin,
+               int total_clients,
                int total_packages);
        ~MotionScan();
 
@@ -107,7 +108,7 @@ public:
                int global_range_w, int global_range_h, int global_block_w, int global_block_h,
                double block_x, double block_y, int frame_type, int tracking_type, int action_type,
                int horizontal_only, int vertical_only, int source_position, int total_steps,
-               int total_dx, int total_dy, int global_origin_x, int global_origin_y,
+               int total_dx, int total_dy, int global_origin_x, int global_origin_y, int passno,
                int load_ok=0, int load_dx=0, int load_dy=0);
        int64_t get_cache(int x, int y);
        void put_cache(int x, int y, int64_t difference);
@@ -126,6 +127,9 @@ public:
 // OVERSAMPLE
        int dx_result, dy_result;
 
+// Currently best expected location of scan block
+       int x_result, y_result;
+
        enum { // action_type
                TRACK,
                STABILIZE,
@@ -158,6 +162,7 @@ private:
 // Downsampled frames
        VFrame *downsampled_previous;
        VFrame *downsampled_current;
+       MotionMain *plugin;
 // Test for identical frames before processing
 // Faster to skip it if the frames are usually different
        int test_match;
index e799aee68224cf0812dd8fd087af518c64de2791..b96bea48d301cc07a837363dfc95741d1677b2dc 100644 (file)
@@ -32,7 +32,7 @@
 #include "pluginserver.h"
 
 MotionWindow::MotionWindow(MotionMain *plugin)
- : PluginClientWindow(plugin, xS(800), yS(640), xS(800), yS(640), 0)
+ : PluginClientWindow(plugin, xS(800), yS(750), xS(800), yS(750), 0)
 {
        this->plugin = plugin;
 }
@@ -43,23 +43,23 @@ MotionWindow::~MotionWindow()
 
 void MotionWindow::create_objects()
 {
-       int xs5 = xS(5), xs10 = xS(10), xs20 = xS(20), xs50 = xS(50), xs120 = xS(120);
-       int ys10 = yS(10), ys20 = yS(20), ys50 = yS(50), ys30 = yS(30), ys40 = yS(40), ys60 = yS(60);
+       int xs10 = xS(10), xs20 = xS(20), xs50 = xS(50), xs120 = xS(120);
+       int ys10 = yS(10), ys20 = yS(20), ys50 = yS(50), ys30 = yS(30), ys40 = yS(40);
        int x = xs10, y = ys10;
-       int x1 = x, x2 = get_w() / 2;
+       int x2 = get_w() / 2;
        BC_Title *title;
 
-       add_subwindow(global = new MotionGlobal(plugin, this, x1, y));
+       add_subwindow(global = new MotionGlobal(plugin, this, x, y));
        add_subwindow(rotate = new MotionRotate(plugin, this, x2, y));
        y += ys50;
 
-       add_subwindow(title = new BC_Title(x1, y,
+       add_subwindow(title = new BC_Title(x, y,
                _("Translation search radius:\n(W/H Percent of image)")));
        add_subwindow(global_range_w = new GlobalRange(plugin,
-               x1 + title->get_w() + xs10, y,
+               x + title->get_w() + xs10, y,
                &plugin->config.global_range_w));
        add_subwindow(global_range_h = new GlobalRange(plugin,
-               x1 + title->get_w() + xs10 + global_range_w->get_w(), y,
+               x + title->get_w() + xs10 + global_range_w->get_w(), y,
                &plugin->config.global_range_h));
 
        add_subwindow(title = new BC_Title(x2, y,
@@ -68,13 +68,13 @@ void MotionWindow::create_objects()
                x2 + title->get_w() + xs10, y));
 
        y += ys50;
-       add_subwindow(title = new BC_Title(x1, y,
+       add_subwindow(title = new BC_Title(x, y,
                _("Translation block size:\n(W/H Percent of image)")));
        add_subwindow(global_block_w =
-               new BlockSize(plugin, x1 + title->get_w() + xs10, y,
+               new BlockSize(plugin, x + title->get_w() + xs10, y,
                &plugin->config.global_block_w));
        add_subwindow(global_block_h =
-               new BlockSize(plugin, x1 + title->get_w() + xs10 +
+               new BlockSize(plugin, x + title->get_w() + xs10 +
                        global_block_w->get_w(), y,
                        &plugin->config.global_block_h));
 
@@ -91,9 +91,9 @@ void MotionWindow::create_objects()
 //             &plugin->config.rotation_block_h));
 
        y += ys50;
-       add_subwindow(title = new BC_Title(x1, y, _("Translation search steps:")));
+       add_subwindow(title = new BC_Title(x, y, _("Translation search steps:")));
        add_subwindow(global_search_positions =
-               new GlobalSearchPositions(plugin, x1 + title->get_w() + xs10, y, xs120));
+               new GlobalSearchPositions(plugin, x + title->get_w() + xs10, y, xs120));
        global_search_positions->create_objects();
 
        add_subwindow(title = new BC_Title(x2, y, _("Rotation search steps:")));
@@ -110,11 +110,6 @@ void MotionWindow::create_objects()
        track_direction->create_objects();
 
        y += ys40;
-       add_subwindow(title = new BC_Title(x2, y, _("Tracking file:")));
-       add_subwindow(tracking_file = new MotionTrackingFile(plugin,
-               plugin->config.tracking_file, this, x2+title->get_w() + xs20, y));
-
-       int y1 = y;
        add_subwindow(title = new BC_Title(x, y + ys10, _("Block X:")));
        add_subwindow(block_x =
                new MotionBlockX(plugin, this, x + title->get_w() + xs10, y));
@@ -122,24 +117,11 @@ void MotionWindow::create_objects()
                new MotionBlockXText(plugin, this,
                        x + title->get_w() + xs10 + block_x->get_w() + xs10, y + ys10));
 
-       y += ys40;
-       add_subwindow(title = new BC_Title(x2, y, _("Rotation center:")));
+       add_subwindow(title = new BC_Title(x2, y + ys10, _("Rotation center:")));
        add_subwindow(rotation_center =
                new RotationCenter(plugin, x2 + title->get_w() + xs10, y));
 
        y += ys40;
-       add_subwindow(title = new BC_Title(x2, y + ys10, _("Maximum angle offset:")));
-       add_subwindow(rotate_magnitude =
-               new MotionRMagnitude(plugin, x2 + title->get_w() + xs10, y));
-
-       y += ys40;
-       add_subwindow(title = new BC_Title(x2, y + ys10, _("Rotation settling speed:")));
-       add_subwindow(rotate_return_speed =
-               new MotionRReturnSpeed(plugin, x2 + title->get_w() + xs10, y));
-       y += ys40;
-       add_subwindow(vectors = new MotionDrawVectors(plugin, this, x2, y));
-
-       y = y1 + ys60;
        add_subwindow(title = new BC_Title(x, y + ys10, _("Block Y:")));
        add_subwindow(block_y =
                new MotionBlockY(plugin, this, x + title->get_w() + xs10, y));
@@ -153,52 +135,96 @@ void MotionWindow::create_objects()
                x + title->get_w() + xs10,
                y));
 
+       add_subwindow(title = new BC_Title(x2, y + ys10, _("Maximum angle offset:")));
+       add_subwindow(rotate_magnitude =
+               new MotionRMagnitude(plugin, x2 + title->get_w() + xs10, y));
+
        y += ys40;
        add_subwindow(title = new BC_Title(x, y + ys10, _("Motion settling speed:")));
        add_subwindow(return_speed =
                new MotionReturnSpeed(plugin, x + title->get_w() + xs10, y));
 
+       add_subwindow(title = new BC_Title(x2, y + ys10, _("Rotation settling speed:")));
+       add_subwindow(rotate_return_speed =
+               new MotionRReturnSpeed(plugin, x2 + title->get_w() + xs10, y));
+
+       y += ys40;
+       add_subwindow(title = new BC_Title(x, y, _("Motion noise level:\n(% of max diff.)")));
+       add_subwindow(noise_level =
+               new MotionNoiseLevel(plugin, this, x + title->get_w() + xs10, y));
+       add_subwindow(noise_level_text =
+               new MotionNoiseLevelText(plugin, this,
+                       x + title->get_w() + xs10 + noise_level->get_w() + xs10,        y + ys10));
+
+       add_subwindow(title = new BC_Title(x2, y, _("Rotation noise level:\n(% of max diff.)")));
+       add_subwindow(noise_rotation =
+               new MotionNoiseRotation(plugin, this, x2 + title->get_w() + xs10, y));
+       add_subwindow(noise_rotation_text =
+               new MotionNoiseRotationText(plugin, this,
+                       x2 + title->get_w() + xs10 + noise_rotation->get_w() + xs10, y + ys10));
+
+       y += ys50;
+       add_subwindow(vectors = new MotionDrawVectors(plugin, this, x, y));
+       add_subwindow(twopass = new MotionTwopass(plugin, this, x2, y));
+
        y += ys40;
        add_subwindow(track_single =
                new TrackSingleFrame(plugin, this, x, y));
+
+       add_subwindow(title = new BC_Title(x2, y, _("Frame number:")));
+       add_subwindow(track_frame_number =
+               new TrackFrameNumber(plugin, this, x2 + title->get_w() + xs10, y));
+       add_subwindow(frame_current =
+               new MotionFrameCurrent(plugin, this, x2 + title->get_w() + track_frame_number->get_w() + xs20, y));
+       if(plugin->config.tracking_object != MotionScan::TRACK_SINGLE)
+       {
+               track_frame_number->disable();
+               frame_current->disable();
+       }
+
        y += ys20;
        add_subwindow(track_previous =
                new TrackPreviousFrame(plugin, this, x, y));
+
        y += ys20;
        add_subwindow(previous_same =
                new PreviousFrameSameBlock(plugin, this, x, y));
 
+       add_subwindow(addtrackedframeoffset =
+               new AddTrackedFrameOffset(plugin, this, x2, y));
+
        y += ys40;
-       x1 = x;  y1 = y;
-       add_subwindow(title =
-               new BC_Title(x1=x2, y1, _("Frame number:")));
-       add_subwindow(track_frame_number =
-               new TrackFrameNumber(plugin, this, x1 += title->get_w(), y1));
-       if(plugin->config.tracking_object != MotionScan::TRACK_SINGLE)
-               track_frame_number->disable();
+       add_subwindow(title = new BC_Title(x, y, _("Tracking file:")));
+       add_subwindow(tracking_file = new MotionTrackingFile(plugin,
+               plugin->config.tracking_file, this, x+title->get_w() + xs10, y));
 
-       add_subwindow(addtrackedframeoffset =
-               new AddTrackedFrameOffset(plugin, this, x1=x2, y1+=track_frame_number->get_h()));
-       int pef = client->server->mwindow->edl->session->video_every_frame;
-       add_subwindow(pef_title = new BC_Title(x1=x2+xs50, y1+=addtrackedframeoffset->get_h() + xs5,
-               !pef ?  _("For best results\n"
-                               " Set: Play every frame\n"
-                               " Preferences-> Playback-> Video Out") :
-                       _("Currently using: Play every frame"), MEDIUMFONT,
-               !pef ? RED : GREEN));
+       add_subwindow(reset_tracking =
+               new MotionResetTracking(plugin, this, x2, y));
 
+       y += ys40;
        add_subwindow(title = new BC_Title(x, y, _("Master layer:")));
        add_subwindow(master_layer = new MasterLayer(plugin,
                this, x + title->get_w() + xs10, y));
        master_layer->create_objects();
-       y += ys30;
 
+       add_subwindow(clear_tracking =
+               new MotionClearTracking(plugin, this, x2, y - ys10));
+
+       y += ys30;
        add_subwindow(title = new BC_Title(x, y, _("Action:")));
        add_subwindow(action_type = new ActionType(plugin,
                this, x + title->get_w() + xs10, y));
        action_type->create_objects();
-       y += ys30;
 
+       int pef = client->server->mwindow->edl->session->video_every_frame;
+       add_subwindow(pef_title = new BC_Title(x2+xs50, y,
+               !pef ?  _("For best results\n"
+                               " Set: Play every frame\n"
+                               " Preferences-> Playback-> Video Out") :
+                       _("Currently using: Play every frame"), MEDIUMFONT,
+               !pef ? RED : GREEN));
+
+       y += ys30;
        add_subwindow(title = new BC_Title(x, y, _("Calculation:")));
        add_subwindow(tracking_type = new TrackingType(plugin,
                this, x + title->get_w() + xs10, y));
@@ -216,6 +242,7 @@ void MotionWindow::update_mode()
        rotation_range->update(plugin->config.rotation_range,
                MIN_ROTATION, MAX_ROTATION);
        vectors->update(plugin->config.draw_vectors);
+       twopass->update(plugin->config.twopass);
        tracking_file->update(plugin->config.tracking_file);
        global->update(plugin->config.global);
        rotate->update(plugin->config.rotate);
@@ -224,7 +251,7 @@ void MotionWindow::update_mode()
 
 MotionTrackingFile::MotionTrackingFile(MotionMain *plugin,
        const char *filename, MotionWindow *gui, int x, int y)
- : BC_TextBox(x, y, xS(150), 1, filename)
+ : BC_TextBox(x, y, gui->get_w()/2-x-xS(10), 1, filename)
 {
        this->plugin = plugin;
        this->gui = gui;
@@ -232,7 +259,92 @@ MotionTrackingFile::MotionTrackingFile(MotionMain *plugin,
 
 int MotionTrackingFile::handle_event()
 {
-       strcpy(plugin->config.tracking_file, get_text());
+       strncpy(plugin->config.tracking_file, get_text(), sizeof(plugin->config.tracking_file));
+       plugin->reset_cache_file();
+       plugin->send_configure_change();
+       return 1;
+}
+
+MotionResetTracking::MotionResetTracking(MotionMain *plugin, MotionWindow *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Generate tracking file name"))
+{
+       this->plugin = plugin;
+       this->gui = gui;
+};
+
+int MotionResetTracking::handle_event()
+{
+// First of all, ensure closing current tracking file
+       plugin->reset_cache_file();
+
+// Generate new tracking filename based on the asset filename
+       const char *sp = TRACKING_FILE;
+       char *cp = plugin->config.tracking_file, *ep = cp+sizeof(plugin->config.tracking_file)-1;
+       while( cp < ep && *sp != 0 ) *cp++ = *sp++;
+       if( cp < ep-1 && (sp=plugin->get_source_path()) ) {
+               *cp++ = '-';
+               const char *bp = strrchr(sp,'/');
+               if( bp ) sp = bp+1;
+               while( cp < ep && *sp != 0 ) {
+                       *cp++ = (*sp>='a' && *sp<='z') ||
+                               (*sp>='A' && *sp<='Z') ||
+                               (*sp>='0' && *sp<='9') ? *sp : '_';
+                       ++sp;
+               }
+       }
+       *cp = 0;
+
+       gui->tracking_file->update(plugin->config.tracking_file);
+       plugin->reset_cache_file();
+
+// Revert tracking type to not using tracking file
+       if( plugin->config.tracking_type == MotionScan::LOAD ||
+           plugin->config.tracking_type == MotionScan::SAVE )
+       {
+               plugin->config.tracking_type = MotionScan::NO_CALCULATE;
+               gui->tracking_type->set_text(TrackingType::to_text(plugin->config.tracking_type));
+       }
+
+       plugin->send_configure_change();
+       return 1;
+}
+
+MotionClearTracking::MotionClearTracking(MotionMain *plugin, MotionWindow *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Clear tracking file contents"))
+{
+       this->plugin = plugin;
+       this->gui = gui;
+};
+
+int MotionClearTracking::handle_event()
+{
+       char save_file[BCTEXTLEN];
+
+// First of all, ensure closing current tracking file
+       plugin->reset_cache_file();
+
+// Suffix .bak not allowed: reserved for intermediate tracking file copy
+       snprintf(save_file, sizeof(save_file), "%s.old", plugin->config.tracking_file);
+       ::rename(plugin->config.tracking_file, save_file);
+
+// Just for safety
+       ::remove(plugin->config.tracking_file);
+       plugin->reset_cache_file();
+
+       return 1;
+}
+
+MotionFrameCurrent::MotionFrameCurrent(MotionMain *plugin, MotionWindow *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Get current"))
+{
+       this->plugin = plugin;
+       this->gui = gui;
+};
+
+int MotionFrameCurrent::handle_event()
+{
+       plugin->config.track_frame = plugin->get_source_position();
+       gui->track_frame_number->update(plugin->config.track_frame);
        plugin->send_configure_change();
        return 1;
 }
@@ -482,6 +594,21 @@ int MotionRotate::handle_event()
        return 1;
 }
 
+MotionTwopass::MotionTwopass(MotionMain *plugin, 
+       MotionWindow *gui, int x, int y)
+ : BC_CheckBox(x, y, plugin->config.twopass, _("Two pass tracking"))
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+
+int MotionTwopass::handle_event()
+{
+       plugin->config.twopass = get_value();
+       plugin->send_configure_change();
+       return 1;
+}
+
 
 MotionBlockX::MotionBlockX(MotionMain *plugin,
        MotionWindow *gui, int x, int y)
@@ -560,6 +687,89 @@ int MotionBlockYText::handle_event()
 }
 
 
+MotionNoiseLevel::MotionNoiseLevel(MotionMain *plugin, 
+       MotionWindow *gui, int x, int y)
+ : BC_FPot(x, y, plugin->config.noise_level, (float)0, (float)100)
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+
+int MotionNoiseLevel::handle_event()
+{
+       float level = plugin->config.noise_level;
+       level = get_value();
+       if (level < 0)   level = 0;
+       if (level > 100) level = 100;
+       plugin->config.noise_level = level;
+       gui->noise_level_text->update((float)plugin->config.noise_level);
+       plugin->send_configure_change();
+       return 1;
+}
+
+MotionNoiseLevelText::MotionNoiseLevelText(MotionMain *plugin, 
+       MotionWindow *gui, int x, int y)
+ : BC_TextBox(x, y, xS(75), 1, (float)plugin->config.noise_level)
+{
+       this->plugin = plugin;
+       this->gui = gui;
+       set_precision(4);
+}
+
+int MotionNoiseLevelText::handle_event()
+{
+       float level = plugin->config.noise_level;
+       level = atof(get_text());
+       if (level < 0)   level = 0;
+       if (level > 100) level = 100;
+       plugin->config.noise_level = level;
+       gui->noise_level->update(plugin->config.noise_level);
+       plugin->send_configure_change();
+       return 1;
+}
+
+MotionNoiseRotation::MotionNoiseRotation(MotionMain *plugin, 
+       MotionWindow *gui, int x, int y)
+ : BC_FPot(x, y, plugin->config.noise_rotation,        (float)0, (float)100)
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+
+int MotionNoiseRotation::handle_event()
+{
+       float level = plugin->config.noise_rotation;
+       level = get_value();
+       if (level < 0)   level = 0;
+       if (level > 100) level = 100;
+       plugin->config.noise_rotation = level;
+       gui->noise_rotation_text->update((float)plugin->config.noise_rotation);
+       plugin->send_configure_change();
+       return 1;
+}
+
+MotionNoiseRotationText::MotionNoiseRotationText(MotionMain *plugin, 
+       MotionWindow *gui, int x, int y)
+ : BC_TextBox(x, y, xS(75), 1, (float)plugin->config.noise_rotation)
+{
+       this->plugin = plugin;
+       this->gui = gui;
+       set_precision(4);
+}
+
+int MotionNoiseRotationText::handle_event()
+{
+       float level = plugin->config.noise_rotation;
+       level = atof(get_text());
+       if (level < 0)   level = 0;
+       if (level > 100) level = 100;
+       plugin->config.noise_rotation = level;
+       gui->noise_rotation->update(plugin->config.noise_rotation);
+       plugin->send_configure_change();
+       return 1;
+}
+
+
 MotionDrawVectors::MotionDrawVectors(MotionMain *plugin,
        MotionWindow *gui, int x, int y)
  : BC_CheckBox(x,
@@ -598,6 +808,7 @@ int TrackSingleFrame::handle_event()
        gui->track_previous->update(0);
        gui->previous_same->update(0);
        gui->track_frame_number->enable();
+       gui->frame_current->enable();
        plugin->send_configure_change();
        return 1;
 }
@@ -638,6 +849,7 @@ int TrackPreviousFrame::handle_event()
        gui->track_single->update(0);
        gui->previous_same->update(0);
        gui->track_frame_number->disable();
+       gui->frame_current->disable();
        plugin->send_configure_change();
        return 1;
 }
@@ -661,6 +873,7 @@ int PreviousFrameSameBlock::handle_event()
        gui->track_single->update(0);
        gui->track_previous->update(0);
        gui->track_frame_number->disable();
+       gui->frame_current->disable();
        plugin->send_configure_change();
        return 1;
 }
index c1f83bd042a69a0aa86c13a55ecf60b639bd4884..b6c28144e2c147e2fa00bb7de4c57b84ce0f5f15 100644 (file)
@@ -233,6 +233,43 @@ public:
 };
 
 
+class MotionNoiseLevel : public BC_FPot
+{
+public:
+       MotionNoiseLevel(MotionMain *plugin, MotionWindow *gui, int x, int y);
+       int handle_event();
+       MotionWindow *gui;
+       MotionMain *plugin;
+};
+
+class MotionNoiseLevelText : public BC_TextBox
+{
+public:
+       MotionNoiseLevelText(MotionMain *plugin, MotionWindow *gui, int x, int y);
+       int handle_event();
+       MotionWindow *gui;
+       MotionMain *plugin;
+};
+
+class MotionNoiseRotation : public BC_FPot
+{
+public:
+       MotionNoiseRotation(MotionMain *plugin, MotionWindow *gui, int x, int y);
+       int handle_event();
+       MotionWindow *gui;
+       MotionMain *plugin;
+};
+
+class MotionNoiseRotationText : public BC_TextBox
+{
+public:
+       MotionNoiseRotationText(MotionMain *plugin, MotionWindow *gui, int x, int y);
+       int handle_event();
+       MotionWindow *gui;
+       MotionMain *plugin;
+};
+
+
 class MotionDrawVectors : public BC_CheckBox
 {
 public:
@@ -261,6 +298,33 @@ public:
        MotionWindow *gui;
 };
 
+class MotionResetTracking : public BC_GenericButton
+{
+public:
+       MotionResetTracking(MotionMain *plugin, MotionWindow *gui, int x, int y);
+       int handle_event();
+       MotionMain *plugin;
+       MotionWindow *gui;
+};
+
+class MotionClearTracking : public BC_GenericButton
+{
+public:
+       MotionClearTracking(MotionMain *plugin, MotionWindow *gui, int x, int y);
+       int handle_event();
+       MotionMain *plugin;
+       MotionWindow *gui;
+};
+
+class MotionFrameCurrent : public BC_GenericButton
+{
+public:
+       MotionFrameCurrent(MotionMain *plugin,  MotionWindow *gui, int x, int y);
+       int handle_event();
+       MotionMain *plugin;
+       MotionWindow *gui;
+};
+
 class MotionGlobal : public BC_CheckBox
 {
 public:
@@ -279,6 +343,15 @@ public:
        MotionMain *plugin;
 };
 
+class MotionTwopass : public BC_CheckBox
+{
+public:
+       MotionTwopass(MotionMain *plugin, MotionWindow *gui, int x, int y);
+       int handle_event();
+       MotionWindow *gui;
+       MotionMain *plugin;
+};
+
 
 
 class MotionWindow : public PluginClientWindow
@@ -309,11 +382,19 @@ public:
        MotionRMagnitude *rotate_magnitude;
        MotionReturnSpeed *return_speed;
        MotionRReturnSpeed *rotate_return_speed;
+       MotionNoiseLevel *noise_level;
+       MotionNoiseLevelText *noise_level_text;
+       MotionNoiseRotation *noise_rotation;
+       MotionNoiseRotationText *noise_rotation_text;
        ActionType *action_type;
        MotionDrawVectors *vectors;
        MotionTrackingFile *tracking_file;
+       MotionResetTracking *reset_tracking;
+       MotionClearTracking *clear_tracking;
+       MotionFrameCurrent *frame_current;
        MotionGlobal *global;
        MotionRotate *rotate;
+       MotionTwopass *twopass;
        AddTrackedFrameOffset *addtrackedframeoffset;
        TrackSingleFrame *track_single;
        TrackFrameNumber *track_frame_number;
diff --git a/cinelerra-5.1/plugins/motion/opencvwrapper.C b/cinelerra-5.1/plugins/motion/opencvwrapper.C
deleted file mode 100644 (file)
index 7c19be6..0000000
+++ /dev/null
@@ -1,551 +0,0 @@
-/*
- * CINELERRA
- * Copyright (C) 1997-2012 Adam Williams <broadcast at earthling dot net>
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- * 
- */
-
-
-
-#include "opencvwrapper.h"
-#include "vframe.h"
-
-#include "opencv2/calib3d/calib3d.hpp"
-#include "opencv2/objdetect/objdetect.hpp"
-#include "opencv2/features2d/features2d.hpp"
-
-
-#include <iostream>
-#include <vector>
-#include <stdio.h>
-#include <stdlib.h>
-
-
-
-
-using namespace std;
-
-
-// define whether to use approximate nearest-neighbor search
-#define USE_FLANN
-
-// Sizes must be quantized a certain amount for OpenCV
-#define QUANTIZE 8
-
-
-
-double
-compareSURFDescriptors( const float* d1, const float* d2, double best, int length )
-{
-    double total_cost = 0;
-    assert( length % 4 == 0 );
-    for( int i = 0; i < length; i += 4 )
-    {
-        double t0 = d1[i  ] - d2[i  ];
-        double t1 = d1[i+1] - d2[i+1];
-        double t2 = d1[i+2] - d2[i+2];
-        double t3 = d1[i+3] - d2[i+3];
-        total_cost += t0*t0 + t1*t1 + t2*t2 + t3*t3;
-        if( total_cost > best )
-            break;
-    }
-    return total_cost;
-}
-
-
-int
-naiveNearestNeighbor( const float* vec, int laplacian,
-                      const CvSeq* model_keypoints,
-                      const CvSeq* model_descriptors )
-{
-    int length = (int)(model_descriptors->elem_size/sizeof(float));
-    int i, neighbor = -1;
-    double d, dist1 = 1e6, dist2 = 1e6;
-    CvSeqReader reader, kreader;
-    cvStartReadSeq( model_keypoints, &kreader, 0 );
-    cvStartReadSeq( model_descriptors, &reader, 0 );
-
-    for( i = 0; i < model_descriptors->total; i++ )
-    {
-        const CvSURFPoint* kp = (const CvSURFPoint*)kreader.ptr;
-        const float* mvec = (const float*)reader.ptr;
-       CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader );
-        CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
-        if( laplacian != kp->laplacian )
-            continue;
-        d = compareSURFDescriptors( vec, mvec, dist2, length );
-        if( d < dist1 )
-        {
-            dist2 = dist1;
-            dist1 = d;
-            neighbor = i;
-        }
-        else if ( d < dist2 )
-            dist2 = d;
-    }
-    if ( dist1 < 0.6*dist2 )
-        return neighbor;
-    return -1;
-}
-
-void
-findPairs( const CvSeq* objectKeypoints, const CvSeq* objectDescriptors,
-           const CvSeq* imageKeypoints, const CvSeq* imageDescriptors, vector<int>& ptpairs )
-{
-    int i;
-    CvSeqReader reader, kreader;
-    cvStartReadSeq( objectKeypoints, &kreader );
-    cvStartReadSeq( objectDescriptors, &reader );
-    ptpairs.clear();
-
-    for( i = 0; i < objectDescriptors->total; i++ )
-    {
-        const CvSURFPoint* kp = (const CvSURFPoint*)kreader.ptr;
-        const float* descriptor = (const float*)reader.ptr;
-        CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader );
-        CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
-        int nearest_neighbor = naiveNearestNeighbor( descriptor, kp->laplacian, imageKeypoints, imageDescriptors );
-        if( nearest_neighbor >= 0 )
-        {
-            ptpairs.push_back(i);
-            ptpairs.push_back(nearest_neighbor);
-        }
-    }
-}
-
-
-void
-flannFindPairs( const CvSeq*, 
-       const CvSeq* objectDescriptors,
-    const CvSeq*, 
-       const CvSeq* imageDescriptors, 
-       vector<int>& ptpairs )
-{
-       int length = (int)(objectDescriptors->elem_size/sizeof(float));
-
-    cv::Mat m_object(objectDescriptors->total, length, CV_32F);
-       cv::Mat m_image(imageDescriptors->total, length, CV_32F);
-
-
-       // copy descriptors
-    CvSeqReader obj_reader;
-       float* obj_ptr = m_object.ptr<float>(0);
-    cvStartReadSeq( objectDescriptors, &obj_reader );
-    for(int i = 0; i < objectDescriptors->total; i++ )
-    {
-        const float* descriptor = (const float*)obj_reader.ptr;
-        CV_NEXT_SEQ_ELEM( obj_reader.seq->elem_size, obj_reader );
-        memcpy(obj_ptr, descriptor, length*sizeof(float));
-        obj_ptr += length;
-    }
-    CvSeqReader img_reader;
-       float* img_ptr = m_image.ptr<float>(0);
-    cvStartReadSeq( imageDescriptors, &img_reader );
-    for(int i = 0; i < imageDescriptors->total; i++ )
-    {
-        const float* descriptor = (const float*)img_reader.ptr;
-        CV_NEXT_SEQ_ELEM( img_reader.seq->elem_size, img_reader );
-        memcpy(img_ptr, descriptor, length*sizeof(float));
-        img_ptr += length;
-    }
-
-    // find nearest neighbors using FLANN
-    cv::Mat m_indices(objectDescriptors->total, 2, CV_32S);
-    cv::Mat m_dists(objectDescriptors->total, 2, CV_32F);
-    cv::flann::Index flann_index(m_image, cv::flann::KDTreeIndexParams(4));  // using 4 randomized kdtrees
-    flann_index.knnSearch(m_object, m_indices, m_dists, 2, cv::flann::SearchParams(64) ); // maximum number of leafs checked
-
-    int* indices_ptr = m_indices.ptr<int>(0);
-    float* dists_ptr = m_dists.ptr<float>(0);
-//printf("flannFindPairs %d m_indices.rows=%d\n", __LINE__, m_indices.rows);
-    for (int i = 0; i < m_indices.rows; ++i) 
-       {
-//printf("flannFindPairs %d dists=%f %f\n", __LINE__, dists_ptr[2 * i], 0.6 * dists_ptr[2 * i + 1]);
-       if (dists_ptr[2 * i] < 0.6 * dists_ptr[2 * i + 1]) 
-               {
-//printf("flannFindPairs %d pairs=%d\n", __LINE__, ptpairs.size());
-               ptpairs.push_back(i);
-               ptpairs.push_back(indices_ptr[2*i]);
-       }
-    }
-}
-
-
-/* a rough implementation for object location */
-int OpenCVWrapper::locatePlanarObject(const CvSeq* objectKeypoints, 
-       const CvSeq* objectDescriptors,
-    const CvSeq* imageKeypoints, 
-       const CvSeq* imageDescriptors,
-    const CvPoint src_corners[4], 
-       int *(*point_pairs),
-       int (*total_pairs))
-{
-    double h[9];
-    CvMat _h = cvMat(3, 3, CV_64F, h);
-    vector<int> ptpairs;
-    vector<CvPoint2D32f> pt1, pt2;
-    CvMat _pt1, _pt2;
-    int i, n;
-       
-       (*point_pairs) = 0;
-       (*total_pairs) = 0;
-
-#ifdef USE_FLANN
-    flannFindPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs );
-#else
-    findPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs );
-#endif
-
-
-// Store keypoints
-       (*point_pairs) = (int*)calloc(ptpairs.size(), sizeof(int));
-       (*total_pairs) = ptpairs.size() / 2;
-       
-       
-    for(int i = 0; i < (int)ptpairs.size(); i++)
-    {
-               (*point_pairs)[i] = ptpairs[i];
-    }
-
-
-
-    n = (int)(ptpairs.size()/2);
-    if( n < 4 )
-        return 0;
-
-    pt1.resize(n);
-    pt2.resize(n);
-    for( i = 0; i < n; i++ )
-    {
-        pt1[i] = ((CvSURFPoint*)cvGetSeqElem(objectKeypoints,ptpairs[i*2]))->pt;
-        pt2[i] = ((CvSURFPoint*)cvGetSeqElem(imageKeypoints,ptpairs[i*2+1]))->pt;
-    }
-
-    _pt1 = cvMat(1, n, CV_32FC2, &pt1[0] );
-    _pt2 = cvMat(1, n, CV_32FC2, &pt2[0] );
-    if( !cvFindHomography( &_pt1, &_pt2, &_h, CV_RANSAC, 5 ))
-        return 0;
-
-    for( i = 0; i < 4; i++ )
-    {
-        double x = src_corners[i].x, y = src_corners[i].y;
-        double Z = 1./(h[6]*x + h[7]*y + h[8]);
-        double X = (h[0]*x + h[1]*y + h[2])*Z;
-        double Y = (h[3]*x + h[4]*y + h[5])*Z;
-        dst_corners[i * 2] = X;
-               dst_corners[i * 2 + 1] = Y;
-    }
-
-    return 1;
-}
-
-
-
-
-OpenCVWrapper::OpenCVWrapper()
-{
-       object_image = 0;
-       scene_image = 0;
-       object_image_w = 0;
-       object_image_h = 0;
-       scene_image_w = 0;
-       scene_image_h = 0;
-       storage = 0;
-       object_keypoints = 0;
-       object_descriptors = 0;
-       scene_keypoints = 0;
-       scene_descriptors = 0;
-       point_pairs = 0;
-       total_pairs = 0;
-}
-
-
-
-
-
-
-OpenCVWrapper::~OpenCVWrapper()
-{
-// This releases all the arrays
-       if(storage) cvReleaseMemStorage(&storage);
-       if(object_image) cvReleaseImage(&object_image);
-       if(scene_image) cvReleaseImage(&scene_image);
-       if(point_pairs) free(point_pairs);
-}
-
-
-
-
-
-int OpenCVWrapper::scan(VFrame *object_frame,
-       VFrame *scene_frame,
-       int object_x1, 
-       int object_y1,
-       int object_x2,
-       int object_y2,
-       int scene_x1,
-       int scene_y1,
-       int scene_x2,
-       int scene_y2)
-{
-       int result = 0;
-       int object_w = object_x2 - object_x1;
-       int object_h = object_y2 - object_y1;
-       int scene_w = scene_x2 - scene_x1;
-       int scene_h = scene_y2 - scene_y1;
-
-
-//object_frame->write_png("/tmp/object.png");
-//scene_frame->write_png("/tmp/scene.png");
-
-// Get quantized sizes
-       int object_image_w = object_w;
-       int object_image_h = object_h;
-       int scene_image_w = scene_w;
-       int scene_image_h = scene_h;
-       if(object_w % QUANTIZE) object_image_w += QUANTIZE - (object_w % QUANTIZE);
-       if(object_h % QUANTIZE) object_image_h += QUANTIZE - (object_h % QUANTIZE);
-       if(scene_w % QUANTIZE) scene_image_w += QUANTIZE - (scene_w % QUANTIZE);
-       if(scene_h % QUANTIZE) scene_image_h += QUANTIZE - (scene_h % QUANTIZE);
-
-       if(object_image && 
-               (object_image_w != this->object_image_w ||
-               object_image_h != this->object_image_h))
-       {
-               cvReleaseImage(&object_image);
-               object_image = 0;
-       }
-       this->object_image_w = object_image_w;
-       this->object_image_h = object_image_h;
-
-       if(scene_image && 
-               (scene_image_w != this->scene_image_w ||
-               scene_image_h != this->scene_image_h))
-       {
-               cvReleaseImage(&scene_image);
-               scene_image = 0;
-       }
-       this->scene_image_w = scene_image_w;
-       this->scene_image_h = scene_image_h;
-
-
-       if(!object_image)
-       {
-// Only does greyscale
-               object_image = cvCreateImage( 
-                       cvSize(object_image_w, object_image_h), 
-                       8, 
-                       1);
-       }
-
-       if(!scene_image)
-       {
-// Only does greyscale
-               scene_image = cvCreateImage( 
-                       cvSize(scene_image_w, scene_image_h), 
-                       8, 
-                       1);
-       }
-
-// Select only region with image size
-// Does this do anything?
-       cvSetImageROI( object_image, cvRect( 0, 0, object_w, object_h ) );
-       cvSetImageROI( scene_image, cvRect( 0, 0, scene_w, scene_h ) );
-
-       grey_crop((unsigned char*)scene_image->imageData, 
-               scene_frame, 
-               scene_x1, 
-               scene_y1, 
-               scene_x2, 
-               scene_y2,
-               scene_image_w,
-               scene_image_h);
-       grey_crop((unsigned char*)object_image->imageData, 
-               object_frame, 
-               object_x1, 
-               object_y1, 
-               object_x2, 
-               object_y2,
-               object_image_w,
-               object_image_h);
-
-
-       if(!storage) storage = cvCreateMemStorage(0);
-       CvSURFParams params = cvSURFParams(500, 1);
-
-
-//printf("OpenCVWrapper::process_buffer %d\n", __LINE__);
-
-// TODO: make the surf data persistent & check for image changes instead
-       if(object_keypoints) cvClearSeq(object_keypoints);
-       if(object_descriptors) cvClearSeq(object_descriptors);
-       if(scene_keypoints) cvClearSeq(scene_keypoints);
-       if(scene_descriptors) cvClearSeq(scene_descriptors);
-       object_keypoints = 0;
-       object_descriptors = 0;
-       scene_keypoints = 0;
-       scene_descriptors = 0;
-
-// Free the image structures
-       if(point_pairs) free(point_pairs);
-       point_pairs = 0;
-
-
-       cvExtractSURF(object_image, 
-               0, 
-               &object_keypoints, 
-               &object_descriptors, 
-               storage, 
-               params,
-               0);
-
-//printf("OpenCVWrapper::scan %d object keypoints=%d\n", __LINE__, object_keypoints->total);
-// Draw the keypoints
-//             for(int i = 0; i < object_keypoints->total; i++)
-//             {
-//             CvSURFPoint* r1 = (CvSURFPoint*)cvGetSeqElem( object_keypoints, i );
-//                     int size = r1->size / 4;
-//                     draw_rect(frame[object_layer], 
-//                             r1->pt.x + object_x1 - size, 
-//                             r1->pt.y + object_y1 - size, 
-//                             r1->pt.x + object_x1 + size, 
-//                             r1->pt.y + object_y1 + size);
-//             }
-
-
-//printf("OpenCVWrapper::process_buffer %d\n", __LINE__);
-
-       cvExtractSURF(scene_image, 
-               0, 
-               &scene_keypoints, 
-               &scene_descriptors, 
-               storage, 
-               params,
-               0);
-
-// Draw the keypoints
-//             for(int i = 0; i < scene_keypoints->total; i++)
-//             {
-//             CvSURFPoint* r1 = (CvSURFPoint*)cvGetSeqElem( scene_keypoints, i );
-//                     int size = r1->size / 4;
-//                     draw_rect(frame[scene_layer], 
-//                             r1->pt.x + scene_x1 - size, 
-//                             r1->pt.y + scene_y1 - size, 
-//                             r1->pt.x + scene_x1 + size, 
-//                             r1->pt.y + scene_y1 + size);
-//             }
-
-// printf("OpenCVWrapper::scan %d %d %d scene keypoints=%d\n", 
-// __LINE__, 
-// scene_w,
-// scene_h,
-// scene_keypoints->total);
-
-       CvPoint src_corners[4] = 
-       {
-               { 0, 0 }, 
-               { object_w, 0 }, 
-               { object_w, object_h }, 
-               { 0, object_h }
-       };
-
-       for(int i = 0; i < 8; i++)
-       {
-               dst_corners[i] = 0;
-       }
-
-
-
-//printf("OpenCVWrapper::process_buffer %d\n", __LINE__);
-       if(scene_keypoints->total &&
-               object_keypoints->total &&
-               locatePlanarObject(object_keypoints, 
-                       object_descriptors, 
-                       scene_keypoints, 
-                       scene_descriptors, 
-                       src_corners, 
-                       &point_pairs,
-                       &total_pairs))
-       {
-               result = 1;
-       }
-
-
-       return result;
-}
-
-
-
-
-
-
-// Convert to greyscale & crop
-void OpenCVWrapper::grey_crop(unsigned char *dst, 
-       VFrame *src, 
-       int x1, 
-       int y1,
-       int x2,
-       int y2,
-       int dst_w,
-       int dst_h)
-{
-// Dimensions of dst frame
-       int w = x2 - x1;
-       int h = y2 - y1;
-
-       bzero(dst, dst_w * dst_h);
-
-//printf("OpenCVWrapper::grey_crop %d %d %d\n", __LINE__, w, h);
-       for(int i = 0; i < h; i++)
-       {
-               switch(src->get_color_model())
-               {
-                       case BC_RGB888:
-                               break;
-
-                       case BC_YUV888:
-                       {
-                               unsigned char *input = src->get_rows()[i + y1] + x1 * 3;
-                               unsigned char *output = dst + i * dst_w;
-
-                               for(int j = 0; j < w; j++)
-                               {
-// Y channel only
-                                       *output = *input;
-                                       input += 3;
-                                       output++;
-                               }
-                               break;
-                       }
-               }
-       }
-}
-
-
-float OpenCVWrapper::get_dst_x(int number)
-{
-       return dst_corners[number * 2];
-}
-
-float OpenCVWrapper::get_dst_y(int number)
-{
-       return dst_corners[number * 2 + 1];
-}
-
-
-
-
-
diff --git a/cinelerra-5.1/plugins/motion/opencvwrapper.h b/cinelerra-5.1/plugins/motion/opencvwrapper.h
deleted file mode 100644 (file)
index a33bec7..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * CINELERRA
- * Copyright (C) 1997-2012 Adam Williams <broadcast at earthling dot net>
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- * 
- */
-
-
-
-#ifndef OPENCVWRAPPER_H
-#define OPENCVWRAPPER_H
-
-#include "opencv2/core/core_c.h"
-
-#include "vframe.inc"
-
-class OpenCVWrapper
-{
-public:
-       OpenCVWrapper();
-       ~OpenCVWrapper();
-
-
-       float get_dst_x(int number);
-       float get_dst_y(int number);
-
-       void grey_crop(unsigned char *dst, 
-               VFrame *src, 
-               int x1, 
-               int y1,
-               int x2,
-               int y2,
-               int dst_w,
-               int dst_h);
-
-// Returns 1 when it got something
-       int scan(VFrame *object_frame,
-               VFrame *scene_frame,
-               int object_x1, 
-               int object_y1,
-               int object_x2,
-               int object_y2,
-               int scene_x1,
-               int scene_y1,
-               int scene_x2,
-               int scene_y2);
-
-private:
-       int locatePlanarObject(const CvSeq* objectKeypoints, 
-               const CvSeq* objectDescriptors,
-       const CvSeq* imageKeypoints, 
-               const CvSeq* imageDescriptors,
-       const CvPoint src_corners[4], 
-               int *(*point_pairs),
-               int (*total_pairs));
-
-// Images in the format OpenCV requires
-       IplImage *object_image;
-       IplImage *scene_image;
-// Quantized sizes
-       int object_image_w;
-       int object_image_h;
-       int scene_image_w;
-       int scene_image_h;
-       CvSeq *object_keypoints;
-       CvSeq *object_descriptors;
-       CvSeq *scene_keypoints;
-       CvSeq *scene_descriptors;
-       CvMemStorage *storage;
-       int *point_pairs;
-       int total_pairs;
-
-// x, y pairs
-       float dst_corners[8];
-};
-
-
-
-
-#endif
-
-
diff --git a/cinelerra-5.1/plugins/motion/opencvwrapper.inc b/cinelerra-5.1/plugins/motion/opencvwrapper.inc
deleted file mode 100644 (file)
index aaa0d18..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef OPENCVWRAPPER_INC
-#define OPENCVWRAPPER_INC
-
-
-class OpenCVWrapper;
-
-
-#endif
-
-
-
-
-