upgrade to ffmpeg-4.0, findobj upgrades, rework plugin visibility
[goodguy/history.git] / cinelerra-5.1 / plugins / findobj / findobj.C
index 70fc9b3f260ded07d8789f78ec22eb7ccb66a506..737601e2d204198b36bf711311a92ff7837145bd 100644 (file)
@@ -19,7 +19,7 @@
  */
 
 #include "affine.h"
-#include "cicolors.h"
+#include "bccolors.h"
 #include "clip.h"
 #include "filexml.h"
 #include "language.h"
@@ -27,6 +27,9 @@
 #include "findobjwindow.h"
 #include "mutex.h"
 #include "overlayframe.h"
+#include "plugin.h"
+#include "pluginserver.h"
+#include "track.h"
 
 #include <errno.h>
 #include <exception>
 REGISTER_PLUGIN(FindObjMain)
 
 FindObjConfig::FindObjConfig()
+{
+       reset();
+}
+
+void FindObjConfig::reset()
 {
        algorithm = NO_ALGORITHM;
        use_flann = 1;
+       mode = MODE_QUADRILATERAL;
+       draw_match = 0;
+       aspect = 1;
+       scale = 1;
+       translate = 1;
+       rotate = 1;
        draw_keypoints = 0;
-       draw_border = 0;
+       draw_scene_border = 0;
        replace_object = 0;
        draw_object_border = 0;
+       draw_replace_border = 0;
        object_x = 50;  object_y = 50;
        object_w = 100; object_h = 100;
+       drag_object = 0;
        scene_x = 50;   scene_y = 50;
        scene_w = 100;  scene_h = 100;
+       drag_replace = 0;
+       replace_x = 50;   replace_y = 50;
+       replace_w = 100;  replace_h = 100;
+       replace_dx = 0;   replace_dy = 0;
+       drag_scene = 0;
        scene_layer = 0;
        object_layer = 1;
        replace_layer = 2;
@@ -69,14 +90,27 @@ int FindObjConfig::equivalent(FindObjConfig &that)
        int result =
                algorithm == that.algorithm &&
                use_flann == that.use_flann &&
+               mode == that.mode &&
+               aspect == that.aspect &&
+               scale == that.scale &&
+               translate == that.translate &&
+               rotate == that.rotate &&
                draw_keypoints == that.draw_keypoints &&
-               draw_border == that.draw_border &&
+               draw_match == that.draw_match &&
+               draw_scene_border == that.draw_scene_border &&
                replace_object == that.replace_object &&
                draw_object_border == that.draw_object_border &&
+               draw_replace_border == that.draw_replace_border &&
                object_x == that.object_x && object_y == that.object_y &&
                object_w == that.object_w && object_h == that.object_h &&
+               drag_object == that.drag_object &&
                scene_x == that.scene_x && scene_y == that.scene_y &&
                scene_w == that.scene_w && scene_h == that.scene_h &&
+               drag_scene == that.drag_scene &&
+               replace_x == that.replace_x && replace_y == that.replace_y &&
+               replace_w == that.replace_w && replace_h == that.replace_h &&
+               replace_dx == that.replace_dx && replace_dy == that.replace_dy &&
+               drag_replace == that.drag_replace &&
                object_layer == that.object_layer &&
                replace_layer == that.replace_layer &&
                scene_layer == that.scene_layer &&
@@ -88,14 +122,27 @@ void FindObjConfig::copy_from(FindObjConfig &that)
 {
        algorithm = that.algorithm;
        use_flann = that.use_flann;
+       mode = that.mode;
+       aspect = that.aspect;
+       scale = that.scale;
+       translate = that.translate;
+       rotate = that.rotate;
        draw_keypoints = that.draw_keypoints;
-       draw_border = that.draw_border;
+       draw_match = that.draw_match;
+       draw_scene_border = that.draw_scene_border;
        replace_object = that.replace_object;
        draw_object_border = that.draw_object_border;
+       draw_replace_border = that.draw_replace_border;
        object_x = that.object_x;  object_y = that.object_y;
        object_w = that.object_w;  object_h = that.object_h;
+       drag_object = that.drag_object;
        scene_x = that.scene_x;    scene_y = that.scene_y;
        scene_w = that.scene_w;    scene_h = that.scene_h;
+       drag_scene = that.drag_scene;
+       replace_x = that.replace_x;   replace_y = that.replace_y;
+       replace_w = that.replace_w;   replace_h = that.replace_h;
+       replace_dx = that.replace_dx; replace_dy = that.replace_dy;
+       drag_replace = that.drag_replace;
        object_layer = that.object_layer;
        replace_layer = that.replace_layer;
        scene_layer = that.scene_layer;
@@ -126,15 +173,18 @@ FindObjMain::FindObjMain(PluginServer *server)
        scene_layer = 1;
        replace_layer = 2;
 
-       border_x1 = 0;  border_y1 = 0;
-       border_x2 = 0;  border_y2 = 0;
-       border_x3 = 0;  border_y3 = 0;
-       border_x4 = 0;  border_y4 = 0;
-
-       obj_x1 = 0;     obj_y1 = 0;
-       obj_x2 = 0;     obj_y2 = 0;
-       obj_x3 = 0;     obj_y3 = 0;
-       obj_x4 = 0;     obj_y4 = 0;
+       match_x1 = 0;  match_y1 = 0;
+       match_x2 = 0;  match_y2 = 0;
+       match_x3 = 0;  match_y3 = 0;
+       match_x4 = 0;  match_y4 = 0;
+       shape_x1 = 0;  shape_y1 = 0;
+       shape_x2 = 0;  shape_y2 = 0;
+       shape_x3 = 0;  shape_y3 = 0;
+       shape_x4 = 0;  shape_y4 = 0;
+       out_x1 = 0;    out_y1 = 0;
+       out_x2 = 0;    out_y2 = 0;
+       out_x3 = 0;    out_y3 = 0;
+       out_x4 = 0;    out_y4 = 0;
 
         init_border = 1;
 }
@@ -145,7 +195,7 @@ FindObjMain::~FindObjMain()
        delete overlayer;
 }
 
