Statistics
| Branch: | Revision:

ffmpeg / libavfilter / vf_gradfun.c @ 2912e87a

History | View | Annotate | Download (8.99 KB)

1
/*
2
 * Copyright (c) 2010 Nolan Lum <nol888@gmail.com>
3
 * Copyright (c) 2009 Loren Merritt <lorenm@u.washignton.edu>
4
 *
5
 * This file is part of Libav.
6
 *
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.
11
 *
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.
16
 *
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
20
 */
21

    
22
/**
23
 * @file
24
 * gradfun debanding filter, ported from MPlayer
25
 * libmpcodecs/vf_gradfun.c
26
 *
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.
33
 */
34

    
35
#include "libavutil/imgutils.h"
36
#include "libavutil/cpu.h"
37
#include "libavutil/pixdesc.h"
38
#include "avfilter.h"
39
#include "gradfun.h"
40

    
41
DECLARE_ALIGNED(16, static const uint16_t, dither)[8][8] = {
42
    {0x00,0x60,0x18,0x78,0x06,0x66,0x1E,0x7E},
43
    {0x40,0x20,0x58,0x38,0x46,0x26,0x5E,0x3E},
44
    {0x10,0x70,0x08,0x68,0x16,0x76,0x0E,0x6E},
45
    {0x50,0x30,0x48,0x28,0x56,0x36,0x4E,0x2E},
46
    {0x04,0x64,0x1C,0x7C,0x02,0x62,0x1A,0x7A},
47
    {0x44,0x24,0x5C,0x3C,0x42,0x22,0x5A,0x3A},
48
    {0x14,0x74,0x0C,0x6C,0x12,0x72,0x0A,0x6A},
49
    {0x54,0x34,0x4C,0x2C,0x52,0x32,0x4A,0x2A},
50
};
51

    
52
void ff_gradfun_filter_line_c(uint8_t *dst, uint8_t *src, uint16_t *dc, int width, int thresh, const uint16_t *dithers)
53
{
54
    int x;
55
    for (x = 0; x < width; x++, dc += x & 1) {
56
        int pix = src[x] << 7;
57
        int delta = dc[0] - pix;
58
        int m = abs(delta) * thresh >> 16;
59
        m = FFMAX(0, 127 - m);
60
        m = m * m * delta >> 14;
61
        pix += m + dithers[x & 7];
62
        dst[x] = av_clip_uint8(pix >> 7);
63
    }
64
}
65

    
66
void ff_gradfun_blur_line_c(uint16_t *dc, uint16_t *buf, uint16_t *buf1, uint8_t *src, int src_linesize, int width)
67
{
68
    int x, v, old;
69
    for (x = 0; x < width; x++) {
70
        v = buf1[x] + src[2 * x] + src[2 * x + 1] + src[2 * x + src_linesize] + src[2 * x + 1 + src_linesize];
71
        old = buf[x];
72
        buf[x] = v;
73
        dc[x] = v - old;
74
    }
75
}
76

    
77
static void filter(GradFunContext *ctx, uint8_t *dst, uint8_t *src, int width, int height, int dst_linesize, int src_linesize, int r)
78
{
79
    int bstride = FFALIGN(width, 16) / 2;
80
    int y;
81
    uint32_t dc_factor = (1 << 21) / (r * r);
82
    uint16_t *dc = ctx->buf + 16;
83
    uint16_t *buf = ctx->buf + bstride + 32;
84
    int thresh = ctx->thresh;
85

    
86
    memset(dc, 0, (bstride + 16) * sizeof(*buf));
87
    for (y = 0; y < r; y++)
88
        ctx->blur_line(dc, buf + y * bstride, buf + (y - 1) * bstride, src + 2 * y * src_linesize, src_linesize, width / 2);
89
    for (;;) {
90
        if (y < height - r) {
91
            int mod = ((y + r) / 2) % r;
92
            uint16_t *buf0 = buf + mod * bstride;
93
            uint16_t *buf1 = buf + (mod ? mod - 1 : r - 1) * bstride;
94
            int x, v;
95
            ctx->blur_line(dc, buf0, buf1, src + (y + r) * src_linesize, src_linesize, width / 2);
96
            for (x = v = 0; x < r; x++)
97
                v += dc[x];
98
            for (; x < width / 2; x++) {
99
                v += dc[x] - dc[x-r];
100
                dc[x-r] = v * dc_factor >> 16;
101
            }
102
            for (; x < (width + r + 1) / 2; x++)
103
                dc[x-r] = v * dc_factor >> 16;
104
            for (x = -r / 2; x < 0; x++)
105
                dc[x] = dc[0];
106
        }
107
        if (y == r) {
108
            for (y = 0; y < r; y++)
109
                ctx->filter_line(dst + y * dst_linesize, src + y * src_linesize, dc - r / 2, width, thresh, dither[y & 7]);
110
        }
111
        ctx->filter_line(dst + y * dst_linesize, src + y * src_linesize, dc - r / 2, width, thresh, dither[y & 7]);
112
        if (++y >= height) break;
113
        ctx->filter_line(dst + y * dst_linesize, src + y * src_linesize, dc - r / 2, width, thresh, dither[y & 7]);
114
        if (++y >= height) break;
115
    }
116
}
117

    
118
static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
119
{
120
    GradFunContext *gf = ctx->priv;
121
    float thresh = 1.2;
122
    int radius = 16;
123
    av_unused int cpu_flags = av_get_cpu_flags();
124

    
125
    if (args)
126
        sscanf(args, "%f:%d", &thresh, &radius);
127

    
128
    thresh = av_clipf(thresh, 0.51, 255);
129
    gf->thresh = (1 << 15) / thresh;
130
    gf->radius = av_clip((radius + 1) & ~1, 4, 32);
131

    
132
    gf->blur_line = ff_gradfun_blur_line_c;
133
    gf->filter_line = ff_gradfun_filter_line_c;
134

    
135
    if (HAVE_MMX && cpu_flags & AV_CPU_FLAG_MMX2)
136
        gf->filter_line = ff_gradfun_filter_line_mmx2;
137
    if (HAVE_SSSE3 && cpu_flags & AV_CPU_FLAG_SSSE3)
138
        gf->filter_line = ff_gradfun_filter_line_ssse3;
139
    if (HAVE_SSE && cpu_flags & AV_CPU_FLAG_SSE2)
140
        gf->blur_line = ff_gradfun_blur_line_sse2;
141

    
142
    av_log(ctx, AV_LOG_INFO, "threshold:%.2f radius:%d\n", thresh, gf->radius);
143

    
144
    return 0;
145
}
146

    
147
static av_cold void uninit(AVFilterContext *ctx)
148
{
149
    GradFunContext *gf = ctx->priv;
150
    av_freep(&gf->buf);
151
}
152

    
153
static int query_formats(AVFilterContext *ctx)
154
{
155
    static const enum PixelFormat pix_fmts[] = {
156
        PIX_FMT_YUV410P,            PIX_FMT_YUV420P,
157
        PIX_FMT_GRAY8,              PIX_FMT_NV12,
158
        PIX_FMT_NV21,               PIX_FMT_YUV444P,
159
        PIX_FMT_YUV422P,            PIX_FMT_YUV411P,
160
        PIX_FMT_NONE
161
    };
162

    
163
    avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));
