FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_sab.c
Date: 2024-04-26 14:42:52
Exec Total Coverage
Lines: 0 116 0.0%
Functions: 0 7 0.0%
Branches: 0 44 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2002 Michael Niedermayer <michaelni@gmx.at>
3 *
4 * This file is part of FFmpeg.
5 *
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.
10 *
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.
15 *
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.
19 */
20
21 /**
22 * @file
23 * Shape Adaptive Blur filter, ported from MPlayer libmpcodecs/vf_sab.c
24 */
25
26 #include "libavutil/mem.h"
27 #include "libavutil/opt.h"
28 #include "libavutil/pixdesc.h"
29 #include "libswscale/swscale.h"
30
31 #include "avfilter.h"
32 #include "internal.h"
33 #include "video.h"
34
35 typedef struct FilterParam {
36 float radius;
37 float pre_filter_radius;
38 float strength;
39 float quality;
40 struct SwsContext *pre_filter_context;
41 uint8_t *pre_filter_buf;
42 int pre_filter_linesize;
43 int dist_width;
44 int dist_linesize;
45 int *dist_coeff;
46 #define COLOR_DIFF_COEFF_SIZE 512
47 int color_diff_coeff[COLOR_DIFF_COEFF_SIZE];
48 } FilterParam;
49
50 typedef struct SabContext {
51 const AVClass *class;
52 FilterParam luma;
53 FilterParam chroma;
54 int hsub;
55 int vsub;
56 unsigned int sws_flags;
57 } SabContext;
58
59 static const enum AVPixelFormat pix_fmts[] = {
60 AV_PIX_FMT_YUV420P,
61 AV_PIX_FMT_YUV410P,
62 AV_PIX_FMT_YUV444P,
63 AV_PIX_FMT_YUV422P,
64 AV_PIX_FMT_YUV411P,
65 AV_PIX_FMT_NONE
66 };
67
68 #define RADIUS_MIN 0.1
69 #define RADIUS_MAX 4.0
70
71 #define PRE_FILTER_RADIUS_MIN 0.1
72 #define PRE_FILTER_RADIUS_MAX 2.0
73
74 #define STRENGTH_MIN 0.1
75 #define STRENGTH_MAX 100.0
76
77 #define OFFSET(x) offsetof(SabContext, x)
78 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
79
80 static const AVOption sab_options[] = {
81 { "luma_radius", "set luma radius", OFFSET(luma.radius), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, RADIUS_MIN, RADIUS_MAX, .flags=FLAGS },
82 { "lr" , "set luma radius", OFFSET(luma.radius), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, RADIUS_MIN, RADIUS_MAX, .flags=FLAGS },
83 { "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 },
84 { "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 },
85 { "luma_strength", "set luma strength", OFFSET(luma.strength), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, STRENGTH_MIN, STRENGTH_MAX, .flags=FLAGS },
86 { "ls", "set luma strength", OFFSET(luma.strength), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, STRENGTH_MIN, STRENGTH_MAX, .flags=FLAGS },
87
88 { "chroma_radius", "set chroma radius", OFFSET(chroma.radius), AV_OPT_TYPE_FLOAT, {.dbl=RADIUS_MIN-1}, RADIUS_MIN-1, RADIUS_MAX, .flags=FLAGS },
89 { "cr", "set chroma radius", OFFSET(chroma.radius), AV_OPT_TYPE_FLOAT, {.dbl=RADIUS_MIN-1}, RADIUS_MIN-1, RADIUS_MAX, .flags=FLAGS },
90 { "chroma_pre_filter_radius", "set chroma pre-filter radius", OFFSET(chroma.pre_filter_radius), AV_OPT_TYPE_FLOAT, {.dbl=PRE_FILTER_RADIUS_MIN-1},
91 PRE_FILTER_RADIUS_MIN-1, PRE_FILTER_RADIUS_MAX, .flags=FLAGS },
92 { "cpfr", "set chroma pre-filter radius", OFFSET(chroma.pre_filter_radius), AV_OPT_TYPE_FLOAT, {.dbl=PRE_FILTER_RADIUS_MIN-1},
93 PRE_FILTER_RADIUS_MIN-1, PRE_FILTER_RADIUS_MAX, .flags=FLAGS },
94 { "chroma_strength", "set chroma strength", OFFSET(chroma.strength), AV_OPT_TYPE_FLOAT, {.dbl=STRENGTH_MIN-1}, STRENGTH_MIN-1, STRENGTH_MAX, .flags=FLAGS },
95 { "cs", "set chroma strength", OFFSET(chroma.strength), AV_OPT_TYPE_FLOAT, {.dbl=STRENGTH_MIN-1}, STRENGTH_MIN-1, STRENGTH_MAX, .flags=FLAGS },
96
97 { NULL }
98 };
99
100 AVFILTER_DEFINE_CLASS(sab);
101
102 static av_cold int init(AVFilterContext *ctx)
103 {
104 SabContext *s = ctx->priv;
105
106 /* make chroma default to luma values, if not explicitly set */
107 if (s->chroma.radius < RADIUS_MIN)
108 s->chroma.radius = s->luma.radius;
109 if (s->chroma.pre_filter_radius < PRE_FILTER_RADIUS_MIN)
110 s->chroma.pre_filter_radius = s->luma.pre_filter_radius;
111 if (s->chroma.strength < STRENGTH_MIN)
112 s->chroma.strength = s->luma.strength;
113
114 s->luma.quality = s->chroma.quality = 3.0;
115 s->sws_flags = SWS_POINT;
116
117 av_log(ctx, AV_LOG_VERBOSE,
118 "luma_radius:%f luma_pre_filter_radius::%f luma_strength:%f "
119 "chroma_radius:%f chroma_pre_filter_radius:%f chroma_strength:%f\n",
120 s->luma .radius, s->luma .pre_filter_radius, s->luma .strength,
121 s->chroma.radius, s->chroma.pre_filter_radius, s->chroma.strength);
122 return 0;
123 }
124
125 static void close_filter_param(FilterParam *f)
126 {
127 if (f->pre_filter_context) {
128 sws_freeContext(f->pre_filter_context);
129 f->pre_filter_context = NULL;
130 }
131 av_freep(&f->pre_filter_buf);
132 av_freep(&f->dist_coeff);
133 }
134
135 static av_cold void uninit(AVFilterContext *ctx)
136 {
137 SabContext *s = ctx->priv;
138
139 close_filter_param(&s->luma);
140 close_filter_param(&s->chroma);
141 }
142
143 static int open_filter_param(FilterParam *f, int width, int height, unsigned int sws_flags)
144 {
145 SwsVector *vec;
146 SwsFilter sws_f;
147 int i, x, y;
148 int linesize = FFALIGN(width, 8);
149
150 f->pre_filter_buf = av_malloc(linesize * height);
151 if (!f->pre_filter_buf)
152 return AVERROR(ENOMEM);
153
154 f->pre_filter_linesize = linesize;
155 vec = sws_getGaussianVec(f->pre_filter_radius, f->quality);
156 sws_f.lumH = sws_f.lumV = vec;
157 sws_f.chrH = sws_f.chrV = NULL;
158 f->pre_filter_context = sws_getContext(width, height, AV_PIX_FMT_GRAY8,
159 width, height, AV_PIX_FMT_GRAY8,
160 sws_flags, &sws_f, NULL, NULL);
161 sws_freeVec(vec);
162
163 vec = sws_getGaussianVec(f->strength, 5.0);
164 for (i = 0; i < COLOR_DIFF_COEFF_SIZE; i++) {
165 double d;
166 int index = i-COLOR_DIFF_COEFF_SIZE/2 + vec->length/2;
167
168 if (index < 0 || index >= vec->length) d = 0.0;
169 else d = vec->coeff[index];
170
171 f->color_diff_coeff[i] = (int)(d/vec->coeff[vec->length/2]*(1<<12) + 0.5);
172 }
173 sws_freeVec(vec);
174
175 vec = sws_getGaussianVec(f->radius, f->quality);
176 f->dist_width = vec->length;
177 f->dist_linesize = FFALIGN(vec->length, 8);
178 f->dist_coeff = av_malloc_array(f->dist_width, f->dist_linesize * sizeof(*f->dist_coeff));
179 if (!f->dist_coeff) {
180 sws_freeVec(vec);
181 return AVERROR(ENOMEM);
182 }
183
184 for (y = 0; y < vec->length; y++) {
185 for (x = 0; x < vec->length; x++) {
186 double d = vec->coeff[x] * vec->coeff[y];
187 f->dist_coeff[x + y*f->dist_linesize] = (int)(d*(1<<10) + 0.5);
188 }
189 }
190 sws_freeVec(vec);
191
192 return 0;
193 }
194
195 static int config_props(AVFilterLink *inlink)
196 {
197 SabContext *s = inlink->dst->priv;
198 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
199 int ret;
200
201 s->hsub = desc->log2_chroma_w;
202 s->vsub = desc->log2_chroma_h;
203
204 close_filter_param(&s->luma);
205 ret = open_filter_param(&s->luma, inlink->w, inlink->h, s->sws_flags);
206 if (ret < 0)
207 return ret;
208
209 close_filter_param(&s->chroma);
210 ret = open_filter_param(&s->chroma,
211 AV_CEIL_RSHIFT(inlink->w, s->hsub),
212 AV_CEIL_RSHIFT(inlink->h, s->vsub), s->sws_flags);
213 return ret;
214 }
215
216 #define NB_PLANES 4
217
218 static void blur(uint8_t *dst, const int dst_linesize,
219 const uint8_t *src, const int src_linesize,
220 const int w, const int h, FilterParam *fp)
221 {
222 int x, y;
223 FilterParam f = *fp;
224 const int radius = f.dist_width/2;
225
226 const uint8_t * const src2[NB_PLANES] = { src };
227 int src2_linesize[NB_PLANES] = { src_linesize };
228 uint8_t *dst2[NB_PLANES] = { f.pre_filter_buf };
229 int dst2_linesize[NB_PLANES] = { f.pre_filter_linesize };
230
231 sws_scale(f.pre_filter_context, src2, src2_linesize, 0, h, dst2, dst2_linesize);
232
233 #define UPDATE_FACTOR do { \
234 int factor; \
235 factor = f.color_diff_coeff[COLOR_DIFF_COEFF_SIZE/2 + pre_val - \
236 f.pre_filter_buf[ix + iy*f.pre_filter_linesize]] * f.dist_coeff[dx + dy*f.dist_linesize]; \
237 sum += src[ix + iy*src_linesize] * factor; \
238 div += factor; \
239 } while (0)
240
241 for (y = 0; y < h; y++) {
242 for (x = 0; x < w; x++) {
243 int sum = 0;
244 int div = 0;
245 int dy;
246 const int pre_val = f.pre_filter_buf[x + y*f.pre_filter_linesize];
247 if (x >= radius && x < w - radius) {
248 for (dy = 0; dy < radius*2 + 1; dy++) {
249 int dx;
250 int iy = y+dy - radius;
251 iy = avpriv_mirror(iy, h-1);
252
253 for (dx = 0; dx < radius*2 + 1; dx++) {
254 const int ix = x+dx - radius;
255 UPDATE_FACTOR;
256 }
257 }
258 } else {
259 for (dy = 0; dy < radius*2+1; dy++) {
260 int dx;
261 int iy = y+dy - radius;
262 iy = avpriv_mirror(iy, h-1);
263
264 for (dx = 0; dx < radius*2 + 1; dx++) {
265 int ix = x+dx - radius;
266 ix = avpriv_mirror(ix, w-1);
267 UPDATE_FACTOR;
268 }
269 }
270 }
271 dst[x + y*dst_linesize] = (sum + div/2) / div;
272 }
273 }
274 }
275
276 static int filter_frame(AVFilterLink *inlink, AVFrame *inpic)
277 {
278 SabContext *s = inlink->dst->priv;
279 AVFilterLink *outlink = inlink->dst->outputs[0];
280 AVFrame *outpic;
281
282 outpic = ff_get_video_buffer(outlink, outlink->w, outlink->h);
283 if (!outpic) {
284 av_frame_free(&inpic);
285 return AVERROR(ENOMEM);
286 }
287 av_frame_copy_props(outpic, inpic);
288
289 blur(outpic->data[0], outpic->linesize[0], inpic->data[0], inpic->linesize[0],
290 inlink->w, inlink->h, &s->luma);
291 if (inpic->data[2]) {
292 int cw = AV_CEIL_RSHIFT(inlink->w, s->hsub);
293 int ch = AV_CEIL_RSHIFT(inlink->h, s->vsub);
294 blur(outpic->data[1], outpic->linesize[1], inpic->data[1], inpic->linesize[1], cw, ch, &s->chroma);
295 blur(outpic->data[2], outpic->linesize[2], inpic->data[2], inpic->linesize[2], cw, ch, &s->chroma);
296 }
297
298 av_frame_free(&inpic);
299 return ff_filter_frame(outlink, outpic);
300 }
301
302 static const AVFilterPad sab_inputs[] = {
303 {
304 .name = "default",
305 .type = AVMEDIA_TYPE_VIDEO,
306 .filter_frame = filter_frame,
307 .config_props = config_props,
308 },
309 };
310
311 const AVFilter ff_vf_sab = {
312 .name = "sab",
313 .description = NULL_IF_CONFIG_SMALL("Apply shape adaptive blur."),
314 .priv_size = sizeof(SabContext),
315 .init = init,
316 .uninit = uninit,
317 FILTER_INPUTS(sab_inputs),
318 FILTER_OUTPUTS(ff_video_default_filterpad),
319 FILTER_PIXFMTS_ARRAY(pix_fmts),
320 .priv_class = &sab_class,
321 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
322 };
323