FFmpeg
Loading...
Searching...
No Matches
vf_smartblur.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2002 Michael Niedermayer <michaelni@gmx.at>
3 * Copyright (c) 2012 Jeremy Tran
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22/**
23 * @file
24 * Apply a smartblur filter to the input video
25 * Ported from MPlayer libmpcodecs/vf_smartblur.c by Michael Niedermayer.
26 */
27
28#include "libavutil/opt.h"
29#include "libavutil/pixdesc.h"
30#include "libswscale/swscale.h"
31
32#include "avfilter.h"
33#include "filters.h"
34#include "video.h"
35
36#define RADIUS_MIN 0.1
37#define RADIUS_MAX 5.0
38
39#define STRENGTH_MIN -1.0
40#define STRENGTH_MAX 1.0
41
42#define THRESHOLD_MIN -30
43#define THRESHOLD_MAX 30
44
45typedef struct FilterParam {
46 float radius;
47 float strength;
49 float quality;
52
62
63#define OFFSET(x) offsetof(SmartblurContext, x)
64#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
65
66static const AVOption smartblur_options[] = {
67 { "luma_radius", "set luma radius", OFFSET(luma.radius), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, RADIUS_MIN, RADIUS_MAX, .flags=FLAGS },
68 { "lr" , "set luma radius", OFFSET(luma.radius), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, RADIUS_MIN, RADIUS_MAX, .flags=FLAGS },
69 { "luma_strength", "set luma strength", OFFSET(luma.strength), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, STRENGTH_MIN, STRENGTH_MAX, .flags=FLAGS },
70 { "ls", "set luma strength", OFFSET(luma.strength), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, STRENGTH_MIN, STRENGTH_MAX, .flags=FLAGS },
71 { "luma_threshold", "set luma threshold", OFFSET(luma.threshold), AV_OPT_TYPE_INT, {.i64=0}, THRESHOLD_MIN, THRESHOLD_MAX, .flags=FLAGS },
72 { "lt", "set luma threshold", OFFSET(luma.threshold), AV_OPT_TYPE_INT, {.i64=0}, THRESHOLD_MIN, THRESHOLD_MAX, .flags=FLAGS },
73
74 { "chroma_radius", "set chroma radius", OFFSET(chroma.radius), AV_OPT_TYPE_FLOAT, {.dbl=RADIUS_MIN-1}, RADIUS_MIN-1, RADIUS_MAX, .flags=FLAGS },
75 { "cr", "set chroma radius", OFFSET(chroma.radius), AV_OPT_TYPE_FLOAT, {.dbl=RADIUS_MIN-1}, RADIUS_MIN-1, RADIUS_MAX, .flags=FLAGS },
76 { "chroma_strength", "set chroma strength", OFFSET(chroma.strength), AV_OPT_TYPE_FLOAT, {.dbl=STRENGTH_MIN-1}, STRENGTH_MIN-1, STRENGTH_MAX, .flags=FLAGS },
77 { "cs", "set chroma strength", OFFSET(chroma.strength), AV_OPT_TYPE_FLOAT, {.dbl=STRENGTH_MIN-1}, STRENGTH_MIN-1, STRENGTH_MAX, .flags=FLAGS },
78 { "chroma_threshold", "set chroma threshold", OFFSET(chroma.threshold), AV_OPT_TYPE_INT, {.i64=THRESHOLD_MIN-1}, THRESHOLD_MIN-1, THRESHOLD_MAX, .flags=FLAGS },
79 { "ct", "set chroma threshold", OFFSET(chroma.threshold), AV_OPT_TYPE_INT, {.i64=THRESHOLD_MIN-1}, THRESHOLD_MIN-1, THRESHOLD_MAX, .flags=FLAGS },
80
81 { "alpha_radius", "set alpha radius", OFFSET(alpha.radius), AV_OPT_TYPE_FLOAT, {.dbl=RADIUS_MIN-1}, RADIUS_MIN-1, RADIUS_MAX, .flags=FLAGS },
82 { "ar" , "set alpha radius", OFFSET(alpha.radius), AV_OPT_TYPE_FLOAT, {.dbl=RADIUS_MIN-1}, RADIUS_MIN-1, RADIUS_MAX, .flags=FLAGS },
83 { "alpha_strength", "set alpha strength", OFFSET(alpha.strength), AV_OPT_TYPE_FLOAT, {.dbl=STRENGTH_MIN-1}, STRENGTH_MIN-1, STRENGTH_MAX, .flags=FLAGS },
84 { "as", "set alpha strength", OFFSET(alpha.strength), AV_OPT_TYPE_FLOAT, {.dbl=STRENGTH_MIN-1}, STRENGTH_MIN-1, STRENGTH_MAX, .flags=FLAGS },
85 { "alpha_threshold", "set alpha threshold", OFFSET(alpha.threshold), AV_OPT_TYPE_INT, {.i64=THRESHOLD_MIN-1}, THRESHOLD_MIN-1, THRESHOLD_MAX, .flags=FLAGS },
86 { "at", "set alpha threshold", OFFSET(alpha.threshold), AV_OPT_TYPE_INT, {.i64=THRESHOLD_MIN-1}, THRESHOLD_MIN-1, THRESHOLD_MAX, .flags=FLAGS },
87
88 { NULL }
89};
90
92
94{
95 SmartblurContext *s = ctx->priv;
96
97 /* make chroma default to luma values, if not explicitly set */
98 if (s->chroma.radius < RADIUS_MIN)
99 s->chroma.radius = s->luma.radius;
100 if (s->chroma.strength < STRENGTH_MIN)
101 s->chroma.strength = s->luma.strength;
102 if (s->chroma.threshold < THRESHOLD_MIN)
103 s->chroma.threshold = s->luma.threshold;
104
105 /* make alpha default to luma values, if not explicitly set */
106 if (s->alpha.radius < RADIUS_MIN)
107 s->alpha.radius = s->luma.radius;
108 if (s->alpha.strength < STRENGTH_MIN)
109 s->alpha.strength = s->luma.strength;
110 if (s->alpha.threshold < THRESHOLD_MIN)
111 s->alpha.threshold = s->luma.threshold;
112
113 s->luma.quality = s->chroma.quality = s->alpha.quality = 3.0;
114 s->sws_flags = SWS_BICUBIC;
115
117 "luma_radius:%f luma_strength:%f luma_threshold:%d "
118 "chroma_radius:%f chroma_strength:%f chroma_threshold:%d "
119 "alpha_radius:%f alpha_strength:%f alpha_threshold:%d\n",
120 s->luma.radius, s->luma.strength, s->luma.threshold,
121 s->chroma.radius, s->chroma.strength, s->chroma.threshold,
122 s->alpha.radius, s->alpha.strength, s->alpha.threshold);
123 return 0;
124}
125
127{
128 SmartblurContext *s = ctx->priv;
129
130 sws_freeContext(s->luma.filter_context);
131 sws_freeContext(s->chroma.filter_context);
132 sws_freeContext(s->alpha.filter_context);
133}
134
143
144static int alloc_sws_context(FilterParam *f, int width, int height, unsigned int flags)
145{
146 SwsVector *vec;
147 SwsFilter sws_filter;
148
149 vec = sws_getGaussianVec(f->radius, f->quality);
150
151 if (!vec)
152 return AVERROR(EINVAL);
153
154 sws_scaleVec(vec, f->strength);
155 vec->coeff[vec->length / 2] += 1.0 - f->strength;
156 sws_filter.lumH = sws_filter.lumV = vec;
157 sws_filter.chrH = sws_filter.chrV = NULL;
158 f->filter_context = sws_getCachedContext(f->filter_context,
161 flags, &sws_filter, NULL, NULL);
162
163 sws_freeVec(vec);
164
165 if (!f->filter_context)
166 return AVERROR(EINVAL);
167
168 return 0;
169}
170
171static int config_props(AVFilterLink *inlink)
172{
173 SmartblurContext *s = inlink->dst->priv;
175
176 s->hsub = desc->log2_chroma_w;
177 s->vsub = desc->log2_chroma_h;
178
179 alloc_sws_context(&s->luma, inlink->w, inlink->h, s->sws_flags);
180 alloc_sws_context(&s->chroma,
181 AV_CEIL_RSHIFT(inlink->w, s->hsub),
182 AV_CEIL_RSHIFT(inlink->h, s->vsub),
183 s->sws_flags);
184 alloc_sws_context(&s->alpha, inlink->w, inlink->h, s->sws_flags);
185
186 return 0;
187}
188
189static void blur(uint8_t *dst, const int dst_linesize,
190 const uint8_t *src, const int src_linesize,
191 const int w, const int h, const int threshold,
192 struct SwsContext *filter_context)
193{
194 int x, y;
195 int orig, filtered;
196 int diff;
197 /* Declare arrays of 4 to get aligned data */
198 const uint8_t* const src_array[4] = {src};
199 uint8_t *dst_array[4] = {dst};
200 int src_linesize_array[4] = {src_linesize};
201 int dst_linesize_array[4] = {dst_linesize};
202
203 sws_scale(filter_context, src_array, src_linesize_array,
204 0, h, dst_array, dst_linesize_array);
205
206 if (threshold > 0) {
207 for (y = 0; y < h; ++y) {
208 for (x = 0; x < w; ++x) {
209 orig = src[x + y * src_linesize];
210 filtered = dst[x + y * dst_linesize];
211 diff = orig - filtered;
212
213 if (diff > 0) {
214 if (diff > 2 * threshold)
215 dst[x + y * dst_linesize] = orig;
216 else if (diff > threshold)
217 /* add 'diff' and subtract 'threshold' from 'filtered' */
218 dst[x + y * dst_linesize] = orig - threshold;
219 } else {
220 if (-diff > 2 * threshold)
221 dst[x + y * dst_linesize] = orig;
222 else if (-diff > threshold)
223 /* add 'diff' and 'threshold' to 'filtered' */
224 dst[x + y * dst_linesize] = orig + threshold;
225 }
226 }
227 }
228 } else if (threshold < 0) {
229 for (y = 0; y < h; ++y) {
230 for (x = 0; x < w; ++x) {
231 orig = src[x + y * src_linesize];
232 filtered = dst[x + y * dst_linesize];
233 diff = orig - filtered;
234
235 if (diff > 0) {
236 if (diff <= -threshold)
237 dst[x + y * dst_linesize] = orig;
238 else if (diff <= -2 * threshold)
239 /* subtract 'diff' and 'threshold' from 'orig' */
240 dst[x + y * dst_linesize] = filtered - threshold;
241 } else {
242 if (diff >= threshold)
243 dst[x + y * dst_linesize] = orig;
244 else if (diff >= 2 * threshold)
245 /* add 'threshold' and subtract 'diff' from 'orig' */
246 dst[x + y * dst_linesize] = filtered + threshold;
247 }
248 }
249 }
250 }
251}
252
254{
255 SmartblurContext *s = inlink->dst->priv;
256 AVFilterLink *outlink = inlink->dst->outputs[0];
257 AVFrame *outpic;
258 int cw = AV_CEIL_RSHIFT(inlink->w, s->hsub);
259 int ch = AV_CEIL_RSHIFT(inlink->h, s->vsub);
260
261 outpic = ff_get_video_buffer(outlink, outlink->w, outlink->h);
262 if (!outpic) {
264 return AVERROR(ENOMEM);
265 }
266 av_frame_copy_props(outpic, inpic);
267
268 blur(outpic->data[0], outpic->linesize[0],
269 inpic->data[0], inpic->linesize[0],
270 inlink->w, inlink->h, s->luma.threshold,
271 s->luma.filter_context);
272
273 if (inpic->data[2]) {
274 blur(outpic->data[1], outpic->linesize[1],
275 inpic->data[1], inpic->linesize[1],
276 cw, ch, s->chroma.threshold,
277 s->chroma.filter_context);
278 blur(outpic->data[2], outpic->linesize[2],
279 inpic->data[2], inpic->linesize[2],
280 cw, ch, s->chroma.threshold,
281 s->chroma.filter_context);
282 }
283
284 if (inpic->data[3]) {
285 blur(outpic->data[3], outpic->linesize[3],
286 inpic->data[3], inpic->linesize[3],
287 inlink->w, inlink->h, s->alpha.threshold,
288 s->alpha.filter_context);
289 }
290
292 return ff_filter_frame(outlink, outpic);
293}
294
296 {
297 .name = "default",
298 .type = AVMEDIA_TYPE_VIDEO,
299 .filter_frame = filter_frame,
300 .config_props = config_props,
301 },
302};
303
305 .p.name = "smartblur",
306 .p.description = NULL_IF_CONFIG_SMALL("Blur the input video without impacting the outlines."),
307 .p.priv_class = &smartblur_class,
309 .priv_size = sizeof(SmartblurContext),
310 .init = init,
311 .uninit = uninit,
315};
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition dsp.h:87
const FFFilter ff_vf_smartblur
static AVFormatContext * ctx
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition avfilter.c:1068
Main libavfilter public API header.
#define flags(name, subs,...)
Definition cbs_h264.c:74
#define f(width, name)
Definition cbs_vp8.c:236
#define s(width, name)
Definition cbs_vp9.c:198
#define FLAGS
Definition cmdutils.c:598
#define AV_CEIL_RSHIFT(a, b)
Definition common.h:60
#define NULL
Definition coverity.c:32
static int filter_frame(DBEDecodeContext *s, AVFrame *frame)
Definition dolby_e.c:1067
int(* init)(AVBSFContext *ctx)
Definition dts2pts.c:608
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_OPT_TYPE_FLOAT
Underlying C type is float.
Definition opt.h:270
#define AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
Some filters support a generic "enable" expression option that can be used to enable or disable a fil...
Definition avfilter.h:196
#define AVERROR(e)
Definition error.h:45
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:64
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition frame.c:599
#define AV_LOG_VERBOSE
Detailed information.
Definition log.h:226
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
void sws_freeVec(SwsVector *a)
Definition utils.c:2146
SwsVector * sws_getGaussianVec(double variance, double quality)
Return a normalized Gaussian curve used to filter stuff quality = 3 is high quality,...
Definition utils.c:1973
SwsContext * sws_getCachedContext(SwsContext *context, int srcW, int srcH, enum AVPixelFormat srcFormat, int dstW, int dstH, enum AVPixelFormat dstFormat, int flags, SwsFilter *srcFilter, SwsFilter *dstFilter, const double *param)
Check if context can be reused, otherwise reallocate a new one.
Definition utils.c:2331
int attribute_align_arg sws_scale(SwsContext *sws, const uint8_t *const srcSlice[], const int srcStride[], int srcSliceY, int srcSliceH, uint8_t *const dst[], const int dstStride[])
swscale wrapper, so we don't need to export the SwsContext.
Definition swscale.c:1626
void sws_freeContext(SwsContext *swsContext)
Free the swscaler context swsContext.
Definition utils.c:2250
void sws_scaleVec(SwsVector *a, double scalar)
Scale all the coefficients of a by the scalar value.
Definition utils.c:2039
@ SWS_BICUBIC
2-tap cubic B-spline
Definition swscale.h:199
static const int16_t alpha[]
Definition ilbcdata.h:55
static av_cold void uninit(AVBitStreamFilterContext *ctx)
#define FILTER_INPUTS(array)
Definition filters.h:264
#define FILTER_OUTPUTS(array)
Definition filters.h:265
#define FILTER_PIXFMTS_ARRAY(array)
Definition filters.h:244
#define AVFILTER_DEFINE_CLASS(fname)
Definition filters.h:478
#define av_cold
Definition attributes.h:117
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
static enum AVPixelFormat pix_fmts[]
Definition libkvazaar.c:296
const char * desc
Definition libsvtav1.c:83
uint8_t w
Definition llvidencdsp.c:39
AVOptions.
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition pixdesc.c:3460
AVPixelFormat
Pixel format.
Definition pixfmt.h:71
@ AV_PIX_FMT_NONE
Definition pixfmt.h:72
@ AV_PIX_FMT_YUV420P
planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
Definition pixfmt.h:73
@ AV_PIX_FMT_YUV440P
planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples)
Definition pixfmt.h:106
@ AV_PIX_FMT_YUV422P
planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
Definition pixfmt.h:77
@ AV_PIX_FMT_GRAY8
Y , 8bpp.
Definition pixfmt.h:81
@ AV_PIX_FMT_YUVA420P
planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples)
Definition pixfmt.h:108
@ AV_PIX_FMT_YUV410P
planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples)
Definition pixfmt.h:79
@ AV_PIX_FMT_YUV411P
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)
Definition pixfmt.h:80
@ AV_PIX_FMT_YUV444P
planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
Definition pixfmt.h:78
@ AV_PIX_FMT_YUVA444P
planar YUV 4:4:4 32bpp, (1 Cr & Cb sample per 1x1 Y & A samples)
Definition pixfmt.h:174
@ AV_PIX_FMT_YUVA422P
planar YUV 4:2:2 24bpp, (1 Cr & Cb sample per 2x1 Y & A samples)
Definition pixfmt.h:173
static int config_props(AVBitStreamFilterLink *link)
Definition source.c:181
Describe the class of an AVClass context structure.
Definition log.h:76
An instance of a filter.
Definition avfilter.h:273
void * priv
private data for use by the filter
Definition avfilter.h:288
AVFilterLink ** outputs
array of pointers to output links
Definition avfilter.h:285
A filter pad used for either input or output.
Definition filters.h:40
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition frame.h:493
int linesize[AV_NUM_DATA_POINTERS]
For video, a positive or negative value, which is typically indicating the size in bytes of each pict...
Definition frame.h:517
AVOption.
Definition opt.h:428
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition pixdesc.h:69
float quality
Definition vf_sab.c:39
int radius
Definition boxblur.h:32
float strength
Definition vf_sab.c:38
struct SwsContext * filter_context
FilterParam luma
FilterParam alpha
FilterParam chroma
unsigned int sws_flags
Main external API structure.
Definition swscale.h:227
SwsVector * chrV
Definition swscale.h:488
SwsVector * lumH
Definition swscale.h:485
SwsVector * lumV
Definition swscale.h:486
SwsVector * chrH
Definition swscale.h:487
double * coeff
pointer to the list of coefficients
Definition swscale.h:479
int length
number of coefficients in the vector
Definition swscale.h:480
external API header
#define av_log(a,...)
#define src
Definition vp8dsp.c:248
#define height
Definition dsp.h:89
#define width
Definition dsp.h:89
av_frame_free & inpic
Definition vf_mcdeint.c:297
static av_always_inline int diff(const struct color_info *a, const struct color_info *b, const int trans_thresh)
#define RADIUS_MAX
Definition vf_sab.c:69
#define STRENGTH_MAX
Definition vf_sab.c:75
#define RADIUS_MIN
Definition vf_sab.c:68
#define STRENGTH_MIN
Definition vf_sab.c:74
static int alloc_sws_context(FilterParam *f, int width, int height, unsigned int flags)
static int filter_frame(AVFilterLink *inlink, AVFrame *inpic)
static int config_props(AVFilterLink *inlink)
#define THRESHOLD_MAX
static const AVOption smartblur_options[]
static av_cold void uninit(AVFilterContext *ctx)
#define OFFSET(x)
#define THRESHOLD_MIN
static void blur(uint8_t *dst, const int dst_linesize, const uint8_t *src, const int src_linesize, const int w, const int h, const int threshold, struct SwsContext *filter_context)
static const AVFilterPad smartblur_inputs[]
static av_always_inline void chroma(WaveformContext *s, AVFrame *in, AVFrame *out, int component, int intensity, int offset_y, int offset_x, int column, int mirror, int jobnr, int nb_jobs)
const AVFilterPad ff_video_default_filterpad[1]
An AVFilterPad array whose only entry has name "default" and is of type AVMEDIA_TYPE_VIDEO.
Definition video.c:37
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
Definition video.c:89