]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_bilateral.c
avfilter: add bilateral filter
[ffmpeg] / libavfilter / vf_bilateral.c
1 /*
2  * Copyright (c) 2017 Ming Yang
3  * Copyright (c) 2019 Paul B Mahol
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to deal
7  * in the Software without restriction, including without limitation the rights
8  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9  * copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23
24 #include "libavutil/imgutils.h"
25 #include "libavutil/opt.h"
26 #include "libavutil/pixdesc.h"
27 #include "avfilter.h"
28 #include "formats.h"
29 #include "internal.h"
30 #include "video.h"
31
32 typedef struct BilateralContext {
33     const AVClass *class;
34
35     float sigmaS;
36     float sigmaR;
37     int planes;
38
39     int nb_planes;
40     int depth;
41     int planewidth[4];
42     int planeheight[4];
43
44     float range_table[65536];
45
46     float *img_out_f;
47     float *img_temp;
48     float *map_factor_a;
49     float *map_factor_b;
50     float *slice_factor_a;
51     float *slice_factor_b;
52     float *line_factor_a;
53     float *line_factor_b;
54 } BilateralContext;
55
56 #define OFFSET(x) offsetof(BilateralContext, x)
57 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
58
59 static const AVOption bilateral_options[] = {
60     { "sigmaS", "set spatial sigma",    OFFSET(sigmaS), AV_OPT_TYPE_FLOAT, {.dbl=0.1}, 0.0,  10, FLAGS },
61     { "sigmaR", "set sigma range",      OFFSET(sigmaR), AV_OPT_TYPE_FLOAT, {.dbl=0.1}, 0.0,   1, FLAGS },
62     { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT,   {.i64=1},     0, 0xF, FLAGS },
63     { NULL }
64 };
65
66 AVFILTER_DEFINE_CLASS(bilateral);
67
68 typedef struct ThreadData {
69     int height;
70     int width;
71 } ThreadData;
72
73 static int query_formats(AVFilterContext *ctx)
74 {
75     static const enum AVPixelFormat pix_fmts[] = {
76         AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P,
77         AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
78         AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P,
79         AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
80         AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
81         AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
82         AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
83         AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12,
84         AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14,
85         AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
86         AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
87         AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
88         AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
89         AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
90         AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
91         AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
92         AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
93         AV_PIX_FMT_NONE
94     };
95
96     return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
97 }
98
99 static int config_input(AVFilterLink *inlink)
100 {
101     BilateralContext *s = inlink->dst->priv;
102     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
103     float inv_sigma_range;
104
105     s->depth = desc->comp[0].depth;
106     inv_sigma_range = 1.0f / (s->sigmaR * ((1 << s->depth) - 1));
107
108     //compute a lookup table
109     for (int i = 0; i < (1 << s->depth); i++)
110         s->range_table[i] = expf(-i * inv_sigma_range);
111
112     s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
113     s->planewidth[0] = s->planewidth[3] = inlink->w;
114     s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
115     s->planeheight[0] = s->planeheight[3] = inlink->h;
116
117     s->nb_planes = av_pix_fmt_count_planes(inlink->format);
118
119     s->img_out_f = av_calloc(inlink->w * inlink->h, sizeof(float));
120     s->img_temp = av_calloc(inlink->w * inlink->h, sizeof(float));
121     s->map_factor_a = av_calloc(inlink->w * inlink->h, sizeof(float));
122     s->map_factor_b = av_calloc(inlink->w * inlink->h, sizeof(float));
123     s->slice_factor_a = av_calloc(inlink->w, sizeof(float));
124     s->slice_factor_b = av_calloc(inlink->w, sizeof(float));
125     s->line_factor_a = av_calloc(inlink->w, sizeof(float));
126     s->line_factor_b = av_calloc(inlink->w, sizeof(float));
127
128     if (!s->img_out_f ||
129         !s->img_temp ||
130         !s->map_factor_a ||
131         !s->map_factor_b ||
132         !s->slice_factor_a ||
133         !s->slice_factor_a ||
134         !s->line_factor_a ||
135         !s->line_factor_a)
136         return AVERROR(ENOMEM);
137
138     return 0;
139 }
140
141 #define BILATERAL(type, name)                                                           \
142 static void bilateral_##name(BilateralContext *s, const uint8_t *ssrc, uint8_t *ddst,   \
143                              float sigma_spatial, float sigma_range,                    \
144                              int width, int height, int src_linesize, int dst_linesize) \
145 {                                                                                       \
146     type *dst = (type *)ddst;                                                           \
147     const type *src = (const type *)ssrc;                                               \
148     float *img_out_f = s->img_out_f, *img_temp = s->img_temp;                           \
149     float *map_factor_a = s->map_factor_a, *map_factor_b = s->map_factor_b;             \
150     float *slice_factor_a = s->slice_factor_a, *slice_factor_b = s->slice_factor_b;     \
151     float *line_factor_a = s->line_factor_a, *line_factor_b = s->line_factor_b;         \
152     float *range_table = s->range_table;                                                \
153     float alpha = expf(-sqrtf(2.f) / (sigma_spatial * width));                          \
154     float ypr, ycr, *ycy, *ypy, *xcy, fp, fc;                                           \
155     float inv_alpha_ = 1 - alpha;                                                       \
156     float *ycf, *ypf, *xcf, *in_factor;                                                 \
157     const type *tcy, *tpy;                                                                \
158     int h1;                                                                               \
159                                                                                           \
160     for (int y = 0; y < height; y++) {                                                    \
161         float *temp_factor_x, *temp_x = &img_temp[y * width];                             \
162         const type *in_x = &src[y * src_linesize];                                        \
163         const type *texture_x = &src[y * src_linesize];                                   \
164         type tpr;                                                                         \
165                                                                                           \
166         *temp_x++ = ypr = *in_x++;                                                        \
167         tpr = *texture_x++;                                                               \
168                                                                                           \
169         temp_factor_x = &map_factor_a[y * width];                                         \
170         *temp_factor_x++ = fp = 1;                                                        \
171                                                                                           \
172         for (int x = 1; x < width; x++) {                                                 \
173             float weight, alpha_;                                                         \
174             int range_dist;                                                               \
175             type tcr = *texture_x++;                                                      \
176             type dr = abs(tcr - tpr);                                                     \
177                                                                                           \
178             range_dist = dr;                                                              \
179             weight = range_table[range_dist];                                             \
180             alpha_ = weight*alpha;                                                        \
181             *temp_x++ = ycr = inv_alpha_*(*in_x++) + alpha_*ypr;                          \
182             tpr = tcr;                                                                    \
183             ypr = ycr;                                                                    \
184             *temp_factor_x++ = fc = inv_alpha_ + alpha_ * fp;                             \
185             fp = fc;                                                                      \
186         }                                                                                 \
187         --temp_x; *temp_x = 0.5f*((*temp_x) + (*--in_x));                                 \
188         tpr = *--texture_x;                                                               \
189         ypr = *in_x;                                                                      \
190                                                                                           \
191         --temp_factor_x; *temp_factor_x = 0.5f*((*temp_factor_x) + 1);                    \
192         fp = 1;                                                                           \
193                                                                                           \
194         for (int x = width - 2; x >= 0; x--) {                                            \
195             type tcr = *--texture_x;                                                      \
196             type dr = abs(tcr - tpr);                                                     \
197             int range_dist = dr;                                                          \
198             float weight = range_table[range_dist];                                       \
199             float alpha_ = weight * alpha;                                                \
200                                                                                           \
201             ycr = inv_alpha_ * (*--in_x) + alpha_ * ypr;                                  \
202             --temp_x; *temp_x = 0.5f*((*temp_x) + ycr);                                   \
203             tpr = tcr;                                                                    \
204             ypr = ycr;                                                                    \
205                                                                                           \
206             fc = inv_alpha_ + alpha_*fp;                                                  \
207             --temp_factor_x;                                                              \
208             *temp_factor_x = 0.5f*((*temp_factor_x) + fc);                                \
209             fp = fc;                                                                      \
210         }                                                                                 \
211     }                                                                                     \
212     memcpy(img_out_f, img_temp, sizeof(float) * width);                                   \
213                                                                                           \
214     alpha = expf(-sqrtf(2.f) / (sigma_spatial * height));                                 \
215     inv_alpha_ = 1 - alpha;                                                               \
216     in_factor = map_factor_a;                                                             \
217     memcpy(map_factor_b, in_factor, sizeof(float) * width);                               \
218     for (int y = 1; y < height; y++) {                                                    \
219         tpy = &src[(y - 1) * src_linesize];                                               \
220         tcy = &src[y * src_linesize];                                                     \
221         xcy = &img_temp[y * width];                                                       \
222         ypy = &img_out_f[(y - 1) * width];                                                \
223         ycy = &img_out_f[y * width];                                                      \
224                                                                                           \
225         xcf = &in_factor[y * width];                                                      \
226         ypf = &map_factor_b[(y - 1) * width];                                             \
227         ycf = &map_factor_b[y * width];                                                   \
228         for (int x = 0; x < width; x++) {                                                 \
229             type dr = abs((*tcy++) - (*tpy++));                                           \
230             int range_dist = dr;                                                          \
231             float weight = range_table[range_dist];                                       \
232             float alpha_ = weight*alpha;                                                  \
233                                                                                           \
234             *ycy++ = inv_alpha_*(*xcy++) + alpha_*(*ypy++);                               \
235             *ycf++ = inv_alpha_*(*xcf++) + alpha_*(*ypf++);                               \
236         }                                                                                 \
237     }                                                                                     \
238     h1 = height - 1;                                                                      \
239     ycf = line_factor_a;                                                                  \
240     ypf = line_factor_b;                                                                  \
241     memcpy(ypf, &in_factor[h1 * width], sizeof(float) * width);                           \
242     for (int x = 0; x < width; x++)                                                       \
243         map_factor_b[h1 * width + x] = 0.5f*(map_factor_b[h1 * width + x] + ypf[x]);      \
244                                                                                           \
245     ycy = slice_factor_a;                                                                 \
246     ypy = slice_factor_b;                                                                 \
247     memcpy(ypy, &img_temp[h1 * width], sizeof(float) * width);                            \
248     for (int x = 0, k = 0; x < width; x++) {                                              \
249         int idx = h1 * width + x;                                                         \
250         img_out_f[idx] = 0.5f*(img_out_f[idx] + ypy[k++]) / map_factor_b[h1 * width + x]; \
251     }                                                                                     \
252                                                                                           \
253     for (int y = h1 - 1; y >= 0; y--) {                                                   \
254         float *ycf_, *ypf_, *factor_;                                                     \
255         float *ycy_, *ypy_, *out_;                                                        \
256                                                                                           \
257         tpy = &src[(y + 1) * src_linesize];                                               \
258         tcy = &src[y * src_linesize];                                                     \
259         xcy = &img_temp[y * width];                                                       \
260         ycy_ = ycy;                                                                       \
261         ypy_ = ypy;                                                                       \
262         out_ = &img_out_f[y * width];                                                     \
263                                                                                           \
264         xcf = &in_factor[y * width];                                                      \
265         ycf_ = ycf;                                                                       \
266         ypf_ = ypf;                                                                       \
267         factor_ = &map_factor_b[y * width];                                               \
268         for (int x = 0; x < width; x++) {                                                 \
269             type dr = abs((*tcy++) - (*tpy++));                                           \
270             int range_dist = dr;                                                          \
271             float weight = range_table[range_dist];                                       \
272             float alpha_ = weight*alpha;                                                  \
273             float ycc, fcc = inv_alpha_*(*xcf++) + alpha_*(*ypf_++);                      \
274                                                                                           \
275             *ycf_++ = fcc;                                                                \
276             *factor_ = 0.5f * (*factor_ + fcc);                                           \
277                                                                                           \
278             ycc = inv_alpha_*(*xcy++) + alpha_*(*ypy_++);                                 \
279             *ycy_++ = ycc;                                                                \
280             *out_ = 0.5f * (*out_ + ycc) / (*factor_);                                    \
281             out_++;                                                                       \
282             factor_++;                                                                    \
283         }                                                                                 \
284                                                                                           \
285         memcpy(ypy, ycy, sizeof(float) * width);                                          \
286         memcpy(ypf, ycf, sizeof(float) * width);                                          \
287     }                                                                                     \
288                                                                                           \
289     for (int i = 0; i < height; i++)                                                      \
290         for (int j = 0; j < width; j++)                                                   \
291             dst[j + i * dst_linesize] = img_out_f[i * width + j];                         \
292 }
293
294 BILATERAL(uint8_t, byte)
295 BILATERAL(uint16_t, word)
296
297 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
298 {
299     AVFilterContext *ctx = inlink->dst;
300     BilateralContext *s = ctx->priv;
301     AVFilterLink *outlink = ctx->outputs[0];
302     AVFrame *out;
303
304     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
305     if (!out) {
306         av_frame_free(&in);
307         return AVERROR(ENOMEM);
308     }
309     av_frame_copy_props(out, in);
310
311     for (int plane = 0; plane < s->nb_planes; plane++) {
312         if (!(s->planes & (1 << plane))) {
313             av_image_copy_plane(out->data[plane], out->linesize[plane],
314                                 in->data[plane], in->linesize[plane],
315                                 s->planewidth[plane] * ((s->depth + 7) / 8), s->planeheight[plane]);
316             continue;
317         }
318
319         if (s->depth <= 8)
320            bilateral_byte(s, in->data[plane], out->data[plane], s->sigmaS, s->sigmaR,
321                       s->planewidth[plane], s->planeheight[plane],
322                       in->linesize[plane], out->linesize[plane]);
323         else
324            bilateral_word(s, in->data[plane], out->data[plane], s->sigmaS, s->sigmaR,
325                       s->planewidth[plane], s->planeheight[plane],
326                       in->linesize[plane] / 2, out->linesize[plane] / 2);
327     }
328
329     av_frame_free(&in);
330     return ff_filter_frame(outlink, out);
331 }
332
333 static av_cold void uninit(AVFilterContext *ctx)
334 {
335     BilateralContext *s = ctx->priv;
336
337     av_freep(&s->img_out_f);
338     av_freep(&s->img_temp);
339     av_freep(&s->map_factor_a);
340     av_freep(&s->map_factor_b);
341     av_freep(&s->slice_factor_a);
342     av_freep(&s->slice_factor_b);
343     av_freep(&s->line_factor_a);
344     av_freep(&s->line_factor_b);
345 }
346
347 static const AVFilterPad bilateral_inputs[] = {
348     {
349         .name         = "default",
350         .type         = AVMEDIA_TYPE_VIDEO,
351         .config_props = config_input,
352         .filter_frame = filter_frame,
353     },
354     { NULL }
355 };
356
357 static const AVFilterPad bilateral_outputs[] = {
358     {
359         .name = "default",
360         .type = AVMEDIA_TYPE_VIDEO,
361     },
362     { NULL }
363 };
364
365 AVFilter ff_vf_bilateral = {
366     .name          = "bilateral",
367     .description   = NULL_IF_CONFIG_SMALL("Apply Bilateral filter."),
368     .priv_size     = sizeof(BilateralContext),
369     .priv_class    = &bilateral_class,
370     .uninit        = uninit,
371     .query_formats = query_formats,
372     .inputs        = bilateral_inputs,
373     .outputs       = bilateral_outputs,
374     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
375 };