• 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 distance;
39     int thres;
40     int thres_y;
41     int thres_u;
42     int thres_v;
43     int sizew;
44     int sizeh;
45     int stepw;
46     int steph;
47     int depth;
48     int chroma_w;
49     int chroma_h;
50     int nb_planes;
51     int linesize[4];
52     int planeheight[4];
53     int planewidth[4];
54 
55     AVFrame *out;
56     int (*filter_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
57 } ChromaNRContext;
58 
59 static const enum AVPixelFormat pix_fmts[] = {
60     AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV444P,
61     AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P,
62     AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ411P,
63     AV_PIX_FMT_YUV420P9,   AV_PIX_FMT_YUV422P9,   AV_PIX_FMT_YUV444P9,
64     AV_PIX_FMT_YUV420P10,  AV_PIX_FMT_YUV422P10,  AV_PIX_FMT_YUV440P10, AV_PIX_FMT_YUV444P10,
65     AV_PIX_FMT_YUV444P12,  AV_PIX_FMT_YUV422P12,  AV_PIX_FMT_YUV440P12, AV_PIX_FMT_YUV420P12,
66     AV_PIX_FMT_YUV444P14,  AV_PIX_FMT_YUV422P14,  AV_PIX_FMT_YUV420P14,
67     AV_PIX_FMT_YUV420P16,  AV_PIX_FMT_YUV422P16,  AV_PIX_FMT_YUV444P16,
68     AV_PIX_FMT_YUVA420P9,  AV_PIX_FMT_YUVA422P9,  AV_PIX_FMT_YUVA444P9,
69     AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
70     AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12,
71     AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
72     AV_PIX_FMT_NONE
73 };
74 
75 #define MANHATTAN_DISTANCE(x, y, z) ((x) + (y) + (z))
76 #define EUCLIDEAN_DISTANCE(x, y, z) (sqrtf((x)*(x) + (y)*(y) + (z)*(z)))
77 
78 #define FILTER_FUNC(distance, name, ctype, type, fun)                                    \
79 static int distance ## _slice##name(AVFilterContext *ctx, void *arg,                     \
80                                     int jobnr, int nb_jobs)                              \
81 {                                                                                        \
82     ChromaNRContext *s = ctx->priv;                                                      \
83     AVFrame *in = arg;                                                                   \
84     AVFrame *out = s->out;                                                               \
85     const int in_ylinesize = in->linesize[0];                                            \
86     const int in_ulinesize = in->linesize[1];                                            \
87     const int in_vlinesize = in->linesize[2];                                            \
88     const int out_ulinesize = out->linesize[1];                                          \
89     const int out_vlinesize = out->linesize[2];                                          \
90     const int chroma_w = s->chroma_w;                                                    \
91     const int chroma_h = s->chroma_h;                                                    \
92     const int stepw = s->stepw;                                                          \
93     const int steph = s->steph;                                                          \
94     const int sizew = s->sizew;                                                          \
95     const int sizeh = s->sizeh;                                                          \
96     const int thres = s->thres;                                                          \
97     const int thres_y = s->thres_y;                                                      \
98     const int thres_u = s->thres_u;                                                      \
99     const int thres_v = s->thres_v;                                                      \
100     const int h = s->planeheight[1];                                                     \
101     const int w = s->planewidth[1];                                                      \
102     const int slice_start = (h * jobnr) / nb_jobs;                                       \
103     const int slice_end = (h * (jobnr+1)) / nb_jobs;                                     \
104     type *out_uptr = (type *)(out->data[1] + slice_start * out_ulinesize);               \
105     type *out_vptr = (type *)(out->data[2] + slice_start * out_vlinesize);               \
106                                                                                          \
107     {                                                                                    \
108         const int h = s->planeheight[0];                                                 \
109         const int slice_start = (h * jobnr) / nb_jobs;                                   \
110         const int slice_end = (h * (jobnr+1)) / nb_jobs;                                 \
111                                                                                          \
112         av_image_copy_plane(out->data[0] + slice_start * out->linesize[0],               \
113                             out->linesize[0],                                            \
114                             in->data[0] + slice_start * in->linesize[0],                 \
115                             in->linesize[0],                                             \
116                             s->linesize[0], slice_end - slice_start);                    \
117                                                                                          \
118         if (s->nb_planes == 4) {                                                         \
119             av_image_copy_plane(out->data[3] + slice_start * out->linesize[3],           \
120                                 out->linesize[3],                                        \
121                                 in->data[3] + slice_start * in->linesize[3],             \
122                                 in->linesize[3],                                         \
123                                 s->linesize[3], slice_end - slice_start);                \
124         }                                                                                \
125     }                                                                                    \
126                                                                                          \
127     for (int y = slice_start; y < slice_end; y++) {                                      \
128         const type *in_yptr = (const type *)(in->data[0] + y * chroma_h * in_ylinesize); \
129         const type *in_uptr = (const type *)(in->data[1] + y * in_ulinesize);            \
130         const type *in_vptr = (const type *)(in->data[2] + y * in_vlinesize);            \
131                                                                                          \
132         for (int x = 0; x < w; x++) {                                                    \
133             const int cy = in_yptr[x * chroma_w];                                        \
134             const int cu = in_uptr[x];                                                   \
135             const int cv = in_vptr[x];                                                   \
136             int su = cu;                                                                 \
137             int sv = cv;                                                                 \
138             int cn = 1;                                                                  \
139                                                                                          \
140             for (int yy = FFMAX(0, y - sizeh); yy <= FFMIN(y + sizeh, h - 1); yy += steph) {      \
141                 const type *in_yptr = (const type *)(in->data[0] + yy * chroma_h * in_ylinesize); \
142                 const type *in_uptr = (const type *)(in->data[1] + yy * in_ulinesize);            \
143                 const type *in_vptr = (const type *)(in->data[2] + yy * in_vlinesize);            \
144                                                                                                   \
145                 for (int xx = FFMAX(0, x - sizew); xx <= FFMIN(x + sizew, w - 1); xx += stepw) {  \
146                     const ctype Y = in_yptr[xx * chroma_w];                            \
147                     const ctype U = in_uptr[xx];                                       \
148                     const ctype V = in_vptr[xx];                                       \
149                     const ctype cyY = FFABS(cy - Y);                                   \
150                     const ctype cuU = FFABS(cu - U);                                   \
151                     const ctype cvV = FFABS(cv - V);                                   \
152                                                                                        \
153                     if (fun(cyY, cuU, cvV) < thres &&                                  \
154                         cuU < thres_u && cvV < thres_v &&                              \
155                         cyY < 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 >> 1)) / cn;                                       \
165             out_vptr[x] = (sv + (cn >> 1)) / 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(manhattan, 8,  int, uint8_t, MANHATTAN_DISTANCE)
176 FILTER_FUNC(manhattan, 16, int, uint16_t, MANHATTAN_DISTANCE)
177 
178 FILTER_FUNC(euclidean, 8,  int, uint8_t, EUCLIDEAN_DISTANCE)
179 FILTER_FUNC(euclidean, 16, int64_t, uint16_t, EUCLIDEAN_DISTANCE)
180 
filter_frame(AVFilterLink * inlink,AVFrame * in)181 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
182 {
183     AVFilterContext *ctx = inlink->dst;
184     AVFilterLink *outlink = ctx->outputs[0];
185     ChromaNRContext *s = ctx->priv;
186     AVFrame *out;
187 
188     switch (s->distance) {
189     case 0:
190         s->filter_slice = s->depth <= 8 ? manhattan_slice8 : manhattan_slice16;
191         break;
192     case 1:
193         s->filter_slice = s->depth <= 8 ? euclidean_slice8 : euclidean_slice16;
194         break;
195     }
196 
197     s->thres = s->threshold * (1 << (s->depth - 8));
198     s->thres_y = s->threshold_y * (1 << (s->depth - 8));
199     s->thres_u = s->threshold_u * (1 << (s->depth - 8));
200     s->thres_v = s->threshold_v * (1 << (s->depth - 8));
201 
202     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
203     if (!out) {
204         av_frame_free(&in);
205         return AVERROR(ENOMEM);
206     }
207 
208     av_frame_copy_props(out, in);
209     s->out = out;
210     ff_filter_execute(ctx, s->filter_slice, in, NULL,
211                       FFMIN3(s->planeheight[1],
212                              s->planeheight[2],
213                              ff_filter_get_nb_threads(ctx)));
214 
215     av_frame_free(&in);
216     return ff_filter_frame(outlink, out);
217 }
218 
config_input(AVFilterLink * inlink)219 static int config_input(AVFilterLink *inlink)
220 {
221     AVFilterContext *ctx = inlink->dst;
222     ChromaNRContext *s = ctx->priv;
223     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
224     int ret;
225 
226     s->nb_planes = desc->nb_components;
227     s->depth = desc->comp[0].depth;
228     s->chroma_w = 1 << desc->log2_chroma_w;
229     s->chroma_h = 1 << desc->log2_chroma_h;
230     s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
231     s->planeheight[0] = s->planeheight[3] = inlink->h;
232     s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
233     s->planewidth[0] = s->planewidth[3] = inlink->w;
234 
235     if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0)
236         return ret;
237 
238     return 0;
239 }
240 
241 #define OFFSET(x) offsetof(ChromaNRContext, x)
242 #define VF AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_RUNTIME_PARAM
243 
244 static const AVOption chromanr_options[] = {
245     { "thres", "set y+u+v threshold", OFFSET(threshold), AV_OPT_TYPE_FLOAT, {.dbl=30}, 1,   200, VF },
246     { "sizew", "set horizontal size", OFFSET(sizew),     AV_OPT_TYPE_INT,   {.i64=5},  1,   100, VF },
247     { "sizeh", "set vertical size",   OFFSET(sizeh),     AV_OPT_TYPE_INT,   {.i64=5},  1,   100, VF },
248     { "stepw", "set horizontal step", OFFSET(stepw),     AV_OPT_TYPE_INT,   {.i64=1},  1,    50, VF },
249     { "steph", "set vertical step",   OFFSET(steph),     AV_OPT_TYPE_INT,   {.i64=1},  1,    50, VF },
250     { "threy", "set y threshold",   OFFSET(threshold_y), AV_OPT_TYPE_FLOAT, {.dbl=200},1,   200, VF },
251     { "threu", "set u threshold",   OFFSET(threshold_u), AV_OPT_TYPE_FLOAT, {.dbl=200},1,   200, VF },
252     { "threv", "set v threshold",   OFFSET(threshold_v), AV_OPT_TYPE_FLOAT, {.dbl=200},1,   200, VF },
253     { "distance", "set distance type", OFFSET(distance), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, VF, "distance" },
254     {   "manhattan", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, VF, "distance" },
255     {   "euclidean", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, VF, "distance" },
256     { NULL }
257 };
258 
259 static const AVFilterPad inputs[] = {
260     {
261         .name         = "default",
262         .type         = AVMEDIA_TYPE_VIDEO,
263         .filter_frame = filter_frame,
264         .config_props = config_input,
265     },
266 };
267 
268 static const AVFilterPad outputs[] = {
269     {
270         .name = "default",
271         .type = AVMEDIA_TYPE_VIDEO,
272     },
273 };
274 
275 AVFILTER_DEFINE_CLASS(chromanr);
276 
277 const AVFilter ff_vf_chromanr = {
278     .name          = "chromanr",
279     .description   = NULL_IF_CONFIG_SMALL("Reduce chrominance noise."),
280     .priv_size     = sizeof(ChromaNRContext),
281     .priv_class    = &chromanr_class,
282     FILTER_OUTPUTS(outputs),
283     FILTER_INPUTS(inputs),
284     FILTER_PIXFMTS_ARRAY(pix_fmts),
285     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
286     .process_command = ff_filter_process_command,
287 };
288