-const char* FindObjMain::plugin_title() { return _("Find Object"); }
+const char* FindObjMain::plugin_title() { return N_("FindObj"); }
 int FindObjMain::is_realtime() { return 1; }
 int FindObjMain::is_multichannel() { return 1; }
 
@@ -158,32 +208,7 @@ void FindObjMain::update_gui()
        if( !load_configuration() ) return;
        FindObjWindow *window = (FindObjWindow*)thread->window;
        window->lock_window("FindObjMain::update_gui");
-       window->algorithm->set_text(FindObjAlgorithm::to_text(config.algorithm));
-       window->use_flann->update(config.use_flann);
-       window->object_x->update(config.object_x);
-       window->object_x_text->update((float)config.object_x);
-       window->object_y->update(config.object_y);
-       window->object_y_text->update((float)config.object_y);
-       window->object_w->update(config.object_w);
-       window->object_w_text->update((float)config.object_w);
-       window->object_h->update(config.object_h);
-       window->object_h_text->update((float)config.object_h);
-       window->scene_x->update(config.scene_x);
-       window->scene_x_text->update((float)config.scene_x);
-       window->scene_y->update(config.scene_y);
-       window->scene_y_text->update((float)config.scene_y);
-       window->scene_w->update(config.scene_w);
-       window->scene_w_text->update((float)config.scene_w);
-       window->scene_h->update(config.scene_h);
-       window->scene_h_text->update((float)config.scene_h);
-       window->draw_keypoints->update(config.draw_keypoints);
-       window->draw_border->update(config.draw_border);
-       window->replace_object->update(config.replace_object);
-       window->draw_object_border->update(config.draw_object_border);
-       window->object_layer->update( (int64_t)config.object_layer);
-       window->replace_layer->update( (int64_t)config.replace_layer);
-       window->scene_layer->update( (int64_t)config.scene_layer);
-       window->blend->update( (int64_t)config.blend);
+       window->update_gui();
        window->flush();
        window->unlock_window();
 }
@@ -197,18 +222,34 @@ void FindObjMain::save_data(KeyFrame *keyframe)
        output.tag.set_title("FINDOBJ");
        output.tag.set_property("ALGORITHM", config.algorithm);
        output.tag.set_property("USE_FLANN", config.use_flann);
+       output.tag.set_property("MODE", config.mode);
+       output.tag.set_property("ASPECT", config.aspect);
+       output.tag.set_property("SCALE", config.scale);
+       output.tag.set_property("TRANSLATE", config.translate);
+       output.tag.set_property("ROTATE", config.rotate);
+       output.tag.set_property("DRAG_OBJECT", config.drag_object);
        output.tag.set_property("OBJECT_X", config.object_x);
        output.tag.set_property("OBJECT_Y", config.object_y);
        output.tag.set_property("OBJECT_W", config.object_w);
        output.tag.set_property("OBJECT_H", config.object_h);
+       output.tag.set_property("DRAG_SCENE", config.drag_scene);
        output.tag.set_property("SCENE_X", config.scene_x);
        output.tag.set_property("SCENE_Y", config.scene_y);
        output.tag.set_property("SCENE_W", config.scene_w);
        output.tag.set_property("SCENE_H", config.scene_h);
+       output.tag.set_property("DRAG_REPLACE", config.drag_replace);
+       output.tag.set_property("REPLACE_X", config.replace_x);
+       output.tag.set_property("REPLACE_Y", config.replace_y);
+       output.tag.set_property("REPLACE_W", config.replace_w);
+       output.tag.set_property("REPLACE_H", config.replace_h);
+       output.tag.set_property("REPLACE_DX", config.replace_dx);
+       output.tag.set_property("REPLACE_DY", config.replace_dy);
        output.tag.set_property("DRAW_KEYPOINTS", config.draw_keypoints);
