2 * Copyright (c) 2010 Nolan Lum <nol888@gmail.com>
3 * Copyright (c) 2009 Loren Merritt <lorenm@u.washington.edu>
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 * gradfun debanding filter, ported from MPlayer
25 * libmpcodecs/vf_gradfun.c
27 * Apply a boxblur debanding algorithm (based on the gradfun2db
28 * AviSynth filter by prunedtree).
29 * Foreach pixel, if it's within threshold of the blurred value, make it closer.
30 * So now we have a smoothed and higher bitdepth version of all the shallow
31 * gradients, while leaving detailed areas untouched.
32 * Dither it back to 8bit.
35 #include "libavutil/imgutils.h"
36 #include "libavutil/common.h"
37 #include "libavutil/cpu.h"
38 #include "libavutil/opt.h"
39 #include "libavutil/pixdesc.h"
46 DECLARE_ALIGNED(16, static const uint16_t, dither
)[8][8] = {
47 {0x00,0x60,0x18,0x78,0x06,0x66,0x1E,0x7E},
48 {0x40,0x20,0x58,0x38,0x46,0x26,0x5E,0x3E},
49 {0x10,0x70,0x08,0x68,0x16,0x76,0x0E,0x6E},
50 {0x50,0x30,0x48,0x28,0x56,0x36,0x4E,0x2E},
51 {0x04,0x64,0x1C,0x7C,0x02,0x62,0x1A,0x7A},
52 {0x44,0x24,0x5C,0x3C,0x42,0x22,0x5A,0x3A},
53 {0x14,0x74,0x0C,0x6C,0x12,0x72,0x0A,0x6A},
54 {0x54,0x34,0x4C,0x2C,0x52,0x32,0x4A,0x2A},
57 void ff_gradfun_filter_line_c(uint8_t *dst
, const uint8_t *src
, const uint16_t *dc
, int width
, int thresh
, const uint16_t *dithers
)
60 for (x
= 0; x
< width
; dc
+= x
& 1, x
++) {
61 int pix
= src
[x
] << 7;
62 int delta
= dc
[0] - pix
;
63 int m
= abs(delta
) * thresh
>> 16;
64 m
= FFMAX(0, 127 - m
);
65 m
= m
* m
* delta
>> 14;
66 pix
+= m
+ dithers
[x
& 7];
67 dst
[x
] = av_clip_uint8(pix
>> 7);
71 void ff_gradfun_blur_line_c(uint16_t *dc
, uint16_t *buf
, const uint16_t *buf1
, const uint8_t *src
, int src_linesize
, int width
)
74 for (x
= 0; x
< width
; x
++) {
75 v
= buf1
[x
] + src
[2 * x
] + src
[2 * x
+ 1] + src
[2 * x
+ src_linesize
] + src
[2 * x
+ 1 + src_linesize
];
82 static void filter(GradFunContext
*ctx
, uint8_t *dst
, const uint8_t *src
, int width
, int height
, int dst_linesize
, int src_linesize
, int r
)
84 int bstride
= FFALIGN(width
, 16) / 2;
86 uint32_t dc_factor
= (1 << 21) / (r
* r
);
87 uint16_t *dc
= ctx
->buf
+ 16;
88 uint16_t *buf
= ctx
->buf
+ bstride
+ 32;
89 int thresh
= ctx
->thresh
;
91 memset(dc
, 0, (bstride
+ 16) * sizeof(*buf
));
92 for (y
= 0; y
< r
; y
++)
93 ctx
->blur_line(dc
, buf
+ y
* bstride
, buf
+ (y
- 1) * bstride
, src
+ 2 * y
* src_linesize
, src_linesize
, width
/ 2);
96 int mod
= ((y
+ r
) / 2) % r
;
97 uint16_t *buf0
= buf
+ mod
* bstride
;
98 uint16_t *buf1
= buf
+ (mod
? mod
- 1 : r
- 1) * bstride
;
100 ctx
->blur_line(dc
, buf0
, buf1
, src
+ (y
+ r
) * src_linesize
, src_linesize
, width
/ 2);
101 for (x
= v
= 0; x
< r
; x
++)
103 for (; x
< width
/ 2; x
++) {
104 v
+= dc
[x
] - dc
[x
-r
];
105 dc
[x
-r
] = v
* dc_factor
>> 16;
107 for (; x
< (width
+ r
+ 1) / 2; x
++)
108 dc
[x
-r
] = v
* dc_factor
>> 16;
109 for (x
= -r
/ 2; x
< 0; x
++)
113 for (y
= 0; y
< r
; y
++)
114 ctx
->filter_line(dst
+ y
* dst_linesize
, src
+ y
* src_linesize
, dc
- r
/ 2, width
, thresh
, dither
[y
& 7]);
116 ctx
->filter_line(dst
+ y
* dst_linesize
, src
+ y
* src_linesize
, dc
- r
/ 2, width
, thresh
, dither
[y
& 7]);
117 if (++y
>= height
) break;
118 ctx
->filter_line(dst
+ y
* dst_linesize
, src
+ y
* src_linesize
, dc
- r
/ 2, width
, thresh
, dither
[y
& 7]);
119 if (++y
>= height
) break;
124 static av_cold
int init(AVFilterContext
*ctx
)
126 GradFunContext
*s
= ctx
->priv
;
128 s
->thresh
= (1 << 15) / s
->strength
;
129 s
->radius
= av_clip((s
->radius
+ 1) & ~1, 4, 32);
131 s
->blur_line
= ff_gradfun_blur_line_c
;
132 s
->filter_line
= ff_gradfun_filter_line_c
;
135 ff_gradfun_init_x86(s
);
137 av_log(ctx
, AV_LOG_VERBOSE
, "threshold:%.2f radius:%d\n", s
->strength
, s
->radius
);
142 static av_cold
void uninit(AVFilterContext
*ctx
)
144 GradFunContext
*s
= ctx
->priv
;
148 static int query_formats(AVFilterContext
*ctx
)
150 static const enum AVPixelFormat pix_fmts
[] = {
151 AV_PIX_FMT_YUV410P
, AV_PIX_FMT_YUV420P
,
152 AV_PIX_FMT_GRAY8
, AV_PIX_FMT_YUV444P
,
153 AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUV411P
,
159 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
164 static int config_input(AVFilterLink
*inlink
)
166 GradFunContext
*s
= inlink
->dst
->priv
;
167 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
168 int hsub
= desc
->log2_chroma_w
;
169 int vsub
= desc
->log2_chroma_h
;
172 s
->buf
= av_calloc((FFALIGN(inlink
->w
, 16) * (s
->radius
+ 1) / 2 + 32), sizeof(*s
->buf
));
174 return AVERROR(ENOMEM
);
176 s
->chroma_w
= FF_CEIL_RSHIFT(inlink
->w
, hsub
);
177 s
->chroma_h
= FF_CEIL_RSHIFT(inlink
->h
, vsub
);
178 s
->chroma_r
= av_clip(((((s
->radius
>> hsub
) + (s
->radius
>> vsub
)) / 2 ) + 1) & ~1, 4, 32);
183 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
185 GradFunContext
*s
= inlink
->dst
->priv
;
186 AVFilterLink
*outlink
= inlink
->dst
->outputs
[0];
190 if (av_frame_is_writable(in
)) {
195 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
198 return AVERROR(ENOMEM
);
200 av_frame_copy_props(out
, in
);
203 for (p
= 0; p
< 4 && in
->data
[p
] && in
->linesize
[p
]; p
++) {
213 if (FFMIN(w
, h
) > 2 * r
)
214 filter(s
, out
->data
[p
], in
->data
[p
], w
, h
, out
->linesize
[p
], in
->linesize
[p
], r
);
215 else if (out
->data
[p
] != in
->data
[p
])
216 av_image_copy_plane(out
->data
[p
], out
->linesize
[p
], in
->data
[p
], in
->linesize
[p
], w
, h
);
222 return ff_filter_frame(outlink
, out
);
225 #define OFFSET(x) offsetof(GradFunContext, x)
226 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
228 static const AVOption gradfun_options
[] = {
229 { "strength", "The maximum amount by which the filter will change any one pixel.", OFFSET(strength
), AV_OPT_TYPE_FLOAT
, { .dbl
= 1.2 }, 0.51, 64, FLAGS
},
230 { "radius", "The neighborhood to fit the gradient to.", OFFSET(radius
), AV_OPT_TYPE_INT
, { .i64
= 16 }, 4, 32, FLAGS
},
234 AVFILTER_DEFINE_CLASS(gradfun
);
236 static const AVFilterPad avfilter_vf_gradfun_inputs
[] = {
239 .type
= AVMEDIA_TYPE_VIDEO
,
240 .config_props
= config_input
,
241 .filter_frame
= filter_frame
,
246 static const AVFilterPad avfilter_vf_gradfun_outputs
[] = {
249 .type
= AVMEDIA_TYPE_VIDEO
,
254 AVFilter ff_vf_gradfun
= {
256 .description
= NULL_IF_CONFIG_SMALL("Debands video quickly using gradients."),
257 .priv_size
= sizeof(GradFunContext
),
258 .priv_class
= &gradfun_class
,
261 .query_formats
= query_formats
,
262 .inputs
= avfilter_vf_gradfun_inputs
,
263 .outputs
= avfilter_vf_gradfun_outputs
,
264 .flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
,