+// 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
+