> On Saturday, October 18, 2014 8:55 AM, Nicholas Robbins 
> <nickrobbins-at-yahoo....@ffmpeg.org> wrote:

> >>  On Saturday, October 18, 2014 8:36 AM, Moritz Barsnick 
> <barsn...@gmx.net> wrote:
> 
>>  You basically got it right. (I happened to follow the thread on
>>  ffmpeg-devel, thanks for forwarding it.)
>> 
>>  a) An inverse calculation, like in your example given. But that's just
>>  totally over the top, no-one can be expected to be capable of such a
>>  calculation. I tried to figure it out from the code, but couldn't.
> 
> I happen to be a mathematician, and I've taught courses on projective 
> geometry and perspective. If you know the right question to ask 
> mathematically 
> it's straight forward, but the answer is a mess.
> 
>>  b) What I meant - and you got it totally right on ffmpeg-devel - is to
>>  re-use the code from the perspective filter, because it contains all
>>  the transformations, considerations, and colorspace cruft. Just the
>>  wrong parameters. So the questions regarding "how to re-use a 
> filter's
>>  algorithms/mechanisms without duplicating its code" are spot-on.
> 
> I'm leaning to making it an option to the perspective filter, so you can say 
> how you want the parameters intrepreted, as the locations in the source of 
> the 
> corners of the new video or the locations in the new video of the corners of 
> the 
> source.
> 
> I don't know anything about the colorspace stuff but that could presumably 
> be put in. If I understand correctly (a big if), the array pv in the context 
> stores the inverse locations of the new image. If that is outside of the 
> original image, it could just be set to [0,0,0,0] or whatever, but I don't 
> know about color formats or anything.
> 
> If you want to handle that, I'll tackle the inverse transformation part.
> 
> -Nick
> 
>>  That said, I pointed out that the perspective filter is doing peculiar
>>  things with the color edges when using such "negative" 
> parameters. 
I see what it is doing, I think it is just interpolating from the data it has. 
The "peculiar" things you see it doing is probably due to the fact that it 
might not sample croma at every pixel. I find that if I put a two pixel black 
frame around the image first it works fine.

>>  Have
>>  a look at my testsrc example's output. Possibly the perspective filter
>>  was only written with "inside" reference points in mind. The 
> opposite
>>  filter would also need to be able to fill the remaining "empty" 
> space
>>  with some kind of transparency/alpha channel, so that overlaying the
>>  warped frame over another stream is possible. That's basically what the
>>  original poster was looking for, and what makes sense to me from a user
>>  perspective.

I imagine you if you add a two pixel pad of transparent black (0x00000000), it 
would do what you suggest. I've attached a patch. Apply the patch, recompile 
and see if 

./ffplay -f lavfi -i testsrc -vf 
pad="iw+4:ih+4:2:2:0x00000000",perspective=x0=W/4:y0=H/4:x1=3*W/4:y1=H/4:sense=destination

does what you want. If this works, I'll add documentation and submit it.





