X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavfilter%2Fvf_geq.c;h=2905efae249b56ea2913b161b48dce14bd0d6a49;hb=3972ec28e76f08466279ad48355c45bcbe45b09f;hp=91eb9685f9fe710cba9d7e2fd570374f0edc2ba0;hpb=a024c3ce9a502849013a4aa2c0a6de0c9270261c;p=ffmpeg diff --git a/libavfilter/vf_geq.c b/libavfilter/vf_geq.c index 91eb9685f9f..2905efae249 100644 --- a/libavfilter/vf_geq.c +++ b/libavfilter/vf_geq.c @@ -4,19 +4,19 @@ * * This file is part of FFmpeg. * - * FFmpeg is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with FFmpeg; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** @@ -33,12 +33,20 @@ #include "libavutil/pixdesc.h" #include "internal.h" +#define NB_PLANES 4 + +enum InterpolationMethods { + INTERP_NEAREST, + INTERP_BILINEAR, + NB_INTERP +}; + static const char *const var_names[] = { "X", "Y", "W", "H", "N", "SW", "SH", "T", NULL }; enum { VAR_X, VAR_Y, VAR_W, VAR_H, VAR_N, VAR_SW, VAR_SH, VAR_T, VAR_VARS_NB }; typedef struct GEQContext { const AVClass *class; - AVExpr *e[4]; ///< expressions for each plane + AVExpr *e[NB_PLANES]; ///< expressions for each plane char *expr_str[4+3]; ///< expression strings for each plane AVFrame *picref; ///< current input buffer uint8_t *dst; ///< reference pointer to the 8bits output @@ -46,8 +54,12 @@ typedef struct GEQContext { double values[VAR_VARS_NB]; ///< expression values int hsub, vsub; ///< chroma subsampling int planes; ///< number of planes + int interpolation; int is_rgb; int bps; + + double *pixel_sums[NB_PLANES]; + int needs_sum[NB_PLANES]; } GEQContext; enum { Y = 0, U, V, A, G, B, R }; @@ -70,6 +82,12 @@ static const AVOption geq_options[] = { { "g", "set green expression", OFFSET(expr_str[G]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, { "blue_expr", "set blue expression", OFFSET(expr_str[B]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, { "b", "set blue expression", OFFSET(expr_str[B]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "interpolation","set interpolation method", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERP_BILINEAR}, 0, NB_INTERP-1, FLAGS, "interp" }, + { "i", "set interpolation method", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERP_BILINEAR}, 0, NB_INTERP-1, FLAGS, "interp" }, + { "nearest", "nearest interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERP_NEAREST}, 0, 0, FLAGS, "interp" }, + { "n", "nearest interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERP_NEAREST}, 0, 0, FLAGS, "interp" }, + { "bilinear", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERP_BILINEAR}, 0, 0, FLAGS, "interp" }, + { "b", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERP_BILINEAR}, 0, 0, FLAGS, "interp" }, {NULL}, }; @@ -88,24 +106,108 @@ static inline double getpix(void *priv, double x, double y, int plane) if (!src) return 0; - xi = x = av_clipf(x, 0, w - 2); - yi = y = av_clipf(y, 0, h - 2); + if (geq->interpolation == INTERP_BILINEAR) { + xi = x = av_clipd(x, 0, w - 2); + yi = y = av_clipd(y, 0, h - 2); - x -= xi; - y -= yi; + x -= xi; + y -= yi; - if (geq->bps > 8) { - const uint16_t *src16 = (const uint16_t*)src; - linesize /= 2; + if (geq->bps > 8) { + const uint16_t *src16 = (const uint16_t*)src; + linesize /= 2; - return (1-y)*((1-x)*src16[xi + yi * linesize] + x*src16[xi + 1 + yi * linesize]) - + y *((1-x)*src16[xi + (yi+1) * linesize] + x*src16[xi + 1 + (yi+1) * linesize]); + return (1-y)*((1-x)*src16[xi + yi * linesize] + x*src16[xi + 1 + yi * linesize]) + + y *((1-x)*src16[xi + (yi+1) * linesize] + x*src16[xi + 1 + (yi+1) * linesize]); + } else { + return (1-y)*((1-x)*src[xi + yi * linesize] + x*src[xi + 1 + yi * linesize]) + + y *((1-x)*src[xi + (yi+1) * linesize] + x*src[xi + 1 + (yi+1) * linesize]); + } } else { - return (1-y)*((1-x)*src[xi + yi * linesize] + x*src[xi + 1 + yi * linesize]) - + y *((1-x)*src[xi + (yi+1) * linesize] + x*src[xi + 1 + (yi+1) * linesize]); + xi = av_clipd(x, 0, w - 1); + yi = av_clipd(y, 0, h - 1); + + if (geq->bps > 8) { + const uint16_t *src16 = (const uint16_t*)src; + linesize /= 2; + + return src16[xi + yi * linesize]; + } else { + return src[xi + yi * linesize]; + } } } +static int calculate_sums(GEQContext *geq, int plane, int w, int h) +{ + int xi, yi; + AVFrame *picref = geq->picref; + const uint8_t *src = picref->data[plane]; + int linesize = picref->linesize[plane]; + + if (!geq->pixel_sums[plane]) + geq->pixel_sums[plane] = av_malloc_array(w, h * sizeof (*geq->pixel_sums[plane])); + if (!geq->pixel_sums[plane]) + return AVERROR(ENOMEM); + if (geq->bps > 8) + linesize /= 2; + for (yi = 0; yi < h; yi ++) { + if (geq->bps > 8) { + const uint16_t *src16 = (const uint16_t*)src; + double linesum = 0; + + for (xi = 0; xi < w; xi ++) { + linesum += src16[xi + yi * linesize]; + geq->pixel_sums[plane][xi + yi * w] = linesum; + } + } else { + double linesum = 0; + + for (xi = 0; xi < w; xi ++) { + linesum += src[xi + yi * linesize]; + geq->pixel_sums[plane][xi + yi * w] = linesum; + } + } + if (yi) + for (xi = 0; xi < w; xi ++) { + geq->pixel_sums[plane][xi + yi * w] += geq->pixel_sums[plane][xi + yi * w - w]; + } + } + return 0; +} + +static inline double getpix_integrate_internal(GEQContext *geq, int x, int y, int plane, int w, int h) +{ + if (x > w - 1) { + double boundary = getpix_integrate_internal(geq, w - 1, y, plane, w, h); + return 2*boundary - getpix_integrate_internal(geq, 2*(w - 1) - x, y, plane, w, h); + } else if (y > h - 1) { + double boundary = getpix_integrate_internal(geq, x, h - 1, plane, w, h); + return 2*boundary - getpix_integrate_internal(geq, x, 2*(h - 1) - y, plane, w, h); + } else if (x < 0) { + if (x == -1) return 0; + return - getpix_integrate_internal(geq, -x-2, y, plane, w, h); + } else if (y < 0) { + if (y == -1) return 0; + return - getpix_integrate_internal(geq, x, -y-2, plane, w, h); + } + + return geq->pixel_sums[plane][x + y * w]; +} + +static inline double getpix_integrate(void *priv, double x, double y, int plane) { + GEQContext *geq = priv; + AVFrame *picref = geq->picref; + const uint8_t *src = picref->data[plane]; + const int w = (plane == 1 || plane == 2) ? AV_CEIL_RSHIFT(picref->width, geq->hsub) : picref->width; + const int h = (plane == 1 || plane == 2) ? AV_CEIL_RSHIFT(picref->height, geq->vsub) : picref->height; + + if (!src) + return 0; + + return getpix_integrate_internal(geq, lrint(av_clipd(x, -w, 2*w)), lrint(av_clipd(y, -h, 2*h)), plane, w, h); +} + //TODO: cubic interpolate //TODO: keep the last few frames static double lum(void *priv, double x, double y) { return getpix(priv, x, y, 0); } @@ -113,6 +215,11 @@ static double cb(void *priv, double x, double y) { return getpix(priv, x, y, 1) static double cr(void *priv, double x, double y) { return getpix(priv, x, y, 2); } static double alpha(void *priv, double x, double y) { return getpix(priv, x, y, 3); } +static double lumsum(void *priv, double x, double y) { return getpix_integrate(priv, x, y, 0); } +static double cbsum(void *priv, double x, double y) { return getpix_integrate(priv, x, y, 1); } +static double crsub(void *priv, double x, double y) { return getpix_integrate(priv, x, y, 2); } +static double alphasum(void *priv, double x, double y) { return getpix_integrate(priv, x, y, 3); } + static av_cold int geq_init(AVFilterContext *ctx) { GEQContext *geq = ctx->priv; @@ -161,17 +268,33 @@ static av_cold int geq_init(AVFilterContext *ctx) goto end; } - for (plane = 0; plane < 4; plane++) { - static double (*p[])(void *, double, double) = { lum, cb, cr, alpha }; - static const char *const func2_yuv_names[] = { "lum", "cb", "cr", "alpha", "p", NULL }; - static const char *const func2_rgb_names[] = { "g", "b", "r", "alpha", "p", NULL }; + for (plane = 0; plane < NB_PLANES; plane++) { + static double (*p[])(void *, double, double) = { + lum , cb , cr , alpha , + lumsum, cbsum, crsub, alphasum, + }; + static const char *const func2_yuv_names[] = { + "lum" , "cb" , "cr" , "alpha" , "p", + "lumsum", "cbsum", "crsum", "alphasum", "psum", + NULL }; + static const char *const func2_rgb_names[] = { + "g" , "b" , "r" , "alpha" , "p", + "gsum", "bsum", "rsum", "alphasum", "psum", + NULL }; const char *const *func2_names = geq->is_rgb ? func2_rgb_names : func2_yuv_names; - double (*func2[])(void *, double, double) = { lum, cb, cr, alpha, p[plane], NULL }; + double (*func2[])(void *, double, double) = { + lum , cb , cr , alpha , p[plane], + lumsum, cbsum, crsub, alphasum, p[plane + 4], + NULL }; + int counter[10] = {0}; ret = av_expr_parse(&geq->e[plane], geq->expr_str[plane < 3 && geq->is_rgb ? plane+4 : plane], var_names, NULL, NULL, func2_names, func2, 0, ctx); if (ret < 0) break; + + av_expr_count_func(geq->e[plane], counter, FF_ARRAY_ELEMS(counter), 2); + geq->needs_sum[plane] = counter[5] + counter[6] + counter[7] + counter[8] + counter[9]; } end: @@ -328,6 +451,9 @@ static int geq_filter_frame(AVFilterLink *inlink, AVFrame *in) td.plane = plane; td.linesize = linesize; + if (geq->needs_sum[plane]) + calculate_sums(geq, plane, width, height); + ctx->internal->execute(ctx, slice_geq_filter, &td, NULL, FFMIN(height, nb_threads)); } @@ -342,6 +468,8 @@ static av_cold void geq_uninit(AVFilterContext *ctx) for (i = 0; i < FF_ARRAY_ELEMS(geq->e); i++) av_expr_free(geq->e[i]); + for (i = 0; i < NB_PLANES; i++) + av_freep(&geq->pixel_sums); } static const AVFilterPad geq_inputs[] = {