2 * Copyright (c) 2010 Nolan Lum <nol888@gmail.com>
3 * Copyright (c) 2009 Loren Merritt <lorenm@u.washignton.edu>
5 * This file is part of Libav.
7 * Libav 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 * Libav 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 Libav; 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/pixdesc.h"
45 DECLARE_ALIGNED(16, static const uint16_t, dither
)[8][8] = {
46 {0x00,0x60,0x18,0x78,0x06,0x66,0x1E,0x7E},
47 {0x40,0x20,0x58,0x38,0x46,0x26,0x5E,0x3E},
48 {0x10,0x70,0x08,0x68,0x16,0x76,0x0E,0x6E},
49 {0x50,0x30,0x48,0x28,0x56,0x36,0x4E,0x2E},
50 {0x04,0x64,0x1C,0x7C,0x02,0x62,0x1A,0x7A},
51 {0x44,0x24,0x5C,0x3C,0x42,0x22,0x5A,0x3A},
52 {0x14,0x74,0x0C,0x6C,0x12,0x72,0x0A,0x6A},
53 {0x54,0x34,0x4C,0x2C,0x52,0x32,0x4A,0x2A},
56 void ff_gradfun_filter_line_c(uint8_t *dst
, uint8_t *src
, uint16_t *dc
, int width
, int thresh
, const uint16_t *dithers
)
59 for (x
= 0; x
< width
; x
++, dc
+= x
& 1) {
60 int pix
= src
[x
] << 7;
61 int delta
= dc
[0] - pix
;
62 int m
= abs(delta
) * thresh
>> 16;
63 m
= FFMAX(0, 127 - m
);
64 m
= m
* m
* delta
>> 14;
65 pix
+= m
+ dithers
[x
& 7];
66 dst
[x
] = av_clip_uint8(pix
>> 7);
70 void ff_gradfun_blur_line_c(uint16_t *dc
, uint16_t *buf
, uint16_t *buf1
, uint8_t *src
, int src_linesize
, int width
)
73 for (x
= 0; x
< width
; x
++) {
74 v
= buf1
[x
] + src
[2 * x
] + src
[2 * x
+ 1] + src
[2 * x
+ src_linesize
] + src
[2 * x
+ 1 + src_linesize
];
81 static void filter(GradFunContext
*ctx
, uint8_t *dst
, uint8_t *src
, int width
, int height
, int dst_linesize
, int src_linesize
, int r
)
83 int bstride
= FFALIGN(width
, 16) / 2;
85 uint32_t dc_factor
= (1 << 21) / (r
* r
);
86 uint16_t *dc
= ctx
->buf
+ 16;
87 uint16_t *buf
= ctx
->buf
+ bstride
+ 32;
88 int thresh
= ctx
->thresh
;
90 memset(dc
, 0, (bstride
+ 16) * sizeof(*buf
));
91 for (y
= 0; y
< r
; y
++)
92 ctx
->blur_line(dc
, buf
+ y
* bstride
, buf
+ (y
- 1) * bstride
, src
+ 2 * y
* src_linesize
, src_linesize
, width
/ 2);
95 int mod
= ((y
+ r
) / 2) % r
;
96 uint16_t *buf0
= buf
+ mod
* bstride
;
97 uint16_t *buf1
= buf
+ (mod
? mod
- 1 : r
- 1) * bstride
;
99 ctx
->blur_line(dc
, buf0
, buf1
, src
+ (y
+ r
) * src_linesize
, src_linesize
, width
/ 2);
100 for (x
= v
= 0; x
< r
; x
++)
102 for (; x
< width
/ 2; x
++) {
103 v
+= dc
[x
] - dc
[x
-r
];
104 dc
[x
-r
] = v
* dc_factor
>> 16;
106 for (; x
< (width
+ r
+ 1) / 2; x
++)
107 dc
[x
-r
] = v
* dc_factor
>> 16;
108 for (x
= -r
/ 2; x
< 0; x
++)
112 for (y
= 0; y
< r
; y
++)
113 ctx
->filter_line(dst
+ y
* dst_linesize
, src
+ y
* src_linesize
, dc
- r
/ 2, width
, thresh
, dither
[y
& 7]);
115 ctx
->filter_line(dst
+ y
* dst_linesize
, src
+ y
* src_linesize
, dc
- r
/ 2, width
, thresh
, dither
[y
& 7]);
116 if (++y
>= height
) break;
117 ctx
->filter_line(dst
+ y
* dst_linesize
, src
+ y
* src_linesize
, dc
- r
/ 2, width
, thresh
, dither
[y
& 7]);
118 if (++y
>= height
) break;
122 static av_cold
int init(AVFilterContext
*ctx
, const char *args
)
124 GradFunContext
*gf
= ctx
->priv
;
129 sscanf(args
, "%f:%d", &thresh
, &radius
);
131 thresh
= av_clipf(thresh
, 0.51, 255);
132 gf
->thresh
= (1 << 15) / thresh
;
133 gf
->radius
= av_clip((radius
+ 1) & ~1, 4, 32);
135 gf
->blur_line
= ff_gradfun_blur_line_c
;
136 gf
->filter_line
= ff_gradfun_filter_line_c
;
139 ff_gradfun_init_x86(gf
);
141 av_log(ctx
, AV_LOG_VERBOSE
, "threshold:%.2f radius:%d\n", thresh
, gf
->radius
);
146 static av_cold
void uninit(AVFilterContext
*ctx
)
148 GradFunContext
*gf
= ctx
->priv
;
152 static int query_formats(AVFilterContext
*ctx
)
154 static const enum AVPixelFormat pix_fmts
[] = {
155 AV_PIX_FMT_YUV410P
, AV_PIX_FMT_YUV420P
,
156 AV_PIX_FMT_GRAY8
, AV_PIX_FMT_NV12
,
157 AV_PIX_FMT_NV21
, AV_PIX_FMT_YUV444P
,
158 AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUV411P
,
162 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
167 static int config_input(AVFilterLink
*inlink
)
169 GradFunContext
*gf
= inlink
->dst
->priv
;
170 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
171 int hsub
= desc
->log2_chroma_w
;
172 int vsub
= desc
->log2_chroma_h
;
174 gf
->buf
= av_mallocz((FFALIGN(inlink
->w
, 16) * (gf
->radius
+ 1) / 2 + 32) * sizeof(uint16_t));
176 return AVERROR(ENOMEM
);
178 gf
->chroma_w
= -((-inlink
->w
) >> hsub
);
179 gf
->chroma_h
= -((-inlink
->h
) >> vsub
);
180 gf
->chroma_r
= av_clip(((((gf
->radius
>> hsub
) + (gf
->radius
>> vsub
)) / 2 ) + 1) & ~1, 4, 32);
185 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
187 GradFunContext
*gf
= inlink
->dst
->priv
;
188 AVFilterLink
*outlink
= inlink
->dst
->outputs
[0];
192 if (av_frame_is_writable(in
)) {
196 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
199 return AVERROR(ENOMEM
);
202 av_frame_copy_props(out
, in
);
203 out
->width
= outlink
->w
;
204 out
->height
= outlink
->h
;
207 for (p
= 0; p
< 4 && in
->data
[p
]; p
++) {
217 if (FFMIN(w
, h
) > 2 * r
)
218 filter(gf
, out
->data
[p
], in
->data
[p
], w
, h
, out
->linesize
[p
], in
->linesize
[p
], r
);
219 else if (out
->data
[p
] != in
->data
[p
])
220 av_image_copy_plane(out
->data
[p
], out
->linesize
[p
], in
->data
[p
], in
->linesize
[p
], w
, h
);
226 return ff_filter_frame(outlink
, out
);
229 static const AVFilterPad avfilter_vf_gradfun_inputs
[] = {
232 .type
= AVMEDIA_TYPE_VIDEO
,
233 .config_props
= config_input
,
234 .filter_frame
= filter_frame
,
239 static const AVFilterPad avfilter_vf_gradfun_outputs
[] = {
242 .type
= AVMEDIA_TYPE_VIDEO
,
247 AVFilter avfilter_vf_gradfun
= {
249 .description
= NULL_IF_CONFIG_SMALL("Debands video quickly using gradients."),
250 .priv_size
= sizeof(GradFunContext
),
253 .query_formats
= query_formats
,
255 .inputs
= avfilter_vf_gradfun_inputs
,
256 .outputs
= avfilter_vf_gradfun_outputs
,