>>  Moritz
diff --git a/libavfilter/vf_perspective.c b/libavfilter/vf_perspective.c
index a796bd4..8cfaf05 100644
--- a/libavfilter/vf_perspective.c
+++ b/libavfilter/vf_perspective.c
@@ -46,6 +46,7 @@ typedef struct PerspectiveContext {
     int height[4];
     int hsub, vsub;
     int nb_planes;
+    int sense;
 
     int (*perspective)(AVFilterContext *ctx,
                        void *arg, int job, int nb_jobs);
@@ -53,6 +54,12 @@ typedef struct PerspectiveContext {
 
 #define OFFSET(x) offsetof(PerspectiveContext, x)
 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+#define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, INT_MIN, INT_MAX, FLAGS, unit }
+
+enum PERSPECTIVESense {
+    PERSPECTIVE_SENSE_SOURCE      = 0, ///< coordinates give locations in source of corners of destination.
+    PERSPECTIVE_SENSE_DESTINATION = 1, ///< coordinates give locations in destination of corners of source.
+};
 
 static const AVOption perspective_options[] = {
     { "x0", "set top left x coordinate",     OFFSET(expr_str[0][0]), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS },
@@ -66,6 +73,10 @@ static const AVOption perspective_options[] = {
     { "interpolation", "set interpolation", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=LINEAR}, 0, 1, FLAGS, "interpolation" },
     {      "linear", "", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, "interpolation" },
     {       "cubic", "", 0, AV_OPT_TYPE_CONST, {.i64=CUBIC},  0, 0, FLAGS, "interpolation" },
+    { "sense",   "specify the sense of the coordinates", OFFSET(sense), AV_OPT_TYPE_INT, {.i64=PERSPECTIVE_SENSE_SOURCE}, 0, 1, FLAGS, "sense"},
+    CONST("source",       "specify locations in source to send to corners in destination", PERSPECTIVE_SENSE_SOURCE,      "sense"),
+    CONST("destination", "specify locations in destination to send corners of source",    PERSPECTIVE_SENSE_DESTINATION, "sense"),
+
     { NULL }
 };
 
@@ -105,7 +116,8 @@ enum                                   { VAR_W, VAR_H, VAR_VARS_NB };
 
 static int config_input(AVFilterLink *inlink)
 {
-    double x0, x1, x2, x3, x4, x5, x6, x7, q;
+    double x0, x1, x2, x3, x4, x5, x6, x7, x8, q;
+    double t0, t1, t2, t3;
     AVFilterContext *ctx = inlink->dst;
     PerspectiveContext *s = ctx->priv;
     double (*ref)[2] = s->ref;
@@ -141,32 +153,64 @@ static int config_input(AVFilterLink *inlink)
     if (!s->pv)
         return AVERROR(ENOMEM);
 
-    x6 = ((ref[0][0] - ref[1][0] - ref[2][0] + ref[3][0]) *
-          (ref[2][1] - ref[3][1]) -
-         ( ref[0][1] - ref[1][1] - ref[2][1] + ref[3][1]) *
-          (ref[2][0] - ref[3][0])) * h;
-    x7 = ((ref[0][1] - ref[1][1] - ref[2][1] + ref[3][1]) *
-          (ref[1][0] - ref[3][0]) -
-         ( ref[0][0] - ref[1][0] - ref[2][0] + ref[3][0]) *
-          (ref[1][1] - ref[3][1])) * w;
-    q =  ( ref[1][0] - ref[3][0]) * (ref[2][1] - ref[3][1]) -
-         ( ref[2][0] - ref[3][0]) * (ref[1][1] - ref[3][1]);
-
-    x0 = q * (ref[1][0] - ref[0][0]) * h + x6 * ref[1][0];
-    x1 = q * (ref[2][0] - ref[0][0]) * w + x7 * ref[2][0];
-    x2 = q *  ref[0][0] * w * h;
-    x3 = q * (ref[1][1] - ref[0][1]) * h + x6 * ref[1][1];
-    x4 = q * (ref[2][1] - ref[0][1]) * w + x7 * ref[2][1];
-    x5 = q *  ref[0][1] * w * h;
+    switch (s->sense) {
+    case PERSPECTIVE_SENSE_SOURCE:
+        x6 = ((ref[0][0] - ref[1][0] - ref[2][0] + ref[3][0]) *
+              (ref[2][1] - ref[3][1]) -
+             ( ref[0][1] - ref[1][1] - ref[2][1] + ref[3][1]) *
+              (ref[2][0] - ref[3][0])) * h;
+        x7 = ((ref[0][1] - ref[1][1] - ref[2][1] + ref[3][1]) *
+              (ref[1][0] - ref[3][0]) -
+             ( ref[0][0] - ref[1][0] - ref[2][0] + ref[3][0]) *
+              (ref[1][1] - ref[3][1])) * w;
+        q =  ( ref[1][0] - ref[3][0]) * (ref[2][1] - ref[3][1]) -
+             ( ref[2][0] - ref[3][0]) * (ref[1][1] - ref[3][1]);
+
+        x0 = q * (ref[1][0] - ref[0][0]) * h + x6 * ref[1][0];
+        x1 = q * (ref[2][0] - ref[0][0]) * w + x7 * ref[2][0];
+        x2 = q *  ref[0][0] * w * h;
+        x3 = q * (ref[1][1] - ref[0][1]) * h + x6 * ref[1][1];
+        x4 = q * (ref[2][1] - ref[0][1]) * w + x7 * ref[2][1];
+        x5 = q *  ref[0][1] * w * h;
+        x8 = q * w * h;
+        break;
+    case PERSPECTIVE_SENSE_DESTINATION:
+        t0 = ref[0][0] * (ref[3][1] - ref[1][1]) +
+             ref[1][0] * (ref[0][1] - ref[3][1]) +
+             ref[3][0] * (ref[1][1] - ref[0][1]);
+        t1 = ref[1][0] * (ref[2][1] - ref[3][1]) +
+             ref[2][0] * (ref[3][1] - ref[1][1]) +
+             ref[3][0] * (ref[1][1] - ref[2][1]);
+        t2 = ref[0][0] * (ref[3][1] - ref[2][1]) +
+             ref[2][0] * (ref[0][1] - ref[3][1]) +
+             ref[3][0] * (ref[2][1] - ref[0][1]);
+        t3 = ref[0][0] * (ref[1][1] - ref[2][1]) +
+             ref[1][0] * (ref[2][1] - ref[0][1]) +
+             ref[2][0] * (ref[0][1] - ref[1][1]);
+
+        x0 = t0 * t1 * w * (ref[2][1] - ref[0][1]);
+        x1 = t0 * t1 * w * (ref[0][0] - ref[2][0]);
+        x2 = t0 * t1 * w * (ref[0][1] * ref[2][0] - ref[0][0] * ref[2][1]);
+        x3 = t1 * t2 * h * (ref[1][1] - ref[0][1]);
+        x4 = t1 * t2 * h * (ref[0][0] - ref[1][0]);
+        x5 = t1 * t2 * h * (ref[0][1] * ref[1][0] - ref[0][0] * ref[1][1]);
+        x6 = t1 * t2 * (ref[1][1] - ref[0][1]) +
+             t0 * t3 * (ref[2][1] - ref[3][1]);
+        x7 = t1 * t2 * (ref[0][0] - ref[1][0]) +
+             t0 * t3 * (ref[3][0] - ref[2][0]);
+        x8 = t1 * t2 * (ref[0][1] * ref[1][0] - ref[0][0] * ref[1][1]) +
+             t0 * t3 * (ref[2][0] * ref[3][1] - ref[2][1] * ref[3][0]);
+        break;
+    }
 
     for (y = 0; y < h; y++){
         for (x = 0; x < w; x++){
             int u, v;
 
             u = (int)floor(SUB_PIXELS * (x0 * x + x1 * y + x2) /
-                                        (x6 * x + x7 * y + q * w * h) + 0.5);
+                                        (x6 * x + x7 * y + x8) + 0.5);
             v = (int)floor(SUB_PIXELS * (x3 * x + x4 * y + x5) /
-                                        (x6 * x + x7 * y + q * w * h) + 0.5);
+                                        (x6 * x + x7 * y + x8) + 0.5);
 
             s->pv[x + y * w][0] = u;
             s->pv[x + y * w][1] = v;
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel

Reply via email to