-       output.tag.set_property("DRAW_BORDER", config.draw_border);
+       output.tag.set_property("DRAW_MATCH", config.draw_match);
+       output.tag.set_property("DRAW_SCENE_BORDER", config.draw_scene_border);
        output.tag.set_property("REPLACE_OBJECT", config.replace_object);
        output.tag.set_property("DRAW_OBJECT_BORDER", config.draw_object_border);
+       output.tag.set_property("DRAW_REPLACE_BORDER", config.draw_replace_border);
        output.tag.set_property("OBJECT_LAYER", config.object_layer);
        output.tag.set_property("REPLACE_LAYER", config.replace_layer);
        output.tag.set_property("SCENE_LAYER", config.scene_layer);
@@ -232,18 +273,34 @@ void FindObjMain::read_data(KeyFrame *keyframe)
                if( input.tag.title_is("FINDOBJ") ) {
                        config.algorithm = input.tag.get_property("ALGORITHM", config.algorithm);
                        config.use_flann = input.tag.get_property("USE_FLANN", config.use_flann);
+                       config.mode = input.tag.get_property("MODE", config.mode);
+                       config.aspect = input.tag.get_property("ASPECT", config.aspect);
+                       config.scale = input.tag.get_property("SCALE", config.scale);
+                       config.translate = input.tag.get_property("TRANSLATE", config.translate);
+                       config.rotate = input.tag.get_property("ROTATE", config.rotate);
                        config.object_x = input.tag.get_property("OBJECT_X", config.object_x);
                        config.object_y = input.tag.get_property("OBJECT_Y", config.object_y);
                        config.object_w = input.tag.get_property("OBJECT_W", config.object_w);
                        config.object_h = input.tag.get_property("OBJECT_H", config.object_h);
+                       config.drag_object = input.tag.get_property("DRAG_OBJECT", config.drag_object);
                        config.scene_x = input.tag.get_property("SCENE_X", config.scene_x);
                        config.scene_y = input.tag.get_property("SCENE_Y", config.scene_y);
                        config.scene_w = input.tag.get_property("SCENE_W", config.scene_w);
                        config.scene_h = input.tag.get_property("SCENE_H", config.scene_h);
+                       config.drag_scene = input.tag.get_property("DRAG_SCENE", config.drag_scene);
+                       config.replace_x = input.tag.get_property("REPLACE_X", config.replace_x);
+                       config.replace_y = input.tag.get_property("REPLACE_Y", config.replace_y);
+                       config.replace_w = input.tag.get_property("REPLACE_W", config.replace_w);
+                       config.replace_h = input.tag.get_property("REPLACE_H", config.replace_h);
+                       config.replace_dx = input.tag.get_property("REPLACE_DX", config.replace_dx);
+                       config.replace_dy = input.tag.get_property("REPLACE_DY", config.replace_dy);
+                       config.drag_replace = input.tag.get_property("DRAG_REPLACE", config.drag_replace);
                        config.draw_keypoints = input.tag.get_property("DRAW_KEYPOINTS", config.draw_keypoints);
-                       config.draw_border = input.tag.get_property("DRAW_BORDER", config.draw_border);
+                       config.draw_match = input.tag.get_property("DRAW_MATCH", config.draw_match);
+                       config.draw_scene_border = input.tag.get_property("DRAW_SCENE_BORDER", config.draw_scene_border);
                        config.replace_object = input.tag.get_property("REPLACE_OBJECT", config.replace_object);
                        config.draw_object_border = input.tag.get_property("DRAW_OBJECT_BORDER", config.draw_object_border);
+                       config.draw_replace_border = input.tag.get_property("DRAW_REPLACE_BORDER", config.draw_replace_border);
                        config.object_layer = input.tag.get_property("OBJECT_LAYER", config.object_layer);
                        config.replace_layer = input.tag.get_property("REPLACE_LAYER", config.replace_layer);
                        config.scene_layer = input.tag.get_property("SCENE_LAYER", config.scene_layer);
@@ -259,12 +316,40 @@ void FindObjMain::draw_line(VFrame *vframe, int x1, int y1, int x2, int y2)
        vframe->draw_line(x1, y1, x2, y2);
 }
 
