From patchwork Sun Sep 30 20:13:45 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Mina X-Patchwork-Id: 10522 Delivered-To: ffmpegpatchwork@gmail.com Received: by 2002:ab0:73d2:0:0:0:0:0 with SMTP id m18csp923461uaq; Sun, 30 Sep 2018 13:22:16 -0700 (PDT) X-Google-Smtp-Source: ACcGV62AHywHIwhgb9ySwc+FebM690zcQJ4cRT6kQMXEQvCXQU3Anl8bMGKIjez6KFz/UgvF0USd X-Received: by 2002:adf:82e3:: with SMTP id 90-v6mr4728844wrc.131.1538338936656; Sun, 30 Sep 2018 13:22:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1538338936; cv=none; d=google.com; s=arc-20160816; b=Dkp19TeMiX1F8WQYqlBPo4HAmro1oOj4wEbEa89aWYJY6KDwQ7B8eTt1FfvzkHxG2D VXtIcAP7FVe+20erJXxQYPKg1+fv+P60q56+ECMJSwfpbwtPCiVC/67BNg3tv5KuhX2D sP1eNw9PvxCFn4o9DBHdqnmjrbmgelPAwgKFHYWvdfmKlAhtWzsLdr8xZavDEFdbSLYf jirFEWBq0OIcvJ2TSUSUlpvs/Rc3h8GD+PB2Y8Gcav1dm+qGB7CLDgKcxxwZcSCJlVDL 44/u04DJoSfjxsCVdKEqHaIPywdL1adYH5hqGYEcYhWw4YXmcca/69YXrtsBxk2junds p1gw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:cc:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:subject :content-language:mime-version:user-agent:date:message-id:to:from :dkim-signature:delivered-to; bh=24k5QgI2MXXOFXtpaYE5y+Qb0qJHukkEPAGESwkDSFU=; b=X2JrMIKnRe58urnhHlMoH1XG4+6RnZYbp/f7rUsB1dhAHJVR3SclBi12t+LMHLy7nD r1ZfK8mnqmt7Wsx8i2Aymvfhab4jNdtWXX6557w3XPgkTIj96ft5xVEW/TWeT6m8aAPM f8wFaP7DBhbVU1pO4pbSIqxdpkwiYpOQ+8y4YSWG3QaXI8+Puov1P383gP31oubhztbY jNtvp43PpJqnvMpIDaN+L6fLa4uUT4W/wXx5LCa8wAAHsoZfXz3AVE4OBrT9ObdC/P2R wsIW5lAudkt+zC18X2llVIF6+01YJfsCy1l9XBOSAm3NmwQsvmEceyIX1cHSvd+mU3W5 yPWg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=kZCpDNz6; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id r11-v6si10215372wrl.282.2018.09.30.13.22.16; Sun, 30 Sep 2018 13:22:16 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=kZCpDNz6; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 3D224689C1C; Sun, 30 Sep 2018 23:21:56 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr1-f42.google.com (mail-wr1-f42.google.com [209.85.221.42]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 869C3689C14 for ; Sun, 30 Sep 2018 23:21:49 +0300 (EEST) Received: by mail-wr1-f42.google.com with SMTP id y16so3310980wrw.3 for ; Sun, 30 Sep 2018 13:22:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:subject:to:cc:message-id:date:user-agent:mime-version :content-language; bh=LD8Vui0yzlkQszeBj0RWQw1dp/biqWeF8ibkwWqWvGk=; b=kZCpDNz6fumxUsq4ON1hFfvVtRzM+VVq+BbNcGxcKRY+q1NJGC2y+UHYST3c3wAKXA YqLcmLHArTV13XtFCzkYMDTKj3ORZ3MjoWcYteVqJEobFiDAeJiA7Ng3gN/PgYDRW5iV n9X5xRslcp27Yrrn7vx4zwmGXcxnWx/g0vMzeAkbsdpoNhhCfBnXh29TrhhPEDdqI3zT RjYMyokbC1myHMFRO6V40Oe+Z+UrtikOGxYgyd8XxUuAx68D2Y+1n3ldrtin14eygvQt aQMh8pv8l7ciA84u8mLOYf6GTgOVORE29mieqi9fhK6UUI/ZTEYftO/fNdKCXz/LvzU8 T0tg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:subject:to:cc:message-id:date:user-agent :mime-version:content-language; bh=LD8Vui0yzlkQszeBj0RWQw1dp/biqWeF8ibkwWqWvGk=; b=ELYgAEV0VQekFP6OJ/VQW8WFJEY+mm2amfvhAQ3t+0jGB6bKPKf/6vNAje9O+20Byg jQP1ol6Awb7kNWjYZGZWcvU9ivsa6HahIlwpVWff+XYFXGlrHvzi2SH7S5F8325zFGk4 ZAf+qmpR2krjoMAru+mXuj81nlSckqRPh4uoh6RhT0U2FI+Jjjij+QR2Wz8xpWq0DawF n7ZVWmMzrxD95KLQ4xwNaKW81l4IodXWpXD7ntphhxaOjYp/xHYVFQaq8jw/3+9cS9hk j23YVtPyVcKXTFAhkMn0TpR4EWfQ3k4iEBL4aYjSW6/hiwpzx6naVswHVNgzyTuVmmT2 tvNg== X-Gm-Message-State: ABuFfogwkwSz79oPH5jsj7WQYHgcSpUdVayPvObSburKd6HqWVeoTzZc VyxcNw/0Htz8onFrSrLtu9I= X-Received: by 2002:a5d:548e:: with SMTP id h14-v6mr5128889wrv.316.1538338438007; Sun, 30 Sep 2018 13:13:58 -0700 (PDT) Received: from [192.168.1.104] ([41.235.43.211]) by smtp.gmail.com with ESMTPSA id z141-v6sm6719560wmc.3.2018.09.30.13.13.53 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 30 Sep 2018 13:13:57 -0700 (PDT) From: Mina To: FFmpeg development discussions and patches Message-ID: <92490191-b6ed-04b2-3ca1-7f175e357c47@gmail.com> Date: Sun, 30 Sep 2018 22:13:45 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.9.1 MIME-Version: 1.0 Content-Language: en-US Subject: [FFmpeg-devel] [PATCH] lavfi/vf_colorconstancy: add weighted_greyedge X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Thilo Borgmann Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Hi,   This patch was part of GSoC Color Constancy filter. It introduces an improved color constancy filter(weighted_greyedge) based on the already pushed greyedge. I'm sending it again after updating it against latest version of master. Regards, Mina Sami From 33b92a766ff575c9765c15097cdda19a2e9e9e60 Mon Sep 17 00:00:00 2001 From: Mina Date: Sun, 30 Sep 2018 21:32:17 +0200 Subject: [PATCH] lavfi/vf_colorconstancy: add weighted_greyedge Signed-off-by: Mina --- doc/filters.texi | 39 ++++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/vf_colorconstancy.c | 322 ++++++++++++++++++++++++-------- 4 files changed, 288 insertions(+), 75 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index 6d3833317e..f00ee4781c 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -17526,6 +17526,45 @@ separatefields,select=eq(mod(n,4),0)+eq(mod(n,4),3),weave @end example @end itemize +@section weighted_greyedge +A color constancy variation filter which estimates scene illumination via weighted grey edge +algorithm and corrects the scene colors accordingly. + +See: @url{http://refbase.cvc.uab.es/files/GGW2012.pdf} + +The filter accepts the following options: + +@table @option +@item minknorm +The Minkowski parameter to be used for calculating the Minkowski distance. Must +be chosen in the range [1,20] and default value is 1. + +@item sigma +The standard deviation of Gaussian blur to be applied on the scene. Must be +chosen in the range [0.2,1024.0] and default value = 1. + +@item wmap_k +The power applied to the weight map to emphasis heigher weights. Must be chosen +in the range [1,20] and default value = 2. + +@item iters +The max number of iterations for performing the algorithm. Must be chosen in the +range [1,20] and default value = 5. + +@item angerr +The angular error threshold in degrees for stoping the algorithm. Must be chosen +in the range [0,360] and default value = 0.001. +@end table + +@subsection Examples +@itemize + +@item +@example +weighted_greyedge=minknorm=5:sigma=2:wmap_k=10:iters=10:angerr=0.0005 +@end example +@end itemize + @section xbr Apply the xBR high-quality magnification filter which is designed for pixel art. It follows a set of edge-detection rules, see diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 62cc2f561f..f9626d4e16 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -400,6 +400,7 @@ OBJS-$(CONFIG_VSTACK_FILTER) += vf_stack.o framesync.o OBJS-$(CONFIG_W3FDIF_FILTER) += vf_w3fdif.o OBJS-$(CONFIG_WAVEFORM_FILTER) += vf_waveform.o OBJS-$(CONFIG_WEAVE_FILTER) += vf_weave.o +OBJS-$(CONFIG_WEIGHTED_GREYEDGE_FILTER) += vf_colorconstancy.o OBJS-$(CONFIG_XBR_FILTER) += vf_xbr.o OBJS-$(CONFIG_YADIF_FILTER) += vf_yadif.o OBJS-$(CONFIG_ZMQ_FILTER) += f_zmq.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 5e72803b13..095da84151 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -381,6 +381,7 @@ extern AVFilter ff_vf_vstack; extern AVFilter ff_vf_w3fdif; extern AVFilter ff_vf_waveform; extern AVFilter ff_vf_weave; +extern AVFilter ff_vf_weighted_greyedge; extern AVFilter ff_vf_xbr; extern AVFilter ff_vf_yadif; extern AVFilter ff_vf_zmq; diff --git a/libavfilter/vf_colorconstancy.c b/libavfilter/vf_colorconstancy.c index e3bb39e51b..3b84a637c7 100644 --- a/libavfilter/vf_colorconstancy.c +++ b/libavfilter/vf_colorconstancy.c @@ -26,6 +26,14 @@ * * @cite * J. van de Weijer, Th. Gevers, A. Gijsenij "Edge-Based Color Constancy". + * + * @cite + * J. van de Weijer, Th. Gevers, and J. Geusebroek, + * “Edge and corner detection by photometric quasi-invariants”. + * + * @cite + * A. Gijsenij, Th. Gevers, J. van de Weijer, + * "Improving Color Constancy by Photometric Edge Weighting". */ #include "libavutil/imgutils.h" @@ -39,7 +47,8 @@ #include -#define GREY_EDGE "greyedge" +#define GREY_EDGE "greyedge" +#define WEIGHTED_GREY_EDGE "weighted_greyedge" #define SQRT3 1.73205080757 @@ -79,6 +88,10 @@ typedef struct ColorConstancyContext { int minknorm; /**< @minknorm = 0 : getMax instead */ double sigma; + int wmap_k; + int iters; + double angerr_thresh; + int nb_threads; int planeheight[4]; int planewidth[4]; @@ -477,53 +490,73 @@ static int filter_slice_grey_edge(AVFilterContext* ctx, void* arg, int jobnr, in } /** - * Main control function for grey edge algorithm. + * Slice function for weighted grey edge algorithm that is called iteratively to + * calculate and apply weight scheme. * * @param ctx the filter context. - * @param in frame to perfrom grey edge on. + * @param arg data to be passed between threads. + * @param jobnr current job nubmer. + * @param nb_jobs total number of jobs. * - * @return 0 in case of success, a negative value corresponding to an - * AVERROR code in case of failure. + * @return 0. */ -static int filter_grey_edge(AVFilterContext *ctx, AVFrame *in) +static int filter_slice_weighted_grey_edge(AVFilterContext* ctx, void* arg, int jobnr, int nb_jobs) { ColorConstancyContext *s = ctx->priv; - ThreadData td; - int minknorm = s->minknorm; - int difford = s->difford; - double *white = s->white; - int nb_jobs = FFMIN3(s->planeheight[1], s->planewidth[1], s->nb_threads); - int plane, job, ret; - - td.in = in; - ret = setup_derivative_buffers(ctx, &td); - if (ret) { - return ret; - } - get_derivative(ctx, &td); - if (difford > 0) { - ctx->internal->execute(ctx, slice_normalize, &td, NULL, nb_jobs); - } + ThreadData *td = arg; + AVFrame *in = td->in; + int minknorm = s->minknorm; + int wmap_k = s->wmap_k; + const uint8_t thresh = 255; - ctx->internal->execute(ctx, filter_slice_grey_edge, &td, NULL, nb_jobs); - if (!minknorm) { - for (plane = 0; plane < NUM_PLANES; ++plane) { - white[plane] = 0; // All values are absolute - for (job = 0; job < nb_jobs; ++job) { - white[plane] = FFMAX(white[plane] , td.data[INDEX_DST][plane][job]); + const int height = s->planeheight[1]; + const int width = s->planewidth[1]; + const int slice_start = (height * jobnr) / nb_jobs; + const int slice_end = (height * (jobnr+1)) / nb_jobs; + double dx, dy, d, dw[NUM_PLANES]; + double o3_x, o3_y; + double spec_var, weight; + double result[NUM_PLANES]; + int r, c, p; + + memset(result, 0, NUM_PLANES * sizeof(result[0])); + + for (r = slice_start; r < slice_end; ++r) { + for (c = 0; c < width; ++c) { + d = 0; + o3_x = 0; + o3_y = 0; + for (p = 0; p < NUM_PLANES; ++p) { + dx = td->data[INDEX_DX][p][INDX2D(r, c, width)]; + dy = td->data[INDEX_DY][p][INDX2D(r, c, width)]; + dw[p] = sqrt(dx * dx + dy * dy); + + d += dw[p] * dw[p]; + o3_x += dx; + o3_y += dy; } - } - } else { - for (plane = 0; plane < NUM_PLANES; ++plane) { - white[plane] = 0; - for (job = 0; job < nb_jobs; ++job) { - white[plane] += td.data[INDEX_DST][plane][job]; + d = sqrt(d); + o3_x /= SQRT3; + o3_y /= SQRT3; + + spec_var = sqrt( o3_x * o3_x + o3_y * o3_y); + weight = spec_var / d; + if (weight > 1) { + weight = 1; + } + weight = pow(weight, wmap_k); + + for (p = 0; p < NUM_PLANES; ++p) { + result[p] += ( pow(dw[p] * weight , minknorm) + * (in->data[p][INDX2D(r, c, in->linesize[p])] < thresh) ); } - white[plane] = pow(white[plane], 1./minknorm); } } - cleanup_derivative_buffers(&td, difford + 1, NUM_PLANES); + for (p = 0; p < NUM_PLANES; ++p) { + /** To make sure you save in a place you won't use again */ + td->data[INDEX_DST][p][slice_start] = result[p]; + } return 0; } @@ -554,32 +587,6 @@ static void normalize_light(double *light) } } -/** - * Redirects to corresponding algorithm estimation function and performs normalization - * after estimation. - * - * @param ctx the filter context. - * @param in frame to perfrom estimation on. - * - * @return 0 in case of success, a negative value corresponding to an - * AVERROR code in case of failure. - */ -static int illumination_estimation(AVFilterContext *ctx, AVFrame *in) -{ - ColorConstancyContext *s = ctx->priv; - int ret; - - ret = filter_grey_edge(ctx, in); - - av_log(ctx, AV_LOG_DEBUG, "Estimated illumination= %f %f %f\n", - s->white[0], s->white[1], s->white[2]); - normalize_light(s->white); - av_log(ctx, AV_LOG_DEBUG, "Estimated illumination after normalization= %f %f %f\n", - s->white[0], s->white[1], s->white[2]); - - return ret; -} - /** * Performs simple correction via diagonal transformation model. * @@ -636,6 +643,146 @@ static void chromatic_adaptation(AVFilterContext *ctx, AVFrame *in, AVFrame *out ctx->internal->execute(ctx, diagonal_transformation, &td, NULL, nb_jobs); } +/** + * Main control function for grey edge algorithm. + * + * @param ctx the filter context. + * @param in frame to perfrom grey edge on. + * + * @return 0 in case of success, a negative value corresponding to an + * AVERROR code in case of failure. + */ +static int filter_grey_edge(AVFilterContext *ctx, AVFrame *in, AVFrame **out) +{ + ColorConstancyContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + int minknorm = s->minknorm; + int difford = s->difford; + double *white = s->white; + int nb_jobs = FFMIN3(s->planeheight[1], s->planewidth[1], s->nb_threads); + ThreadData td; + int plane, job, ret; + + td.in = in; + ret = setup_derivative_buffers(ctx, &td); + if (ret) { + return ret; + } + get_derivative(ctx, &td); + if (difford > 0) { + ctx->internal->execute(ctx, slice_normalize, &td, NULL, nb_jobs); + } + + ctx->internal->execute(ctx, filter_slice_grey_edge, &td, NULL, nb_jobs); + if (!minknorm) { + for (plane = 0; plane < NUM_PLANES; ++plane) { + white[plane] = 0; // All values are absolute + for (job = 0; job < nb_jobs; ++job) { + white[plane] = FFMAX(white[plane] , td.data[INDEX_DST][plane][job]); + } + } + } else { + for (plane = 0; plane < NUM_PLANES; ++plane) { + white[plane] = 0; + for (job = 0; job < nb_jobs; ++job) { + white[plane] += td.data[INDEX_DST][plane][job]; + } + white[plane] = pow(white[plane], 1./minknorm); + } + } + + cleanup_derivative_buffers(&td, difford + 1, NUM_PLANES); + + av_log(ctx, AV_LOG_DEBUG, "Estimated illumination= %f %f %f\n", + s->white[0], s->white[1], s->white[2]); + normalize_light(s->white); + av_log(ctx, AV_LOG_DEBUG, "Estimated illumination after normalization= %f %f %f\n", + s->white[0], s->white[1], s->white[2]); + + if (av_frame_is_writable(in)) { + *out = in; + } else { + *out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if ( !(*out) ) { + av_log(ctx, AV_LOG_ERROR, "Out of memory while allocating output video buffer.\n"); + return AVERROR(ENOMEM); + } + av_frame_copy_props(*out, in); + } + chromatic_adaptation(ctx, in, *out); + + return 0; +} + +#define ANG_ERR(w) acos( ( (w)[0] + (w)[1] + (w)[2] ) / sqrt(3) ) +#define RAD2DEG(r) (r) * 180 / M_PI + +/** + * Main control function for weighted grey edge algorithm. + * + * @param ctx the filter context. + * @param in frame to perfrom grey edge on. + * + * @return 0 in case of success, a negative value corresponding to an + * AVERROR code in case of failure. + */ +static int filter_weighted_grey_edge(AVFilterContext *ctx, AVFrame *in, AVFrame **out) +{ + ColorConstancyContext *s = ctx->priv; + const double angerr_thresh = s->angerr_thresh; + double *curr_estim_illum = s->white; + int difford = s->difford; + int minknorm = s->minknorm; + int it = s->iters; + int nb_jobs = FFMIN3(s->planeheight[1], s->planewidth[1], s->nb_threads); + const int height = s->planeheight[1]; + double final_estim_illum[NUM_PLANES]; + ThreadData td; + int ret, plane, job; + + ret = filter_grey_edge(ctx, in, out); + if (ret) { + return ret; + } + memcpy(final_estim_illum, curr_estim_illum, NUM_PLANES * sizeof(*curr_estim_illum)); + + td.in = in; + ret = setup_derivative_buffers(ctx, &td); + if (ret) { + return ret; + } + + while (it-- && RAD2DEG(ANG_ERR(curr_estim_illum)) > angerr_thresh) { + get_derivative(ctx, &td); + ctx->internal->execute(ctx, filter_slice_weighted_grey_edge, &td, NULL, nb_jobs); + + for (plane = 0; plane < NUM_PLANES; ++plane) { + curr_estim_illum[plane] = 0; + for (job = 0; job < nb_jobs; ++job) { + curr_estim_illum[plane] += td.data[INDEX_DST][plane][(height * job) / nb_jobs]; + } + curr_estim_illum[plane] = pow(curr_estim_illum[plane], 1./minknorm); + } + + normalize_light(curr_estim_illum); + chromatic_adaptation(ctx, td.in, td.in); + + for (plane = 0; plane < NUM_PLANES; ++plane) { + final_estim_illum[plane] *= curr_estim_illum[plane]; + } + normalize_light(final_estim_illum); + + av_log(ctx, AV_LOG_DEBUG, "Current scene illumination=%f %f %f\n", curr_estim_illum[0], + curr_estim_illum[1], curr_estim_illum[2]); + av_log(ctx, AV_LOG_DEBUG, "Angular error = %.16f degrees\n", RAD2DEG(ANG_ERR(curr_estim_illum)) ); + av_log(ctx, AV_LOG_DEBUG, "Iterative estimated illumination after normalization= %f %f %f\n", + final_estim_illum[0], final_estim_illum[1], final_estim_illum[2]); + } + + cleanup_derivative_buffers(&td, difford + 1, NUM_PLANES); + return 0; +} + static int query_formats(AVFilterContext *ctx) { static const enum AVPixelFormat pix_fmts[] = { @@ -657,6 +804,11 @@ static int config_props(AVFilterLink *inlink) double sigma = s->sigma; int ret; + if (!strcmp(ctx->filter->name, WEIGHTED_GREY_EDGE)) { + /** Enforce difford since it's not an option */ + s->difford = 1; + } + if (!floor(break_off_sigma * sigma + 0.5) && s->difford) { av_log(ctx, AV_LOG_ERROR, "floor(%f * sigma) must be > 0 when difford > 0.\n", break_off_sigma); return AVERROR(EINVAL); @@ -683,22 +835,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) AVFrame *out; int ret; - ret = illumination_estimation(ctx, in); - if (ret) { - return ret; + if (!strcmp(ctx->filter->name, GREY_EDGE)) { + ret = filter_grey_edge(ctx, in, &out); + } else if (!strcmp(ctx->filter->name, WEIGHTED_GREY_EDGE)) { + ret = filter_weighted_grey_edge(ctx, in, &out); } - if (av_frame_is_writable(in)) { - out = in; - } else { - out = ff_get_video_buffer(outlink, outlink->w, outlink->h); - if (!out) { - av_log(ctx, AV_LOG_ERROR, "Out of memory while allocating output video buffer.\n"); - return AVERROR(ENOMEM); - } - av_frame_copy_props(out, in); + if (ret) { + return ret; } - chromatic_adaptation(ctx, in, out); return ff_filter_frame(outlink, out); } @@ -756,3 +901,30 @@ AVFilter ff_vf_greyedge = { }; #endif /* CONFIG_GREY_EDGE_FILTER */ + +#if CONFIG_WEIGHTED_GREYEDGE_FILTER + +static const AVOption weighted_greyedge_options[] = { + { "minknorm", "set Minkowski norm", OFFSET(minknorm), AV_OPT_TYPE_INT, {.i64=1}, 1, 20, FLAGS }, + { "sigma", "set sigma", OFFSET(sigma), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.2, 1024.0, FLAGS }, + { "wmap_k", "set power of weight map", OFFSET(wmap_k), AV_OPT_TYPE_INT, {.i64=2}, 1, 20, FLAGS }, + { "iters", "set max number of iterations", OFFSET(iters), AV_OPT_TYPE_INT, {.i64=5}, 1, 20, FLAGS }, + { "angerr", "set angular error threshold", OFFSET(angerr_thresh), AV_OPT_TYPE_DOUBLE, {.dbl=0.001}, 0, 360, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(weighted_greyedge); + +AVFilter ff_vf_weighted_greyedge = { + .name = WEIGHTED_GREY_EDGE, + .description = NULL_IF_CONFIG_SMALL("Estimates scene illumination by weighted grey edge assumption."), + .priv_size = sizeof(ColorConstancyContext), + .priv_class = &weighted_greyedge_class, + .query_formats = query_formats, + .uninit = uninit, + .inputs = colorconstancy_inputs, + .outputs = colorconstancy_outputs, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, +}; + +#endif /* CONFIG_WEIGHTED_GREYEDGE_FILTER */ \ No newline at end of file -- 2.17.1