164

    
165
    return 0;
166
}
167

    
168
static int config_input(AVFilterLink *inlink)
169
{
170
    GradFunContext *gf = inlink->dst->priv;
171
    int hsub = av_pix_fmt_descriptors[inlink->format].log2_chroma_w;
172
    int vsub = av_pix_fmt_descriptors[inlink->format].log2_chroma_h;
173

    
174
    gf->buf = av_mallocz((FFALIGN(inlink->w, 16) * (gf->radius + 1) / 2 + 32) * sizeof(uint16_t));
175
    if (!gf->buf)
176
        return AVERROR(ENOMEM);
177

    
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);
181

    
182
    return 0;
183
}
184

    
185
static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *inpicref)
186
{
187
    AVFilterLink *outlink = inlink->dst->outputs[0];
188
    AVFilterBufferRef *outpicref;
189

    
190
    if (inpicref->perms & AV_PERM_PRESERVE) {
191
        outpicref = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
192
        avfilter_copy_buffer_ref_props(outpicref, inpicref);
193
        outpicref->video->w = outlink->w;
194
        outpicref->video->h = outlink->h;
195
    } else
196
        outpicref = inpicref;
197

    
198
    outlink->out_buf = outpicref;
199
    avfilter_start_frame(outlink, avfilter_ref_buffer(outpicref, ~0));
200
}
201

    
202
static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
203

    
204
static void end_frame(AVFilterLink *inlink)
205
{
206
    GradFunContext *gf = inlink->dst->priv;
207
    AVFilterBufferRef *inpic = inlink->cur_buf;
208
    AVFilterLink *outlink = inlink->dst->outputs[0];
209
    AVFilterBufferRef *outpic = outlink->out_buf;
210
    int p;
211

    
212
    for (p = 0; p < 4 && inpic->data[p]; p++) {
213
        int w = inlink->w;
214
        int h = inlink->h;
215
        int r = gf->radius;
216
        if (p) {
217
            w = gf->chroma_w;
218
            h = gf->chroma_h;
219
            r = gf->chroma_r;
220
        }
221

    
222
        if (FFMIN(w, h) > 2 * r)
223
            filter(gf, outpic->data[p], inpic->data[p], w, h, outpic->linesize[p], inpic->linesize[p], r);
224
        else if (outpic->data[p] != inpic->data[p])
225
            av_image_copy_plane(outpic->data[p], outpic->linesize[p], inpic->data[p], inpic->linesize[p], w, h);
226
    }
227

    
228
    avfilter_draw_slice(outlink, 0, inlink->h, 1);
229
    avfilter_end_frame(outlink);
230
    avfilter_unref_buffer(inpic);
231
    if (outpic != inpic)
232
        avfilter_unref_buffer(outpic);
233
}
234

    
235
AVFilter avfilter_vf_gradfun = {
236
    .name          = "gradfun",
237
    .description   = NULL_IF_CONFIG_SMALL("Debands video quickly using gradients."),
238
    .priv_size     = sizeof(GradFunContext),
239
    .init          = init,
240
    .uninit        = uninit,
241
    .query_formats = query_formats,
242

    
243
    .inputs    = (AVFilterPad[]) {{ .name             = "default",
244
                                    .type             = AVMEDIA_TYPE_VIDEO,
245
                                    .config_props     = config_input,
246
                                    .start_frame      = start_frame,
247
                                    .draw_slice       = null_draw_slice,
248
                                    .end_frame        = end_frame,
249
                                    .min_perms        = AV_PERM_READ, },
250
                                  { .name = NULL}},
251
    .outputs   = (AVFilterPad[]) {{ .name             = "default",
252
                                    .type             = AVMEDIA_TYPE_VIDEO, },
253
                                  { .name = NULL}},
254
};