[FFmpeg-devel] [PATCH] avfilter/vf_zoompan: fix shaking when zooming
Robert Deibel
deibel.robert at googlemail.com
Thu Jan 23 14:40:55 EET 2020
Fix shaking of image when zoom is applied by the zoompan filter.
Resolves ticket https://trac.ffmpeg.org/ticket/4298
---
libavfilter/vf_zoompan.c | 81 ++++++++++++++++++++++++++++------------
1 file changed, 57 insertions(+), 24 deletions(-)
diff --git a/libavfilter/vf_zoompan.c b/libavfilter/vf_zoompan.c
index 59c9b19ec8..ac8d69dac7 100644
--- a/libavfilter/vf_zoompan.c
+++ b/libavfilter/vf_zoompan.c
@@ -150,16 +150,29 @@ static int config_output(AVFilterLink *outlink)
return 0;
}
+/**
+ * Scales n to be a multiple of grid_size and divisible by two but minimally 2 * grid_size.
+ *
+ * Used to scale the width and height of a frame to fit with the subsampling grid.
+ * @param n The number to be scaled.
+ * @param grid_size the size of the grid.
+ * @return The scaled number divisible by 2, minimally 2 * grid_size
+ */
+static int scale_to_grid(int n, uint8_t grid_size){
+ return (((n + (1 << grid_size) * 2) & ~((1 << grid_size) - 1)) + 1) & ~1;
+}
+
static int output_single_frame(AVFilterContext *ctx, AVFrame *in, double *var_values, int i,
double *zoom, double *dx, double *dy)
{
ZPContext *s = ctx->priv;
AVFilterLink *outlink = ctx->outputs[0];
int64_t pts = s->frame_count;
- int k, x, y, w, h, ret = 0;
+ int k, x, y, crop_x, crop_y, w, h, crop_w, crop_h, overscaled_w, overscaled_h, ret = 0;
uint8_t *input[4];
int px[4], py[4];
AVFrame *out;
+ double dw, dh, dx_scaled, dy_scaled;
var_values[VAR_PX] = s->x;
var_values[VAR_PY] = s->y;
@@ -173,32 +186,38 @@ static int output_single_frame(AVFilterContext *ctx, AVFrame *in, double *var_va
*zoom = av_clipd(*zoom, 1, 10);
var_values[VAR_ZOOM] = *zoom;
- w = in->width * (1.0 / *zoom);
- h = in->height * (1.0 / *zoom);
- *dx = av_expr_eval(s->x_expr, var_values, NULL);
+ w = dw = (double) in->width * (1.0 / *zoom);
+ h = dh = (double) in->height * (1.0 / *zoom);
+ crop_w = scale_to_grid(w, s->desc->log2_chroma_w);
+ crop_h = scale_to_grid(h, s->desc->log2_chroma_h);
+ overscaled_w = outlink->w + (crop_w - dw) * *zoom;
+ overscaled_h = outlink->h + (crop_h - dh) * *zoom;
- x = *dx = av_clipd(*dx, 0, FFMAX(in->width - w, 0));
- var_values[VAR_X] = *dx;
- x &= ~((1 << s->desc->log2_chroma_w) - 1);
+ *dx = av_expr_eval(s->x_expr, var_values, NULL);
+ crop_x = ceil(av_clipd(*dx - (((double) crop_w - w) / 2.0), 0, FFMAX(in->width - crop_w, 0)));
+ var_values[VAR_X] = *dx = av_clipd(*dx, 0, FFMAX(in->width - w, 0));
+ crop_x &= ~((1 << s->desc->log2_chroma_w) - 1);
*dy = av_expr_eval(s->y_expr, var_values, NULL);
+ crop_y = ceil(av_clipd(*dy - (((double) crop_h - h)/ 2.0), 0, FFMAX(in->height - crop_h, 0)));
+ var_values[VAR_Y] = *dy = av_clipd(*dy, 0, FFMAX(in->height - h, 0));
+ crop_y &= ~((1 << s->desc->log2_chroma_h) - 1);
- y = *dy = av_clipd(*dy, 0, FFMAX(in->height - h, 0));
- var_values[VAR_Y] = *dy;
- y &= ~((1 << s->desc->log2_chroma_h) - 1);
-
- out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+ out = ff_get_video_buffer(outlink, overscaled_w, overscaled_h);
if (!out) {
ret = AVERROR(ENOMEM);
return ret;
}
- px[1] = px[2] = AV_CEIL_RSHIFT(x, s->desc->log2_chroma_w);
- px[0] = px[3] = x;
+ px[1] = px[2] = AV_CEIL_RSHIFT(crop_x, s->desc->log2_chroma_w);
+ px[0] = px[3] = crop_x;
- py[1] = py[2] = AV_CEIL_RSHIFT(y, s->desc->log2_chroma_h);
- py[0] = py[3] = y;
+ py[1] = py[2] = AV_CEIL_RSHIFT(crop_y, s->desc->log2_chroma_h);
+ py[0] = py[3] = crop_y;
+
+ for (k = 0; k<4; k++)
+ input[k] = in->data[k] + py[k] * in->linesize[k] + px[k];
s->sws = sws_alloc_context();
if (!s->sws) {
@@ -206,21 +225,35 @@ static int output_single_frame(AVFilterContext *ctx, AVFrame *in, double *var_va
goto error;
}
- for (k = 0; in->data[k]; k++)
- input[k] = in->data[k] + py[k] * in->linesize[k] + px[k];
-
- av_opt_set_int(s->sws, "srcw", w, 0);
- av_opt_set_int(s->sws, "srch", h, 0);
+ av_opt_set_int(s->sws, "srcw", crop_w, 0);
+ av_opt_set_int(s->sws, "srch", crop_h, 0);
av_opt_set_int(s->sws, "src_format", in->format, 0);
- av_opt_set_int(s->sws, "dstw", outlink->w, 0);
- av_opt_set_int(s->sws, "dsth", outlink->h, 0);
+ av_opt_set_int(s->sws, "dstw", overscaled_w, 0);
+ av_opt_set_int(s->sws, "dsth", overscaled_h, 0);
av_opt_set_int(s->sws, "dst_format", outlink->format, 0);
av_opt_set_int(s->sws, "sws_flags", SWS_BICUBIC, 0);
if ((ret = sws_init_context(s->sws, NULL, NULL)) < 0)
goto error;
- sws_scale(s->sws, (const uint8_t *const *)&input, in->linesize, 0, h, out->data, out->linesize);
+ sws_scale(s->sws, (const uint8_t *const *)&input, in->linesize, 0, crop_h, out->data, out->linesize);
+
+ dx_scaled = ((crop_w - dw) * *zoom) / (((double) crop_w - dw) / (*dx - (double) crop_x));
+ x = ceil(av_clipd(dx_scaled, 0, FFMAX(overscaled_w - outlink->w, 0)));
+ x &= ~((1 << s->desc->log2_chroma_w) - 1);
+
+ dy_scaled = ((crop_h - dh) * *zoom) / (((double) crop_h - dh) / (*dx - (double) crop_y));
+ y = ceil(av_clipd(dy_scaled, 0, FFMAX(overscaled_h - outlink->h, 0)));
+ y &= ~((1 << s->desc->log2_chroma_h) - 1);
+
+ px[1] = px[2] = AV_CEIL_RSHIFT(x, s->desc->log2_chroma_w);
+ px[0] = px[3] = x;
+
+ py[1] = py[2] = AV_CEIL_RSHIFT(y, s->desc->log2_chroma_h);
+ py[0] = py[3] = y;
+
+ for (k = 0; k<4; k++)
+ out->data[k] = out->data[k] + py[k] * out->linesize[k] + px[k];
out->pts = pts;
s->frame_count++;
--
2.25.0
More information about the ffmpeg-devel
mailing list