lavfi/avcodec.c: fix copyright
[ffmpeg.git] / libavfilter / vf_deshake.c
index 838a28a..cdece5c 100644 (file)
@@ -20,7 +20,7 @@
  */
 
 /**
- * @file libavfilter/vf_deshake.c
+ * @file
  * fast deshake / depan video filter
  *
  * SAD block-matching motion compensation to fix small changes in
 #define CHROMA_HEIGHT(link) -((-link->h) >> av_pix_fmt_descriptors[link->format].log2_chroma_h)
 
 enum SearchMethod {
-    EXHAUSTIVE,         //< Search all possible positions
-    SMART_EXHAUSTIVE,   //< Search most possible positions (faster)
+    EXHAUSTIVE,        ///< Search all possible positions
+    SMART_EXHAUSTIVE,  ///< Search most possible positions (faster)
     SEARCH_COUNT
 };
 
 typedef struct {
-    double x;              //< Horizontal shift
-    double y;              //< Vertical shift
+    int x;             ///< Horizontal shift
+    int y;             ///< Vertical shift
+} IntMotionVector;
+
+typedef struct {
+    double x;             ///< Horizontal shift
+    double y;             ///< Vertical shift
 } MotionVector;
 
 typedef struct {
-    MotionVector vector; //< Motion vector
-    double angle;        //< Angle of rotation
-    double zoom;         //< Zoom percentage
+    MotionVector vector;  ///< Motion vector
+    double angle;         ///< Angle of rotation
+    double zoom;          ///< Zoom percentage
 } Transform;
 
 typedef struct {
-    AVFilterBufferRef *ref;     //< Previous frame
-    int rx;                     //< Maximum horizontal shift
-    int ry;                     //< Maximum vertical shift
-    enum FillMethod edge;       //< Edge fill method
-    int blocksize;              //< Size of blocks to compare
-    int contrast;               //< Contrast threshold
-    enum SearchMethod search;   //< Motion search method
+    AVClass av_class;
+    AVFilterBufferRef *ref;    ///< Previous frame
+    int rx;                    ///< Maximum horizontal shift
+    int ry;                    ///< Maximum vertical shift
+    enum FillMethod edge;      ///< Edge fill method
+    int blocksize;             ///< Size of blocks to compare
+    int contrast;              ///< Contrast threshold
+    enum SearchMethod search;  ///< Motion search method
     AVCodecContext *avctx;
-    DSPContext c;               //< Context providing optimized SAD methods
-    Transform last;             //< Transform from last frame
-    int refcount;             //< Number of reference frames (defines averaging window)
+    DSPContext c;              ///< Context providing optimized SAD methods
+    Transform last;            ///< Transform from last frame
+    int refcount;              ///< Number of reference frames (defines averaging window)
     FILE *fp;
+    Transform avg;
+    int cw;                    ///< Crop motion search to this box
+    int ch;
+    int cx;
+    int cy;
 } DeshakeContext;
 
-static int cmp(void const *ca, void const *cb)
+static int cmp(const double *a, const double *b)
 {
-    double *a = (double *) ca;
-    double *b = (double *) cb;
     return *a < *b ? -1 : ( *a > *b ? 1 : 0 );
 }
 
@@ -108,7 +117,7 @@ static double clean_mean(double *values, int count)
     int cut = count / 5;
     int x;
 
-    qsort(values, count, sizeof(double), cmp);
+    qsort(values, count, sizeof(double), (void*)cmp);
 
     for (x = cut; x < count - cut; x++) {
         mean += values[x];
@@ -125,7 +134,7 @@ static double clean_mean(double *values, int count)
  */
 static void find_block_motion(DeshakeContext *deshake, uint8_t *src1,
                               uint8_t *src2, int cx, int cy, int stride,
-                              MotionVector *mv)
+                              IntMotionVector *mv)
 {
     int x, y;
     int diff;
@@ -218,7 +227,7 @@ static int block_contrast(uint8_t *src, int x, int y, int stride, int blocksize)
 /**
  * Find the rotation for a given block.
  */
-static double block_angle(int x, int y, int cx, int cy, MotionVector *shift)
+static double block_angle(int x, int y, int cx, int cy, IntMotionVector *shift)
 {
     double a1, a2, diff;
 
@@ -243,15 +252,13 @@ static void find_motion(DeshakeContext *deshake, uint8_t *src1, uint8_t *src2,
                         int width, int height, int stride, Transform *t)
 {
     int x, y;
-    MotionVector mv = {0, 0};
+    IntMotionVector mv = {0, 0};
     int counts[128][128];
     int count_max_value = 0;
     int contrast;
 
     int pos;
-    double angles[1200];
-    double totalangles = 0;
-
+    double *angles = av_malloc(sizeof(*angles) * width * height / (16 * deshake->blocksize));
     int center_x = 0, center_y = 0;
     double p_x, p_y;
 
@@ -274,7 +281,7 @@ static void find_motion(DeshakeContext *deshake, uint8_t *src1, uint8_t *src2,
                 //av_log(NULL, AV_LOG_ERROR, "%d\n", contrast);
                 find_block_motion(deshake, src1, src2, x, y, stride, &mv);
                 if (mv.x != -1 && mv.y != -1) {
-                    counts[(int)(mv.x + deshake->rx)][(int)(mv.y + deshake->ry)] += 1;
+                    counts[mv.x + deshake->rx][mv.y + deshake->ry] += 1;
                     if (x > deshake->rx && y > deshake->ry)
                         angles[pos++] = block_angle(x, y, 0, 0, &mv);
 
@@ -285,22 +292,16 @@ static void find_motion(DeshakeContext *deshake, uint8_t *src1, uint8_t *src2,
         }
     }
 
-    pos = FFMAX(1, pos);
-
-    center_x /= pos;
-    center_y /= pos;
-
-    for (x = 0; x < pos; x++) {
-        totalangles += angles[x];
+    if (pos) {
+         center_x /= pos;
+         center_y /= pos;
+         t->angle = clean_mean(angles, pos);
+         if (t->angle < 0.001)
+              t->angle = 0;
+    } else {
+         t->angle = 0;
     }
 
-    //av_log(NULL, AV_LOG_ERROR, "Angle: %lf\n", totalangles / (pos - 1));
-    t->angle = totalangles / (pos - 1);
-
-    t->angle = clean_mean(angles, pos);
-    if (t->angle < 0.001)
-        t->angle = 0;
-
     // Find the most common motion vector in the frame and use it as the gmv
     for (y = deshake->ry * 2; y >= 0; y--) {
         for (x = 0; x < deshake->rx * 2 + 1; x++) {
@@ -325,25 +326,32 @@ static void find_motion(DeshakeContext *deshake, uint8_t *src1, uint8_t *src2,
     t->angle = av_clipf(t->angle, -0.1, 0.1);
 
     //av_log(NULL, AV_LOG_ERROR, "%d x %d\n", avg->x, avg->y);
+    av_free(angles);
 }
 
 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 {
     DeshakeContext *deshake = ctx->priv;
+    char filename[256] = {0};
 
     deshake->rx = 16;
     deshake->ry = 16;
-    deshake->edge = FILL_BLANK;
+    deshake->edge = FILL_MIRROR;
     deshake->blocksize = 8;
     deshake->contrast = 125;
     deshake->search = EXHAUSTIVE;
     deshake->refcount = 20;
-    deshake->fp = fopen("stats.txt", "w");
-    fwrite("Ori x, Avg x, Fin x, Ori y, Avg y, Fin y, Ori angle, Avg angle, Fin angle, Ori zoom, Avg zoom, Fin zoom\n", sizeof(char), 104, deshake->fp);
+
+    deshake->cw = -1;
+    deshake->ch = -1;
+    deshake->cx = -1;
+    deshake->cy = -1;
 
     if (args) {
-        sscanf(args, "%d:%d:%d:%d:%d:%d", &deshake->rx, &deshake->ry, (int *)&deshake->edge,
-               &deshake->blocksize, &deshake->contrast, (int *)&deshake->search);
+        sscanf(args, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%255s",
+               &deshake->cx, &deshake->cy, &deshake->cw, &deshake->ch,
+               &deshake->rx, &deshake->ry, (int *)&deshake->edge,
+               &deshake->blocksize, &deshake->contrast, (int *)&deshake->search, filename);
 
         deshake->blocksize /= 2;
 
@@ -353,9 +361,22 @@ static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
         deshake->blocksize = av_clip(deshake->blocksize, 4, 128);
         deshake->contrast = av_clip(deshake->contrast, 1, 255);
         deshake->search = av_clip(deshake->search, EXHAUSTIVE, SEARCH_COUNT - 1);
+
+    }
+    if (*filename)
+        deshake->fp = fopen(filename, "w");
+    if (deshake->fp)
+        fwrite("Ori x, Avg x, Fin x, Ori y, Avg y, Fin y, Ori angle, Avg angle, Fin angle, Ori zoom, Avg zoom, Fin zoom\n", sizeof(char), 104, deshake->fp);
+
+    // Quadword align left edge of box for MMX code, adjust width if necessary
+    // to keep right margin
+    if (deshake->cx > 0) {
+        deshake->cw += deshake->cx - (deshake->cx & ~15);
+        deshake->cx &= ~15;
     }
 
-    av_log(ctx, AV_LOG_INFO, "rx: %d, ry: %d, edge: %d blocksize: %d contrast: %d search: %d\n",
+    av_log(ctx, AV_LOG_INFO, "cx: %d, cy: %d, cw: %d, ch: %d, rx: %d, ry: %d, edge: %d blocksize: %d contrast: %d search: %d\n",
+           deshake->cx, deshake->cy, deshake->cw, deshake->ch,
            deshake->rx, deshake->ry, deshake->edge, deshake->blocksize * 2, deshake->contrast, deshake->search);
 
     return 0;
@@ -384,7 +405,7 @@ static int config_props(AVFilterLink *link)
     deshake->last.angle = 0;
     deshake->last.zoom = 0;
 
-    deshake->avctx= avcodec_alloc_context3(NULL);
+    deshake->avctx = avcodec_alloc_context3(NULL);
     dsputil_init(&deshake->c, deshake->avctx);
 
     return 0;
@@ -395,7 +416,10 @@ static av_cold void uninit(AVFilterContext *ctx)
     DeshakeContext *deshake = ctx->priv;
 
     avfilter_unref_buffer(deshake->ref);
-    fclose(deshake->fp);
+    if (deshake->fp)
+        fclose(deshake->fp);
+    avcodec_close(deshake->avctx);
+    av_freep(&deshake->avctx);
 }
 
 static void end_frame(AVFilterLink *link)
@@ -403,22 +427,33 @@ static void end_frame(AVFilterLink *link)
     DeshakeContext *deshake = link->dst->priv;
     AVFilterBufferRef *in  = link->cur_buf;
     AVFilterBufferRef *out = link->dst->outputs[0]->out_buf;
-    Transform t;
-    static float matrix[9];
-    static Transform avg = {
-        .vector = {
-            .x = 0,
-            .y = 0
-        },
-        .angle = 0,
-        .zoom = 0.0f
-    };
+    Transform t = {{0},0}, orig = {{0},0};
+    float matrix[9];
     float alpha = 2.0 / deshake->refcount;
     char tmp[256];
-    Transform orig;
 
-    // Find the most likely global motion for the current frame
-    find_motion(deshake, (deshake->ref == NULL) ? in->data[0] : deshake->ref->data[0], in->data[0], link->w, link->h, in->linesize[0], &t);
+    if (deshake->cx < 0 || deshake->cy < 0 || deshake->cw < 0 || deshake->ch < 0) {
+        // Find the most likely global motion for the current frame
+        find_motion(deshake, (deshake->ref == NULL) ? in->data[0] : deshake->ref->data[0], in->data[0], link->w, link->h, in->linesize[0], &t);
+    } else {
+        uint8_t *src1 = (deshake->ref == NULL) ? in->data[0] : deshake->ref->data[0];
+        uint8_t *src2 = in->data[0];
+
+        deshake->cx = FFMIN(deshake->cx, link->w);
+        deshake->cy = FFMIN(deshake->cy, link->h);
+
+        if ((unsigned)deshake->cx + (unsigned)deshake->cw > link->w) deshake->cw = link->w - deshake->cx;
+        if ((unsigned)deshake->cy + (unsigned)deshake->ch > link->h) deshake->ch = link->h - deshake->cy;
+
+        // Quadword align right margin
+        deshake->cw &= ~15;
+
+        src1 += deshake->cy * in->linesize[0] + deshake->cx;
+        src2 += deshake->cy * in->linesize[0] + deshake->cx;
+
+        find_motion(deshake, src1, src2, deshake->cw, deshake->ch, in->linesize[0], &t);
+    }
+
 
     // Copy transform so we can output it later to compare to the smoothed value
     orig.vector.x = t.vector.x;
@@ -427,17 +462,17 @@ static void end_frame(AVFilterLink *link)
     orig.zoom = t.zoom;
 
     // Generate a one-sided moving exponential average
-    avg.vector.x = alpha * t.vector.x + (1.0 - alpha) * avg.vector.x;
-    avg.vector.y = alpha * t.vector.y + (1.0 - alpha) * avg.vector.y;
-    avg.angle = alpha * t.angle + (1.0 - alpha) * avg.angle;
-    avg.zoom = alpha * t.zoom + (1.0 - alpha) * avg.zoom;
+    deshake->avg.vector.x = alpha * t.vector.x + (1.0 - alpha) * deshake->avg.vector.x;
+    deshake->avg.vector.y = alpha * t.vector.y + (1.0 - alpha) * deshake->avg.vector.y;
+    deshake->avg.angle = alpha * t.angle + (1.0 - alpha) * deshake->avg.angle;
+    deshake->avg.zoom = alpha * t.zoom + (1.0 - alpha) * deshake->avg.zoom;
 
     // Remove the average from the current motion to detect the motion that
     // is not on purpose, just as jitter from bumping the camera
-    t.vector.x -= avg.vector.x;
-    t.vector.y -= avg.vector.y;
-    t.angle -= avg.angle;
-    t.zoom -= avg.zoom;
+    t.vector.x -= deshake->avg.vector.x;
+    t.vector.y -= deshake->avg.vector.y;
+    t.angle -= deshake->avg.angle;
+    t.zoom -= deshake->avg.zoom;
 
     // Invert the motion to undo it
     t.vector.x *= -1;
@@ -445,8 +480,10 @@ static void end_frame(AVFilterLink *link)
     t.angle *= -1;
 
     // Write statistics to file
-    snprintf(tmp, 256, "%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f\n", orig.vector.x, avg.vector.x, t.vector.x, orig.vector.y, avg.vector.y, t.vector.y, orig.angle, avg.angle, t.angle, orig.zoom, avg.zoom, t.zoom);
-    fwrite(tmp, sizeof(char), strlen(tmp), deshake->fp);
+    if (deshake->fp) {
+        snprintf(tmp, 256, "%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f\n", orig.vector.x, deshake->avg.vector.x, t.vector.x, orig.vector.y, deshake->avg.vector.y, t.vector.y, orig.angle, deshake->avg.angle, t.angle, orig.zoom, deshake->avg.zoom, t.zoom);
+        fwrite(tmp, sizeof(char), strlen(tmp), deshake->fp);
+    }
 
     // Turn relative current frame motion into absolute by adding it to the
     // last absolute motion
@@ -507,7 +544,7 @@ AVFilter avfilter_vf_deshake = {
     .uninit = uninit,
     .query_formats = query_formats,
 
-    .inputs    = (AVFilterPad[]) {{ .name             = "default",
+    .inputs    = (const AVFilterPad[]) {{ .name       = "default",
                                     .type             = AVMEDIA_TYPE_VIDEO,
                                     .draw_slice       = draw_slice,
                                     .end_frame        = end_frame,
@@ -515,7 +552,7 @@ AVFilter avfilter_vf_deshake = {
                                     .min_perms        = AV_PERM_READ, },
                                   { .name = NULL}},
 
-    .outputs   = (AVFilterPad[]) {{ .name             = "default",
+    .outputs   = (const AVFilterPad[]) {{ .name       = "default",
                                     .type             = AVMEDIA_TYPE_VIDEO, },
                                   { .name = NULL}},
 };