GCC Code Coverage Report
Directory: ../../../ffmpeg/ Exec Total Coverage
File: src/libavfilter/vf_entropy.c Lines: 0 63 0.0 %
Date: 2020-08-14 10:39:37 Branches: 0 42 0.0 %

Line Branch Exec Source
1
/*
2
 * Copyright (c) 2017 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/imgutils.h"
22
#include "libavutil/opt.h"
23
#include "libavutil/pixdesc.h"
24
#include "avfilter.h"
25
#include "drawutils.h"
26
#include "formats.h"
27
#include "internal.h"
28
#include "video.h"
29
30
typedef struct EntropyContext {
31
    const AVClass *class;
32
33
    int mode;
34
35
    int nb_planes;
36
    int planeheight[4];
37
    int planewidth[4];
38
    int depth;
39
    int is_rgb;
40
    uint8_t rgba_map[4];
41
    char planenames[4];
42
    int64_t *histogram;
43
} EntropyContext;
44
45
#define OFFSET(x) offsetof(EntropyContext, x)
46
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
47
static const AVOption entropy_options[] = {
48
    { "mode", "set kind of histogram entropy measurement",  OFFSET(mode), AV_OPT_TYPE_INT,   {.i64=0}, 0, 1, FLAGS, "mode" },
49
    { "normal", NULL,                                       0,            AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode" },
50
    { "diff",   NULL,                                       0,            AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode" },
51
    { NULL }
52
};
53
54
AVFILTER_DEFINE_CLASS(entropy);
55
56
static int query_formats(AVFilterContext *ctx)
57
{
58
    static const enum AVPixelFormat pixfmts[] = {
59
        AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P,
60
        AV_PIX_FMT_YUV440P,
61
        AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ411P,
62
        AV_PIX_FMT_YUVJ440P,
63
        AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV420P9,
64
        AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV420P10,
65
        AV_PIX_FMT_YUV440P10,
66
        AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12,
67
        AV_PIX_FMT_YUV440P12,
68
        AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14,
69
        AV_PIX_FMT_YUV444P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV420P16,
70
        AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
71
        AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
72
        AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
73
        AV_PIX_FMT_NONE
74
    };
75
76
    AVFilterFormats *formats = ff_make_format_list(pixfmts);
77
    if (!formats)
78
        return AVERROR(ENOMEM);
79
    return ff_set_common_formats(ctx, formats);
80
}
81
82
static int config_input(AVFilterLink *inlink)
83
{
84
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
85
    AVFilterContext *ctx = inlink->dst;
86
    EntropyContext *s = ctx->priv;
87
88
    s->nb_planes = desc->nb_components;
89
90
    s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
91
    s->planeheight[0] = s->planeheight[3] = inlink->h;
92
    s->planewidth[1]  = s->planewidth[2]  = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
93
    s->planewidth[0]  = s->planewidth[3]  = inlink->w;
94
95
    s->depth = desc->comp[0].depth;
96
    s->is_rgb = ff_fill_rgba_map(s->rgba_map, inlink->format) >= 0;
97
98
    s->planenames[0] = s->is_rgb ? 'R' : 'Y';
99
    s->planenames[1] = s->is_rgb ? 'G' : 'U';
100
    s->planenames[2] = s->is_rgb ? 'B' : 'V';
101
    s->planenames[3] = 'A';
102
103
    s->histogram = av_malloc_array(1 << s->depth, sizeof(*s->histogram));
104
    if (!s->histogram)
105
        return AVERROR(ENOMEM);
106
107
    return 0;
108
}
109
110
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
111
{
112
    AVFilterContext *ctx = inlink->dst;
113
    AVFilterLink *outlink = ctx->outputs[0];
114
    EntropyContext *s = ctx->priv;
115
    int plane, y, x;
116
117
    for (plane = 0; plane < s->nb_planes; plane++) {
118
        int cidx = s->is_rgb ? s->rgba_map[plane] : plane;
119
        const uint8_t *src8 = in->data[plane];
120
        const uint16_t *src16 = (const uint16_t *)in->data[plane];
121
        float total = s->planewidth[plane] * s->planeheight[plane];
122
        float entropy = 0;
123
        char metabuf[128];
124
        char key[128];
125
126
        memset(s->histogram, 0, (1 << s->depth) * sizeof(*s->histogram));
127
128
        if (s->depth <= 8) {
129
            for (y = 0; y < s->planeheight[plane]; y++) {
130
                for (x = 0; x < s->planewidth[plane]; x++) {
131
                    s->histogram[src8[x]]++;
132
                }
133
134
                src8 += in->linesize[plane];
135
            }
136
        } else {
137
            for (y = 0; y < s->planeheight[plane]; y++) {
138
                for (x = 0; x < s->planewidth[plane]; x++) {
139
                    s->histogram[src16[x]]++;
140
                }
141
142
                src16 += in->linesize[plane] / 2;
143
            }
144
        }
145
146
        for (y = 0; y < 1 << s->depth; y++) {
147
            if (s->mode == 0) {
148
                if (s->histogram[y]) {
149
                    float p = s->histogram[y] / total;
150
                    entropy += -log2(p) * p;
151
                }
152
            } else if (s->mode == 1) {
153
                if (y && (s->histogram[y] - s->histogram[y - 1]) != 0) {
154
                    float p = FFABS(s->histogram[y] - s->histogram[y - 1]) / total;
155
                    entropy += -log2(p) * p;
156
                }
157
            }
158
        }
159
160
        snprintf(key, sizeof(key), "lavfi.entropy.entropy.%s.%c", s->mode ? "diff" : "normal", s->planenames[cidx]);
161
        snprintf(metabuf, sizeof(metabuf), "%f", entropy);
162
        av_dict_set(&in->metadata, key, metabuf, 0);
163
        snprintf(key, sizeof(key), "lavfi.entropy.normalized_entropy.%s.%c", s->mode ? "diff" : "normal", s->planenames[cidx]);
164
        snprintf(metabuf, sizeof(metabuf), "%f", entropy / log2(1 << s->depth));
165
        av_dict_set(&in->metadata, key, metabuf, 0);
166
    }
167
168
    return ff_filter_frame(outlink, in);
169
}
170
171
static av_cold void uninit(AVFilterContext *ctx)
172
{
173
    EntropyContext *s = ctx->priv;
174
175
    av_freep(&s->histogram);
176
}
177
178
static const AVFilterPad inputs[] = {
179
    {
180
        .name           = "default",
181
        .type           = AVMEDIA_TYPE_VIDEO,
182
        .filter_frame   = filter_frame,
183
        .config_props   = config_input,
184
    },
185
    { NULL }
186
};
187
188
static const AVFilterPad outputs[] = {
189
    {
190
        .name = "default",
191
        .type = AVMEDIA_TYPE_VIDEO,
192
    },
193
    { NULL }
194
};
195
196
AVFilter ff_vf_entropy = {
197
    .name           = "entropy",
198
    .description    = NULL_IF_CONFIG_SMALL("Measure video frames entropy."),
199
    .priv_size      = sizeof(EntropyContext),
200
    .uninit         = uninit,
201
    .query_formats  = query_formats,
202
    .inputs         = inputs,
203
    .outputs        = outputs,
204
    .priv_class     = &entropy_class,
205
    .flags          = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
206
};