• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2020 Paul B Mahol
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include "libavutil/avstring.h"
22 #include "libavutil/imgutils.h"
23 #include "libavutil/opt.h"
24 #include "libavutil/pixdesc.h"
25 
26 #include "avfilter.h"
27 #include "formats.h"
28 #include "internal.h"
29 #include "video.h"
30 
31 typedef struct ChromaNRContext {
32     const AVClass *class;
33 
34     float threshold;
35     float threshold_y;
36     float threshold_u;
37     float threshold_v;
38     int thres;
39     int thres_y;
40     int thres_u;
41     int thres_v;
42     int sizew;
43     int sizeh;
44     int stepw;
45     int steph;
46     int depth;
47     int chroma_w;
48     int chroma_h;
49     int nb_planes;
50     int linesize[4];
51     int planeheight[4];
52     int planewidth[4];
53 
54     AVFrame *out;
55     int (*filter_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
56 } ChromaNRContext;
57 
query_formats(AVFilterContext * ctx)58 static int query_formats(AVFilterContext *ctx)
59 {
60     static const enum AVPixelFormat pix_fmts[] = {
61         AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV444P,
62         AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P,
63         AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ411P,
64         AV_PIX_FMT_YUV420P9,   AV_PIX_FMT_YUV422P9,   AV_PIX_FMT_YUV444P9,
65         AV_PIX_FMT_YUV420P10,  AV_PIX_FMT_YUV422P10,  AV_PIX_FMT_YUV440P10, AV_PIX_FMT_YUV444P10,
66         AV_PIX_FMT_YUV444P12,  AV_PIX_FMT_YUV422P12,  AV_PIX_FMT_YUV440P12, AV_PIX_FMT_YUV420P12,
67         AV_PIX_FMT_YUV444P14,  AV_PIX_FMT_YUV422P14,  AV_PIX_FMT_YUV420P14,
68         AV_PIX_FMT_YUV420P16,  AV_PIX_FMT_YUV422P16,  AV_PIX_FMT_YUV444P16,
69         AV_PIX_FMT_YUVA420P9,  AV_PIX_FMT_YUVA422P9,  AV_PIX_FMT_YUVA444P9,
70         AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
71         AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12,
72         AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
73         AV_PIX_FMT_NONE
74     };
75 
76     AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
77     if (!fmts_list)
78         return AVERROR(ENOMEM);
79     return ff_set_common_formats(ctx, fmts_list);
80 }
81 
82 #define FILTER_FUNC(name, type)                                                        \
83 static int filter_slice##name(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)   \
84 {                                                                                        \
85     ChromaNRContext *s = ctx->priv;                                                      \
86     AVFrame *in = arg;                                                                   \
87     AVFrame *out = s->out;                                                               \
88     const int in_ylinesize = in->linesize[0];                                            \
89     const int in_ulinesize = in->linesize[1];                                            \
90     const int in_vlinesize = in->linesize[2];                                            \
91     const int out_ulinesize = out->linesize[1];                                          \
92     const int out_vlinesize = out->linesize[2];                                          \
93     const int chroma_w = s->chroma_w;                                                    \
94     const int chroma_h = s->chroma_h;                                                    \
95     const int stepw = s->stepw;                                                          \
96     const int steph = s->steph;                                                          \
97     const int sizew = s->sizew;                                                          \
98     const int sizeh = s->sizeh;                                                          \
99     const int thres = s->thres;                                                          \
100     const int thres_y = s->thres_y;                                                      \
101     const int thres_u = s->thres_u;                                                      \
102     const int thres_v = s->thres_v;                                                      \
103     const int h = s->planeheight[1];                                                     \
104     const int w = s->planewidth[1];                                                      \
105     const int slice_start = (h * jobnr) / nb_jobs;                                       \
106     const int slice_end = (h * (jobnr+1)) / nb_jobs;                                     \
107     type *out_uptr = (type *)(out->data[1] + slice_start * out_ulinesize);               \
108     type *out_vptr = (type *)(out->data[2] + slice_start * out_vlinesize);               \
109                                                                                          \
110     {                                                                                    \
111         const int h = s->planeheight[0];                                                 \
112         const int slice_start = (h * jobnr) / nb_jobs;                                   \
113         const int slice_end = (h * (jobnr+1)) / nb_jobs;                                 \
114                                                                                          \
115         av_image_copy_plane(out->data[0] + slice_start * out->linesize[0],               \
116                             out->linesize[0],                                            \
117                             in->data[0] + slice_start * in->linesize[0],                 \
118                             in->linesize[0],                                             \
119                             s->linesize[0], slice_end - slice_start);                    \
120                                                                                          \
121         if (s->nb_planes == 4) {                                                         \
122             av_image_copy_plane(out->data[3] + slice_start * out->linesize[3],           \
123                                 out->linesize[3],                                        \
124                                 in->data[3] + slice_start * in->linesize[3],             \
125                                 in->linesize[3],                                         \
126                                 s->linesize[3], slice_end - slice_start);                \
127         }                                                                                \
128     }                                                                                    \
129                                                                                          \
130     for (int y = slice_start; y < slice_end; y++) {                                      \
131         const type *in_yptr = (const type *)(in->data[0] + y * chroma_h * in_ylinesize); \
132         const type *in_uptr = (const type *)(in->data[1] + y * in_ulinesize);            \
133         const type *in_vptr = (const type *)(in->data[2] + y * in_vlinesize);            \
134                                                                                          \
135         for (int x = 0; x < w; x++) {                                                    \
136             const int cy = in_yptr[x * chroma_w];                                        \
137             const int cu = in_uptr[x];                                                   \
138             const int cv = in_vptr[x];                                                   \
139             int su = cu;                                                                 \
140             int sv = cv;                                                                 \
141             int cn = 1;                                                                  \
142                                                                                          \
143             for (int yy = FFMAX(0, y - sizeh); yy < FFMIN(y + sizeh, h); yy += steph) {           \
144                 const type *in_yptr = (const type *)(in->data[0] + yy * chroma_h * in_ylinesize); \
145                 const type *in_uptr = (const type *)(in->data[1] + yy * in_ulinesize);            \
146                 const type *in_vptr = (const type *)(in->data[2] + yy * in_vlinesize);            \
147                                                                                                   \
148                 for (int xx = FFMAX(0, x - sizew); xx < FFMIN(x + sizew, w); xx += stepw) {       \
149                     const int Y = in_yptr[xx * chroma_w];                              \
150                     const int U = in_uptr[xx];                                         \
151                     const int V = in_vptr[xx];                                         \
152                                                                                        \
153                     if (FFABS(cu - U) + FFABS(cv - V) + FFABS(cy - Y) < thres &&       \
154                         FFABS(cu - U) < thres_u && FFABS(cv - V) < thres_v &&          \
155                         FFABS(cy - Y) < thres_y &&                                     \
156                         xx != x && yy != y) {                                          \
157                         su += U;                                                       \
158                         sv += V;                                                       \
159                         cn++;                                                          \
160                     }                                                                  \
161                 }                                                                      \
162             }                                                                          \
163                                                                                        \
164             out_uptr[x] = su / cn;                                                     \
165             out_vptr[x] = sv / cn;                                                     \
166         }                                                                              \
167                                                                                        \
168         out_uptr += out_ulinesize / sizeof(type);                                      \
169         out_vptr += out_vlinesize / sizeof(type);                                      \
170     }                                                                                  \
171                                                                                        \
172     return 0;                                                                          \
173 }
174 
175 FILTER_FUNC(8,  uint8_t)
176 FILTER_FUNC(16, uint16_t)
177 
filter_frame(AVFilterLink * inlink,AVFrame * in)178 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
179 {
180     AVFilterContext *ctx = inlink->dst;
181     AVFilterLink *outlink = ctx->outputs[0];
182     ChromaNRContext *s = ctx->priv;
183     AVFrame *out;
184 
185     s->thres = s->threshold * (1 << (s->depth - 8));
186     s->thres_y = s->threshold_y * (1 << (s->depth - 8));
187     s->thres_u = s->threshold_u * (1 << (s->depth - 8));
188     s->thres_v = s->threshold_v * (1 << (s->depth - 8));
189 
190     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
191     if (!out) {
192         av_frame_free(&in);
193         return AVERROR(ENOMEM);
194     }
195 
196     av_frame_copy_props(out, in);
197     s->out = out;
198     ctx->internal->execute(ctx, s->filter_slice, in, NULL,
199                            FFMIN3(s->planeheight[1],
200                                   s->planeheight[2],
201                                   ff_filter_get_nb_threads(ctx)));
202 
203     av_frame_free(&in);
204     return ff_filter_frame(outlink, out);
205 }
206 
config_input(AVFilterLink * inlink)207 static int config_input(AVFilterLink *inlink)
208 {
209     AVFilterContext *ctx = inlink->dst;
210     ChromaNRContext *s = ctx->priv;
211     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
212     int ret;
213 
214     s->nb_planes = desc->nb_components;
215     s->depth = desc->comp[0].depth;
216     s->filter_slice = s->depth <= 8 ? filter_slice8 : filter_slice16;
217     s->chroma_w = 1 << desc->log2_chroma_w;
218     s->chroma_h = 1 << desc->log2_chroma_h;
219     s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
220     s->planeheight[0] = s->planeheight[3] = inlink->h;
221     s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
222     s->planewidth[0] = s->planewidth[3] = inlink->w;
223 
224     if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0)
225         return ret;
226 
227     return 0;
228 }
229 
230 #define OFFSET(x) offsetof(ChromaNRContext, x)
231 #define VF AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_RUNTIME_PARAM
232 
233 static const AVOption chromanr_options[] = {
234     { "thres", "set y+u+v threshold", OFFSET(threshold), AV_OPT_TYPE_FLOAT, {.dbl=30}, 1, 200, VF },
235     { "sizew", "set horizontal size", OFFSET(sizew),     AV_OPT_TYPE_INT,   {.i64=5},  1, 100, VF },
236     { "sizeh", "set vertical size",   OFFSET(sizeh),     AV_OPT_TYPE_INT,   {.i64=5},  1, 100, VF },
237     { "stepw", "set horizontal step", OFFSET(stepw),     AV_OPT_TYPE_INT,   {.i64=1},  1,  50, VF },
238     { "steph", "set vertical step",   OFFSET(steph),     AV_OPT_TYPE_INT,   {.i64=1},  1,  50, VF },
239     { "threy", "set y threshold",   OFFSET(threshold_y), AV_OPT_TYPE_FLOAT, {.dbl=200},1, 200, VF },
240     { "threu", "set u threshold",   OFFSET(threshold_u), AV_OPT_TYPE_FLOAT, {.dbl=200},1, 200, VF },
241     { "threv", "set v threshold",   OFFSET(threshold_v), AV_OPT_TYPE_FLOAT, {.dbl=200},1, 200, VF },
242     { NULL }
243 };
244 
245 static const AVFilterPad inputs[] = {
246     {
247         .name         = "default",
248         .type         = AVMEDIA_TYPE_VIDEO,
249         .filter_frame = filter_frame,
250         .config_props = config_input,
251     },
252     { NULL }
253 };
254 
255 static const AVFilterPad outputs[] = {
256     {
257         .name = "default",
258         .type = AVMEDIA_TYPE_VIDEO,
259     },
260     { NULL }
261 };
262 
263 AVFILTER_DEFINE_CLASS(chromanr);
264 
265 AVFilter ff_vf_chromanr = {
266     .name          = "chromanr",
267     .description   = NULL_IF_CONFIG_SMALL("Reduce chrominance noise."),
268     .priv_size     = sizeof(ChromaNRContext),
269     .priv_class    = &chromanr_class,
270     .query_formats = query_formats,
271     .outputs       = outputs,
272     .inputs        = inputs,
273     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
274     .process_command = ff_filter_process_command,
275 };
276