+void FindObjMain::draw_quad(VFrame *vframe,
+               int x1, int y1, int x2, int y2,
+               int x3, int y3, int x4, int y4)
+{
+       int r = bmin(vframe->get_w(), vframe->get_h()) / 200 + 1;
+       for( int i=r; --i>0; ) {
+               draw_line(vframe, x1+i, y1+i, x2, y2);
+               draw_line(vframe, x1-i, y1-i, x2, y2);
+               draw_line(vframe, x2+i, y2+i, x3, y3);
+               draw_line(vframe, x2-i, y2-i, x3, y3);
+               draw_line(vframe, x3+i, y3+i, x4, y4);
+               draw_line(vframe, x3-i, y3-i, x4, y4);
+               draw_line(vframe, x4+i, y4+i, x1, y1);
+               draw_line(vframe, x4-i, y4-i, x1, y1);
+       }
+       draw_line(vframe, x1, y1, x2, y2);
+       draw_line(vframe, x2, y2, x3, y3);
+       draw_line(vframe, x3, y3, x4, y4);
+       draw_line(vframe, x4, y4, x1, y1);
+       for( int i=r; --i>0; )
+               draw_circle(vframe, x1, y1, i);
+}
+
 void FindObjMain::draw_rect(VFrame *vframe, int x1, int y1, int x2, int y2)
 {
-       draw_line(vframe, x1, y1, x2, y1);
-       draw_line(vframe, x2, y1 + 1, x2, y2);
-       draw_line(vframe, x2 - 1, y2, x1, y2);
-       draw_line(vframe, x1, y2 - 1, x1, y1 + 1);
+       int r = bmin(vframe->get_w(), vframe->get_h()) / 200 + 1;
+       for( int i=r; --i>0; ) {
+               --x2;  --y2;
+               draw_line(vframe, x1, y1, x2, y1);
+               draw_line(vframe, x2, y1, x2, y2);
+               draw_line(vframe, x2, y2, x1, y2);
+               draw_line(vframe, x1, y2, x1, y1);
+               ++x1;  ++y1;
+       }
 }
 
 void FindObjMain::draw_circle(VFrame *vframe, int x, int y, int r)
@@ -404,12 +489,10 @@ void FindObjMain::set_brisk()
 }
 #endif
 
