2 * Copyright (c) 2002 Michael Niedermayer <michaelni@gmx.at>
4 * This file is part of FFmpeg.
6 * FFmpeg is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 * Shape Adaptive Blur filter, ported from MPlayer libmpcodecs/vf_sab.c
26 #include "libavutil/opt.h"
27 #include "libavutil/pixdesc.h"
28 #include "libswscale/swscale.h"
36 float pre_filter_radius
;
39 struct SwsContext
*pre_filter_context
;
40 uint8_t *pre_filter_buf
;
41 int pre_filter_linesize
;
45 #define COLOR_DIFF_COEFF_SIZE 512
46 int color_diff_coeff
[COLOR_DIFF_COEFF_SIZE
];
55 unsigned int sws_flags
;
58 static int query_formats(AVFilterContext
*ctx
)
60 static const enum AVPixelFormat pix_fmts
[] = {
68 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
73 #define RADIUS_MIN 0.1
74 #define RADIUS_MAX 4.0
76 #define PRE_FILTER_RADIUS_MIN 0.1
77 #define PRE_FILTER_RADIUS_MAX 2.0
79 #define STRENGTH_MIN 0.1
80 #define STRENGTH_MAX 100.0
82 #define OFFSET(x) offsetof(SabContext, x)
83 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
85 static const AVOption sab_options
[] = {
86 { "luma_radius", "set luma radius", OFFSET(luma
.radius
), AV_OPT_TYPE_FLOAT
, {.dbl
=1.0}, RADIUS_MIN
, RADIUS_MAX
, .flags
=FLAGS
},
87 { "lr" , "set luma radius", OFFSET(luma
.radius
), AV_OPT_TYPE_FLOAT
, {.dbl
=1.0}, RADIUS_MIN
, RADIUS_MAX
, .flags
=FLAGS
},
88 { "luma_pre_filter_radius", "set luma pre-filter radius", OFFSET(luma
.pre_filter_radius
), AV_OPT_TYPE_FLOAT
, {.dbl
=1.0}, PRE_FILTER_RADIUS_MIN
, PRE_FILTER_RADIUS_MAX
, .flags
=FLAGS
},
89 { "lpfr", "set luma pre-filter radius", OFFSET(luma
.pre_filter_radius
), AV_OPT_TYPE_FLOAT
, {.dbl
=1.0}, PRE_FILTER_RADIUS_MIN
, PRE_FILTER_RADIUS_MAX
, .flags
=FLAGS
},
90 { "luma_strength", "set luma strength", OFFSET(luma
.strength
), AV_OPT_TYPE_FLOAT
, {.dbl
=1.0}, STRENGTH_MIN
, STRENGTH_MAX
, .flags
=FLAGS
},
91 { "ls", "set luma strength", OFFSET(luma
.strength
), AV_OPT_TYPE_FLOAT
, {.dbl
=1.0}, STRENGTH_MIN
, STRENGTH_MAX
, .flags
=FLAGS
},
93 { "chroma_radius", "set chroma radius", OFFSET(chroma
.radius
), AV_OPT_TYPE_FLOAT
, {.dbl
=RADIUS_MIN
-1}, RADIUS_MIN
-1, RADIUS_MAX
, .flags
=FLAGS
},
94 { "cr", "set chroma radius", OFFSET(chroma
.radius
), AV_OPT_TYPE_FLOAT
, {.dbl
=RADIUS_MIN
-1}, RADIUS_MIN
-1, RADIUS_MAX
, .flags
=FLAGS
},
95 { "chroma_pre_filter_radius", "set chroma pre-filter radius", OFFSET(chroma
.pre_filter_radius
), AV_OPT_TYPE_FLOAT
, {.dbl
=PRE_FILTER_RADIUS_MIN
-1},
96 PRE_FILTER_RADIUS_MIN
-1, PRE_FILTER_RADIUS_MAX
, .flags
=FLAGS
},
97 { "cpfr", "set chroma pre-filter radius", OFFSET(chroma
.pre_filter_radius
), AV_OPT_TYPE_FLOAT
, {.dbl
=PRE_FILTER_RADIUS_MIN
-1},
98 PRE_FILTER_RADIUS_MIN
-1, PRE_FILTER_RADIUS_MAX
, .flags
=FLAGS
},
99 { "chroma_strength", "set chroma strength", OFFSET(chroma
.strength
), AV_OPT_TYPE_FLOAT
, {.dbl
=STRENGTH_MIN
-1}, STRENGTH_MIN
-1, STRENGTH_MAX
, .flags
=FLAGS
},
100 { "cs", "set chroma strength", OFFSET(chroma
.strength
), AV_OPT_TYPE_FLOAT
, {.dbl
=STRENGTH_MIN
-1}, STRENGTH_MIN
-1, STRENGTH_MAX
, .flags
=FLAGS
},
105 AVFILTER_DEFINE_CLASS(sab
);
107 static av_cold
int init(AVFilterContext
*ctx
)
109 SabContext
*sab
= ctx
->priv
;
111 /* make chroma default to luma values, if not explicitly set */
112 if (sab
->chroma
.radius
< RADIUS_MIN
)
113 sab
->chroma
.radius
= sab
->luma
.radius
;
114 if (sab
->chroma
.pre_filter_radius
< PRE_FILTER_RADIUS_MIN
)
115 sab
->chroma
.pre_filter_radius
= sab
->luma
.pre_filter_radius
;
116 if (sab
->chroma
.strength
< STRENGTH_MIN
)
117 sab
->chroma
.strength
= sab
->luma
.strength
;
119 sab
->luma
.quality
= sab
->chroma
.quality
= 3.0;
120 sab
->sws_flags
= SWS_POINT
;
122 av_log(ctx
, AV_LOG_VERBOSE
,
123 "luma_radius:%f luma_pre_filter_radius::%f luma_strength:%f "
124 "chroma_radius:%f chroma_pre_filter_radius:%f chroma_strength:%f\n",
125 sab
->luma
.radius
, sab
->luma
.pre_filter_radius
, sab
->luma
.strength
,
126 sab
->chroma
.radius
, sab
->chroma
.pre_filter_radius
, sab
->chroma
.strength
);
130 static void close_filter_param(FilterParam
*f
)
132 if (f
->pre_filter_context
) {
133 sws_freeContext(f
->pre_filter_context
);
134 f
->pre_filter_context
= NULL
;
136 av_freep(&f
->pre_filter_buf
);
137 av_freep(&f
->dist_coeff
);
140 static av_cold
void uninit(AVFilterContext
*ctx
)
142 SabContext
*sab
= ctx
->priv
;
144 close_filter_param(&sab
->luma
);
145 close_filter_param(&sab
->chroma
);
148 static int open_filter_param(FilterParam
*f
, int width
, int height
, unsigned int sws_flags
)
153 int linesize
= FFALIGN(width
, 8);
155 f
->pre_filter_buf
= av_malloc(linesize
* height
);
156 if (!f
->pre_filter_buf
)
157 return AVERROR(ENOMEM
);
159 f
->pre_filter_linesize
= linesize
;
160 vec
= sws_getGaussianVec(f
->pre_filter_radius
, f
->quality
);
161 sws_f
.lumH
= sws_f
.lumV
= vec
;
162 sws_f
.chrH
= sws_f
.chrV
= NULL
;
163 f
->pre_filter_context
= sws_getContext(width
, height
, AV_PIX_FMT_GRAY8
,
164 width
, height
, AV_PIX_FMT_GRAY8
,
165 sws_flags
, &sws_f
, NULL
, NULL
);
168 vec
= sws_getGaussianVec(f
->strength
, 5.0);
169 for (i
= 0; i
< COLOR_DIFF_COEFF_SIZE
; i
++) {
171 int index
= i
-COLOR_DIFF_COEFF_SIZE
/2 + vec
->length
/2;
173 if (index
< 0 || index
>= vec
->length
) d
= 0.0;
174 else d
= vec
->coeff
[index
];
176 f
->color_diff_coeff
[i
] = (int)(d
/vec
->coeff
[vec
->length
/2]*(1<<12) + 0.5);
180 vec
= sws_getGaussianVec(f
->radius
, f
->quality
);
181 f
->dist_width
= vec
->length
;
182 f
->dist_linesize
= FFALIGN(vec
->length
, 8);
183 f
->dist_coeff
= av_malloc_array(f
->dist_width
, f
->dist_linesize
* sizeof(*f
->dist_coeff
));
184 if (!f
->dist_coeff
) {
186 return AVERROR(ENOMEM
);
189 for (y
= 0; y
< vec
->length
; y
++) {
190 for (x
= 0; x
< vec
->length
; x
++) {
191 double d
= vec
->coeff
[x
] * vec
->coeff
[y
];
192 f
->dist_coeff
[x
+ y
*f
->dist_linesize
] = (int)(d
*(1<<10) + 0.5);
200 static int config_props(AVFilterLink
*inlink
)
202 SabContext
*sab
= inlink
->dst
->priv
;
203 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
206 sab
->hsub
= desc
->log2_chroma_w
;
207 sab
->vsub
= desc
->log2_chroma_h
;
209 close_filter_param(&sab
->luma
);
210 ret
= open_filter_param(&sab
->luma
, inlink
->w
, inlink
->h
, sab
->sws_flags
);
214 close_filter_param(&sab
->chroma
);
215 ret
= open_filter_param(&sab
->chroma
,
216 FF_CEIL_RSHIFT(inlink
->w
, sab
->hsub
),
217 FF_CEIL_RSHIFT(inlink
->h
, sab
->vsub
), sab
->sws_flags
);
223 static void blur(uint8_t *dst
, const int dst_linesize
,
224 const uint8_t *src
, const int src_linesize
,
225 const int w
, const int h
, FilterParam
*fp
)
229 const int radius
= f
.dist_width
/2;
231 const uint8_t * const src2
[NB_PLANES
] = { src
};
232 int src2_linesize
[NB_PLANES
] = { src_linesize
};
233 uint8_t *dst2
[NB_PLANES
] = { f
.pre_filter_buf
};
234 int dst2_linesize
[NB_PLANES
] = { f
.pre_filter_linesize
};
236 sws_scale(f
.pre_filter_context
, src2
, src2_linesize
, 0, h
, dst2
, dst2_linesize
);
238 #define UPDATE_FACTOR do { \
240 factor = f.color_diff_coeff[COLOR_DIFF_COEFF_SIZE/2 + pre_val - \
241 f.pre_filter_buf[ix + iy*f.pre_filter_linesize]] * f.dist_coeff[dx + dy*f.dist_linesize]; \
242 sum += src[ix + iy*src_linesize] * factor; \
246 for (y
= 0; y
< h
; y
++) {
247 for (x
= 0; x
< w
; x
++) {
251 const int pre_val
= f
.pre_filter_buf
[x
+ y
*f
.pre_filter_linesize
];
252 if (x
>= radius
&& x
< w
- radius
) {
253 for (dy
= 0; dy
< radius
*2 + 1; dy
++) {
255 int iy
= y
+dy
- radius
;
256 if (iy
< 0) iy
= -iy
;
257 else if (iy
>= h
) iy
= h
+h
-iy
-1;
259 for (dx
= 0; dx
< radius
*2 + 1; dx
++) {
260 const int ix
= x
+dx
- radius
;
265 for (dy
= 0; dy
< radius
*2+1; dy
++) {
267 int iy
= y
+dy
- radius
;
268 if (iy
< 0) iy
= -iy
;
269 else if (iy
>= h
) iy
= h
+h
-iy
-1;
271 for (dx
= 0; dx
< radius
*2 + 1; dx
++) {
272 int ix
= x
+dx
- radius
;
273 if (ix
< 0) ix
= -ix
;
274 else if (ix
>= w
) ix
= w
+w
-ix
-1;
279 dst
[x
+ y
*dst_linesize
] = (sum
+ div
/2) / div
;
284 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*inpic
)
286 SabContext
*sab
= inlink
->dst
->priv
;
287 AVFilterLink
*outlink
= inlink
->dst
->outputs
[0];
290 outpic
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
292 av_frame_free(&inpic
);
293 return AVERROR(ENOMEM
);
295 av_frame_copy_props(outpic
, inpic
);
297 blur(outpic
->data
[0], outpic
->linesize
[0], inpic
->data
[0], inpic
->linesize
[0],
298 inlink
->w
, inlink
->h
, &sab
->luma
);
299 if (inpic
->data
[2]) {
300 int cw
= FF_CEIL_RSHIFT(inlink
->w
, sab
->hsub
);
301 int ch
= FF_CEIL_RSHIFT(inlink
->h
, sab
->vsub
);
302 blur(outpic
->data
[1], outpic
->linesize
[1], inpic
->data
[1], inpic
->linesize
[1], cw
, ch
, &sab
->chroma
);
303 blur(outpic
->data
[2], outpic
->linesize
[2], inpic
->data
[2], inpic
->linesize
[2], cw
, ch
, &sab
->chroma
);
306 av_frame_free(&inpic
);
307 return ff_filter_frame(outlink
, outpic
);
310 static const AVFilterPad sab_inputs
[] = {
313 .type
= AVMEDIA_TYPE_VIDEO
,
314 .filter_frame
= filter_frame
,
315 .config_props
= config_props
,
320 static const AVFilterPad sab_outputs
[] = {
323 .type
= AVMEDIA_TYPE_VIDEO
,
328 AVFilter ff_vf_sab
= {
330 .description
= NULL_IF_CONFIG_SMALL("Apply shape adaptive blur."),
331 .priv_size
= sizeof(SabContext
),
334 .query_formats
= query_formats
,
335 .inputs
= sab_inputs
,
336 .outputs
= sab_outputs
,
337 .priv_class
= &sab_class
,
338 .flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
,