+
 void FindObjMain::process_match()
 {
        if( config.algorithm == NO_ALGORITHM ) return;
-       if( !config.replace_object &&
-           !config.draw_border &&
-           !config.draw_keypoints ) return;
 
        if( detector.empty() ) {
                switch( config.algorithm ) {
@@ -449,22 +532,166 @@ void FindObjMain::process_match()
        }
 
        ptV src, dst;
-       float obj_x1 = 0, obj_x2 = object_w;
-       float obj_y1 = 0, obj_y2 = object_h;
-       src.push_back(Point2f(obj_x1,obj_y1));
-       src.push_back(Point2f(obj_x2,obj_y1));
-       src.push_back(Point2f(obj_x2,obj_y2));
-       src.push_back(Point2f(obj_x1,obj_y2));
+       float out_x1 = 0, out_x2 = object_w;
+       float out_y1 = 0, out_y2 = object_h;
+       src.push_back(Point2f(out_x1,out_y1));
+       src.push_back(Point2f(out_x2,out_y1));
+       src.push_back(Point2f(out_x2,out_y2));
+       src.push_back(Point2f(out_x1,out_y2));
        perspectiveTransform(src, dst, H);
 
-       border_x1 = dst[0].x + scene_x;  border_y1 = dst[0].y + scene_y;
-       border_x2 = dst[1].x + scene_x;  border_y2 = dst[1].y + scene_y;
-       border_x3 = dst[2].x + scene_x;  border_y3 = dst[2].y + scene_y;
-       border_x4 = dst[3].x + scene_x;  border_y4 = dst[3].y + scene_y;
-//printf("src %f,%f  %f,%f  %f,%f  %f,%f\n",
-// src[0].x,src[0].y, src[1].x,src[1].y, src[2].x,src[2].y, src[3].x,src[3].y);
-//printf("dst %f,%f  %f,%f  %f,%f  %f,%f\n",
-// dst[0].x,dst[0].y, dst[1].x,dst[1].y, dst[2].x,dst[2].y, dst[3].x,dst[3].y);
+       match_x1 = dst[0].x + scene_x;  match_y1 = dst[0].y + scene_y;
+       match_x2 = dst[1].x + scene_x;  match_y2 = dst[1].y + scene_y;
+       match_x3 = dst[2].x + scene_x;  match_y3 = dst[2].y + scene_y;
+       match_x4 = dst[3].x + scene_x;  match_y4 = dst[3].y + scene_y;
+}
+
+
+static double area(float x1, float y1, float x2, float y2,
+               float x3, float y3, float x4, float y4)
+{ // quadrelateral area, sign is +ccw,-cw, use abs
+       double dx1 = x3-x1, dy1 = y3-y1;
+       double dx2 = x4-x2, dy2 = y4-y2;
+       return 0.5 * (dx1 * dy2 - dx2 * dy1);
+}
+static double dist(float x1,float y1, float x2, float y2)
+{
+       double dx = x2-x1, dy = y2-y1;
+       return sqrt(dx*dx + dy*dy);
+}
+static int intersects(double x1, double y1, double x2, double y2,
+               double x3, double y3, double x4, double y4)
+{
+       double dx12 = x2 - x1, dy12 = y2 - y1;
+       double dx34 = x4 - x3, dy34 = y4 - y3;
+       double d = dx12*dy34 - dx34*dy12;
+       if( !d ) return 0; // parallel
+       double dx13 = x3 - x1, dy13 = y3 - y1;
+       double u = (dx13*dy34 - dx34*dy13) / d;
+       if( u < 0 || u > 1 ) return 0;
+       double v = (dx13*dy12 - dx12*dy13) / d;
+       if( v < 0 || v > 1 ) return 0;
+       return 1;
+}
+
+/*
+ * 4---------3    1---------2
+ * |0,h   w,h|    |0,0   w,0|
+ * |    +    |    |    +    |
+ * |0,0   w,0|    |0,h   w,h|
+ * 1---------2    1---------2
+ * pt locations    screen pts
+ */
+void FindObjMain::reshape()
+{
+       if( config.mode == MODE_NONE ) return;
+       const double pi = M_PI;
+       double x1 = match_x1, y1 = match_y1;
+       double x2 = match_x2, y2 = match_y2;
+       double x3 = match_x3, y3 = match_y3;
+       double x4 = match_x4, y4 = match_y4;
+       double ia = area(x1,y1, x2,y2, x3,y3, x4,y4);
+// centroid
+       double cx = (x1 + x2 + x3 + x4) / 4;
+       double cy = (y1 + y2 + y3 + y4) / 4;
+// centered
+       x1 -= cx;  x2 -= cx;  x3 -= cx;  x4 -= cx;
+       y1 -= cy;  y2 -= cy;  y3 -= cy;  y4 -= cy;
+// bowtied
+       if( intersects(x1,y1, x2,y2, x3,y3, x4,y4) ) {
+               double x = x2, y = y2;
+               x2 = x3;  y2 = y3;
+               x3 = x;   y3 = y;
+       }
+       else if( intersects(x1,y1, x4,y4, x3,y3, x2,y2) ) {
+               double x = x4, y = y4;
+               x4 = x3;  y4 = y3;
+               x3 = x;   y3 = y;
+       }
+
+// rotation, if mode is quad: reverse rotate
+       double r = 0;
+       if( (config.mode == MODE_QUADRILATERAL) ^ (config.rotate != 0) ) {
+// edge centers
+               double cx12 = (x1 + x2) / 2, cy12 = (y1 + y2) / 2;
+               double cx23 = (x2 + x3) / 2, cy23 = (y2 + y3) / 2;
+               double cx34 = (x3 + x4) / 2, cy34 = (y3 + y4) / 2;
+               double cx41 = (x4 + x1) / 2, cy41 = (y4 + y1) / 2;
+               double vx = cx34 - cx12, vy = cy34 - cy12;
+               double hx = cx23 - cx41, hy = cy23 - cy41;
+               double v = atan2(vy, vx);
+               double h = atan2(hy, hx);
+               r = (h + v - pi/2) / 2;
+       }
+// diagonal length
+       double a = dist(x1,y1, x3,y3) / 2;
+       double b = dist(x2,y2, x4,y4) / 2;
+       if( config.mode == MODE_SQUARE ||
+           config.mode == MODE_RECTANGLE )
+               a = b = (a + b) / 2;
+// central angles
+       double a1 = atan2(y1, x1);
+       double a2 = atan2(y2, x2);
+       double a3 = atan2(y3, x3);
+       double a4 = atan2(y4, x4);
+// edge angles
+       double a12 = a2 - a1, a23 = a3 - a2;
+       double a34 = a4 - a3, a41 = a1 - a4;
+       double dt = (a12 - a23 + a34 - a41)/4;
+// mirrored
+       if( ia < 0 ) { ia = -ia;  dt = -dt;  r = -r; }
+       switch( config.mode ) {
+       case MODE_SQUARE:
+       case MODE_RHOMBUS:
+               dt = pi/2;
+       case MODE_RECTANGLE:
+       case MODE_PARALLELOGRAM: {
+               double t = -(pi+dt)/2;
+               x1 = a*cos(t);  y1 = a*sin(t);  t += dt;
+               x2 = b*cos(t);  y2 = b*sin(t);  t += pi - dt;
+               x3 = a*cos(t);  y3 = a*sin(t);  t += dt;
+               x4 = b*cos(t);  y4 = b*sin(t); }
+       case MODE_QUADRILATERAL:
+               break;
+       }
+// aspect
+       if( !config.aspect ) {
+               double cx12 = (x1 + x2) / 2, cy12 = (y1 + y2) / 2;
+               double cx23 = (x2 + x3) / 2, cy23 = (y2 + y3) / 2;
+               double cx34 = (x3 + x4) / 2, cy34 = (y3 + y4) / 2;
+               double cx41 = (x4 + x1) / 2, cy41 = (y4 + y1) / 2;
+               double iw = dist(cx41,cy41, cx23,cy23);
+               double ih = dist(cx12,cy12, cx34,cy34);
+               double ow = object_w, oh = object_h;
+               double sx = iw && ih ? sqrt((ih*ow)/(iw*oh)) : 1;
+               double sy = sx ? 1 / sx : 1;
+               x1 *= sx;  x2 *= sx;  x3 *= sx;  x4 *= sx;
+               y1 *= sy;  y2 *= sy;  y3 *= sy;  y4 *= sy;
+       }
+// rotation
+       if( r ) {
+               double ct = cos(r), st = sin(r), x, y;
+               x = x1*ct + y1*st;  y = y1*ct - x1*st;  x1 = x;  y1 = y;
+               x = x2*ct + y2*st;  y = y2*ct - x2*st;  x2 = x;  y2 = y;
+               x = x3*ct + y3*st;  y = y3*ct - x3*st;  x3 = x;  y3 = y;
+               x = x4*ct + y4*st;  y = y4*ct - x4*st;  x4 = x;  y4 = y;
+       }
+// scaling
+       ia = !config.scale ? object_w * object_h : ia;
+       double oa = abs(area(x1,y1, x2,y2, x3,y3, x4,y4));
+       double sf =  oa ? sqrt(ia / oa) : 0;
+       x1 *= sf;  x2 *= sf;  x3 *= sf;  x4 *= sf;
+       y1 *= sf;  y2 *= sf;  y3 *= sf;  y4 *= sf;
+// translation
+       double ox = !config.translate ? object_x + object_w/2. : cx;
+       double oy = !config.translate ? object_y + object_h/2. : cy;
+       x1 += ox;  x2 += ox;  x3 += ox;  x4 += ox;
+       y1 += oy;  y2 += oy;  y3 += oy;  y4 += oy;
+
+       shape_x1 = x1;  shape_y1 = y1;
+       shape_x2 = x2;  shape_y2 = y2;
+       shape_x3 = x3;  shape_y3 = y3;
+       shape_x4 = x4;  shape_y4 = y4;
 }
 
 int FindObjMain::process_buffer(VFrame **frame, int64_t start_position, double frame_rate)
@@ -481,34 +708,33 @@ int FindObjMain::process_buffer(VFrame **frame, int64_t start_position, double f
                matcher.release();
        }
 
-       w = frame[0]->get_w();
-       h = frame[0]->get_h();
-
        object_layer = config.object_layer;
        scene_layer = config.scene_layer;
        replace_layer = config.replace_layer;
+       Track *track = server->plugin->track;
+       w = track->track_w;
+       h = track->track_h;
 
        int max_layer = PluginClient::get_total_buffers() - 1;
        object_layer = bclip(config.object_layer, 0, max_layer);
        scene_layer = bclip(config.scene_layer, 0, max_layer);
        replace_layer = bclip(config.replace_layer, 0, max_layer);
 
-       int cfg_w, cfg_h, cfg_x1, cfg_y1, cfg_x2, cfg_y2;
-       cfg_w = (int)(config.object_w * w / 100);
-       cfg_h = (int)(config.object_h * h / 100);
-       cfg_x1 = (int)(config.object_x * w / 100 - cfg_w / 2);
-       cfg_y1 = (int)(config.object_y * h / 100 - cfg_h / 2);
-       cfg_x2 = cfg_x1 + cfg_w;
-       cfg_y2 = cfg_y1 + cfg_h;
+       int cfg_w = (int)(w * config.object_w / 100.);
+       int cfg_h = (int)(h * config.object_h / 100.);
+       int cfg_x1 = (int)(w * config.object_x / 100. - cfg_w / 2);
+       int cfg_y1 = (int)(h * config.object_y / 100. - cfg_h / 2);
+       int cfg_x2 = cfg_x1 + cfg_w;
+       int cfg_y2 = cfg_y1 + cfg_h;
        bclamp(cfg_x1, 0, w);  object_x = cfg_x1;
        bclamp(cfg_y1, 0, h);  object_y = cfg_y1;
        bclamp(cfg_x2, 0, w);  object_w = cfg_x2 - cfg_x1;
        bclamp(cfg_y2, 0, h);  object_h = cfg_y2 - cfg_y1;
 
-       cfg_w = (int)(config.scene_w * w / 100);
-       cfg_h = (int)(config.scene_h * h / 100);
-       cfg_x1 = (int)(config.scene_x * w / 100 - cfg_w / 2);
-       cfg_y1 = (int)(config.scene_y * h / 100 - cfg_h / 2);
+       cfg_w = (int)(w * config.scene_w / 100.);
+       cfg_h = (int)(h * config.scene_h / 100.);
+       cfg_x1 = (int)(w * config.scene_x / 100. - cfg_w / 2);
+       cfg_y1 = (int)(h * config.scene_y / 100. - cfg_h / 2);
        cfg_x2 = cfg_x1 + cfg_w;
        cfg_y2 = cfg_y1 + cfg_h;
        bclamp(cfg_x1, 0, w);  scene_x = cfg_x1;
@@ -516,6 +742,22 @@ int FindObjMain::process_buffer(VFrame **frame, int64_t start_position, double f
        bclamp(cfg_x2, 0, w);  scene_w = cfg_x2 - cfg_x1;
        bclamp(cfg_y2, 0, h);  scene_h = cfg_y2 - cfg_y1;
 
+       cfg_w = (int)(w * config.replace_w / 100.);
+       cfg_h = (int)(h * config.replace_h / 100.);
+       cfg_x1 = (int)(w * config.replace_x / 100. - cfg_w / 2);
+       cfg_y1 = (int)(h * config.replace_y / 100. - cfg_h / 2);
+       cfg_x2 = cfg_x1 + cfg_w;
+       cfg_y2 = cfg_y1 + cfg_h;
+       bclamp(cfg_x1, 0, w);  replace_x = cfg_x1;
+       bclamp(cfg_y1, 0, h);  replace_y = cfg_y1;
+       bclamp(cfg_x2, 0, w);  replace_w = cfg_x2 - cfg_x1;
+       bclamp(cfg_y2, 0, h);  replace_h = cfg_y2 - cfg_y1;
+
+       int cfg_dx = (int)(w * config.replace_dx / 100.);
+       int cfg_dy = (int)(h * config.replace_dy / 100.);
+       bclamp(cfg_dx, -h, h);  replace_dx = cfg_dx;
+       bclamp(cfg_dy, -w, w);  replace_dy = cfg_dy;
+
 // Read in the input frames
        for( int i = 0; i < PluginClient::get_total_buffers(); i++ ) {
                read_frame(frame[i], i, start_position, frame_rate, 0);
@@ -525,21 +767,22 @@ int FindObjMain::process_buffer(VFrame **frame, int64_t start_position, double f
        scene = frame[scene_layer];
        replace = frame[replace_layer];
 
-       border_x1 = obj_x1;  border_y1 = obj_y1;
-       border_x2 = obj_x2;  border_y2 = obj_y2;
-       border_x3 = obj_x3;  border_y3 = obj_y3;
-       border_x4 = obj_x4;  border_y4 = obj_y4;
+       shape_x1 = out_x1;  shape_y1 = out_y1;
+       shape_x2 = out_x2;  shape_y2 = out_y2;
+       shape_x3 = out_x3;  shape_y3 = out_y3;
+       shape_x4 = out_x4;  shape_y4 = out_y4;
 
        if( scene_w > 0 && scene_h > 0 && object_w > 0 && object_h > 0 ) {
                process_match();
+               reshape();
        }
 
        double w0 = init_border ? 1. : config.blend/100., w1 = 1. - w0;
-       obj_x1 = border_x1*w0 + obj_x1*w1;  obj_y1 = border_y1*w0 + obj_y1*w1;
-       obj_x2 = border_x2*w0 + obj_x2*w1;  obj_y2 = border_y2*w0 + obj_y2*w1;
-       obj_x3 = border_x3*w0 + obj_x3*w1;  obj_y3 = border_y3*w0 + obj_y3*w1;
-       obj_x4 = border_x4*w0 + obj_x4*w1;  obj_y4 = border_y4*w0 + obj_y4*w1;
-
+       init_border = 0;
+       out_x1 = shape_x1*w0 + out_x1*w1;  out_y1 = shape_y1*w0 + out_y1*w1;
+       out_x2 = shape_x2*w0 + out_x2*w1;  out_y2 = shape_y2*w0 + out_y2*w1;
+       out_x3 = shape_x3*w0 + out_x3*w1;  out_y3 = shape_y3*w0 + out_y3*w1;
+       out_x4 = shape_x4*w0 + out_x4*w1;  out_y4 = shape_y4*w0 + out_y4*w1;
 // Replace object in the scene layer
        if( config.replace_object ) {
                int cpus1 = get_project_smp() + 1;
@@ -547,32 +790,48 @@ int FindObjMain::process_buffer(VFrame **frame, int64_t start_position, double f
                        affine = new AffineEngine(cpus1, cpus1);
                if( !overlayer )
                        overlayer = new OverlayFrame(cpus1);
-
-               VFrame *temp = new_temp(scene->get_w(), scene->get_h(), scene->get_color_model());
+               VFrame *temp = new_temp(w, h, scene->get_color_model());
                temp->clear_frame();
-               float sx = 100./w, sy = 100./h;
-               float x1 = sx * obj_x1, y1 = sy * obj_y1;
-               float x2 = sx * obj_x2, y2 = sy * obj_y2;
-               float x3 = sx * obj_x3, y3 = sy * obj_y3;
-               float x4 = sx * obj_x4, y4 = sy * obj_y4;
-               affine->process(temp, replace, 0, AffineEngine::PERSPECTIVE,
-                       x1,y1, x2,y2, x3,y3, x4,y4, 1);
+               affine->set_in_viewport(replace_x, replace_y, replace_w, replace_h);
+               float ix1 = replace_x, ix2 = ix1 + replace_w;
+               float iy1 = replace_y, iy2 = iy1 + replace_h;
+               float dx = replace_dx, dy = replace_dy;
+               float ox1 = out_x1+dx, ox2 = out_x2+dx, ox3 = out_x3+dx, ox4 = out_x4+dx;
+               float oy1 = out_y1-dy, oy2 = out_y2-dy, oy3 = out_y3-dy, oy4 = out_y4-dy;
+               affine->set_matrix(ix1,iy1, ix2,iy2, ox1,oy1, ox2,oy2, ox4,oy4, ox3,oy3);
+               affine->process(temp, replace, 0,
+                       AffineEngine::TRANSFORM, 0,0, 100,0, 100,100, 0,100, 1);
                overlayer->overlay(scene, temp,  0,0, w,h,  0,0, w,h,
                        1, TRANSFER_NORMAL, NEAREST_NEIGHBOR);
 
        }
 
-       if( config.draw_border ) {
-               int wh = (w+h)>>8, ss = 1; while( wh>>=1 ) ss<<=1;
-               scene->set_pixel_color(WHITE);  scene->set_stiple(ss*2);
-               draw_line(scene, obj_x1, obj_y1, obj_x2, obj_y2);
-               draw_line(scene, obj_x2, obj_y2, obj_x3, obj_y3);
-               draw_line(scene, obj_x3, obj_y3, obj_x4, obj_y4);
-               draw_line(scene, obj_x4, obj_y4, obj_x1, obj_y1);
+       int wh = (w+h)>>8, ss = 1; while( wh>>=1 ) ss<<=1;
+       if( config.draw_scene_border ) {
+               scene->set_stiple(ss*2);
+               scene->set_pixel_color(WHITE);
+               draw_rect(scene, scene_x, scene_y, scene_x+scene_w, scene_y+scene_h);
        }
-
+       if( config.draw_object_border ) {
+               scene->set_stiple(ss*3);
+               scene->set_pixel_color(YELLOW); 
+               draw_rect(scene, object_x, object_y, object_x+object_w, object_y+object_h);
+       }
+       if( config.draw_replace_border ) {
+               scene->set_stiple(ss*3);
+               scene->set_pixel_color(GREEN);
+               draw_rect(scene, replace_x, replace_y, replace_x+replace_w, replace_y+replace_h);
+       }
+       scene->set_stiple(0);
        if( config.draw_keypoints ) {
-               scene->set_pixel_color(RED);  scene->set_stiple(0);
+               scene->set_pixel_color(GREEN);
+               for( int i=0,n=obj_keypts.size(); i<n; ++i ) {
+                       Point2f &pt = obj_keypts[i].pt;
+                       int r = obj_keypts[i].size * 1.2/9 * 2;
+                       int x = pt.x + object_x, y = pt.y + object_y;
+                       draw_circle(scene, x, y, r);
+               }
+               scene->set_pixel_color(RED);
                for( int i=0,n=scn_keypts.size(); i<n; ++i ) {
                        Point2f &pt = scn_keypts[i].pt;
                        int r = scn_keypts[i].size * 1.2/9 * 2;
@@ -580,18 +839,25 @@ int FindObjMain::process_buffer(VFrame **frame, int64_t start_position, double f
                        draw_circle(scene, x, y, r);
                }
        }
+       if( config.draw_match ) {
+               scene->set_pixel_color(BLUE);
+               draw_quad(scene, match_x1, match_y1, match_x2, match_y2,
+                               match_x3, match_y3, match_x4, match_y4);
+       }
 
-// Draw object outline in the object layer
-       if( config.draw_object_border ) {
-               int wh = (w+h)>>8, ss = 1; while( wh>>=1 ) ss<<=1;
-               scene->set_pixel_color(YELLOW);  scene->set_stiple(ss*3);
-               int x1 = object_x, x2 = x1 + object_w - 1;
-               int y1 = object_y, y2 = y1 + object_h - 1;
-               draw_rect(scene, x1, y1, x2, y2);
-               scene->set_pixel_color(GREEN);  scene->set_stiple(0);
-               x1 = scene_x, x2 = x1 + scene_w - 1;
-               y1 = scene_y, y2 = y1 + scene_h - 1;
-               draw_rect(scene, x1, y1, x2, y2);
+       if( gui_open() ) {
+               if( config.drag_scene ) {
+                       scene->set_pixel_color(WHITE);
+                       DragCheckBox::draw_boundary(scene, scene_x, scene_y, scene_w, scene_h);
+               }
+               if( config.drag_object ) {
+                       scene->set_pixel_color(YELLOW);
+                       DragCheckBox::draw_boundary(scene, object_x, object_y, object_w, object_h);
+               }
+               if( config.drag_replace ) {
+                       scene->set_pixel_color(GREEN);
+                       DragCheckBox::draw_boundary(scene, replace_x, replace_y, replace_w, replace_h);
+               }
        }
 
        scene->set_pixel_color(BLACK);