]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_histogram.c
Merge commit '17d57848fc14e82f76a65ffb25c90f2f011dc4a0'
[ffmpeg] / libavfilter / vf_histogram.c
1 /*
2  * Copyright (c) 2012-2013 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/avassert.h"
22 #include "libavutil/opt.h"
23 #include "libavutil/parseutils.h"
24 #include "libavutil/pixdesc.h"
25 #include "avfilter.h"
26 #include "formats.h"
27 #include "internal.h"
28 #include "video.h"
29
30 enum HistogramMode {
31     MODE_LEVELS,
32     MODE_WAVEFORM,
33     MODE_COLOR,
34     MODE_COLOR2,
35     MODE_NB
36 };
37
38 typedef struct HistogramContext {
39     const AVClass *class;               ///< AVClass context for log and options purpose
40     enum HistogramMode mode;
41     unsigned       histogram[256];
42     int            ncomp;
43     const uint8_t  *bg_color;
44     const uint8_t  *fg_color;
45     int            level_height;
46     int            scale_height;
47     int            step;
48     int            waveform_mode;
49     int            display_mode;
50     int            levels_mode;
51 } HistogramContext;
52
53 #define OFFSET(x) offsetof(HistogramContext, x)
54 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
55
56 static const AVOption histogram_options[] = {
57     { "mode", "set histogram mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_LEVELS}, 0, MODE_NB-1, FLAGS, "mode"},
58     { "levels", "standard histogram", 0, AV_OPT_TYPE_CONST, {.i64=MODE_LEVELS}, 0, 0, FLAGS, "mode" },
59     { "waveform", "per row/column luminance graph", 0, AV_OPT_TYPE_CONST, {.i64=MODE_WAVEFORM}, 0, 0, FLAGS, "mode" },
60     { "color", "chroma values in vectorscope", 0, AV_OPT_TYPE_CONST, {.i64=MODE_COLOR}, 0, 0, FLAGS, "mode" },
61     { "color2", "chroma values in vectorscope", 0, AV_OPT_TYPE_CONST, {.i64=MODE_COLOR2}, 0, 0, FLAGS, "mode" },
62     { "level_height", "set level height", OFFSET(level_height), AV_OPT_TYPE_INT, {.i64=200}, 50, 2048, FLAGS},
63     { "scale_height", "set scale height", OFFSET(scale_height), AV_OPT_TYPE_INT, {.i64=12}, 0, 40, FLAGS},
64     { "step", "set waveform step value", OFFSET(step), AV_OPT_TYPE_INT, {.i64=10}, 1, 255, FLAGS},
65     { "waveform_mode", "set waveform mode", OFFSET(waveform_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "waveform_mode"},
66     { "row",   NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "waveform_mode" },
67     { "column", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "waveform_mode" },
68     { "display_mode", "set display mode", OFFSET(display_mode), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "display_mode"},
69     { "parade",  NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "display_mode" },
70     { "overlay", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "display_mode" },
71     { "levels_mode", "set levels mode", OFFSET(levels_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "levels_mode"},
72     { "linear",      NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "levels_mode" },
73     { "logarithmic", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "levels_mode" },
74     { NULL },
75 };
76
77 AVFILTER_DEFINE_CLASS(histogram);
78
79 static const enum AVPixelFormat color_pix_fmts[] = {
80     AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVJ444P,
81     AV_PIX_FMT_NONE
82 };
83
84 static const enum AVPixelFormat levels_pix_fmts[] = {
85     AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVJ444P,
86     AV_PIX_FMT_GRAY8, AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_NONE
87 };
88
89 static int query_formats(AVFilterContext *ctx)
90 {
91     HistogramContext *h = ctx->priv;
92     const enum AVPixelFormat *pix_fmts;
93
94     switch (h->mode) {
95     case MODE_WAVEFORM:
96     case MODE_LEVELS:
97         pix_fmts = levels_pix_fmts;
98         break;
99     case MODE_COLOR:
100     case MODE_COLOR2:
101         pix_fmts = color_pix_fmts;
102         break;
103     default:
104         av_assert0(0);
105     }
106
107     ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
108
109     return 0;
110 }
111
112 static const uint8_t black_yuva_color[4] = { 0, 127, 127, 255 };
113 static const uint8_t black_gbrp_color[4] = { 0, 0, 0, 255 };
114 static const uint8_t white_yuva_color[4] = { 255, 127, 127, 255 };
115 static const uint8_t white_gbrp_color[4] = { 255, 255, 255, 255 };
116
117 static int config_input(AVFilterLink *inlink)
118 {
119     HistogramContext *h = inlink->dst->priv;
120     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
121
122     h->ncomp = desc->nb_components;
123
124     switch (inlink->format) {
125     case AV_PIX_FMT_GBRAP:
126     case AV_PIX_FMT_GBRP:
127         h->bg_color = black_gbrp_color;
128         h->fg_color = white_gbrp_color;
129         break;
130     default:
131         h->bg_color = black_yuva_color;
132         h->fg_color = white_yuva_color;
133     }
134
135     return 0;
136 }
137
138 static int config_output(AVFilterLink *outlink)
139 {
140     AVFilterContext *ctx = outlink->src;
141     HistogramContext *h = ctx->priv;
142
143     switch (h->mode) {
144     case MODE_LEVELS:
145         outlink->w = 256;
146         outlink->h = (h->level_height + h->scale_height) * FFMAX(h->ncomp * h->display_mode, 1);
147         break;
148     case MODE_WAVEFORM:
149         if (h->waveform_mode)
150             outlink->h = 256 * FFMAX(h->ncomp * h->display_mode, 1);
151         else
152             outlink->w = 256 * FFMAX(h->ncomp * h->display_mode, 1);
153         break;
154     case MODE_COLOR:
155     case MODE_COLOR2:
156         outlink->h = outlink->w = 256;
157         break;
158     default:
159         av_assert0(0);
160     }
161
162     outlink->sample_aspect_ratio = (AVRational){1,1};
163
164     return 0;
165 }
166
167 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
168 {
169     HistogramContext *h   = inlink->dst->priv;
170     AVFilterContext *ctx  = inlink->dst;
171     AVFilterLink *outlink = ctx->outputs[0];
172     AVFrame *out;
173     const uint8_t *src;
174     uint8_t *dst;
175     int i, j, k, l;
176
177     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
178     if (!out) {
179         av_frame_free(&in);
180         return AVERROR(ENOMEM);
181     }
182
183     out->pts = in->pts;
184
185     for (k = 0; k < h->ncomp; k++)
186         for (i = 0; i < outlink->h; i++)
187             memset(out->data[k] + i * out->linesize[k], h->bg_color[k], outlink->w);
188
189     switch (h->mode) {
190     case MODE_LEVELS:
191         for (k = 0; k < h->ncomp; k++) {
192             int start = k * (h->level_height + h->scale_height) * h->display_mode;
193             double max_hval_log;
194             unsigned max_hval = 0;
195
196             for (i = 0; i < in->height; i++) {
197                 src = in->data[k] + i * in->linesize[k];
198                 for (j = 0; j < in->width; j++)
199                     h->histogram[src[j]]++;
200             }
201
202             for (i = 0; i < 256; i++)
203                 max_hval = FFMAX(max_hval, h->histogram[i]);
204             max_hval_log = log2(max_hval + 1);
205
206             for (i = 0; i < outlink->w; i++) {
207                 int col_height;
208
209                 if (h->levels_mode)
210                     col_height = round(h->level_height * (1. - (log2(h->histogram[i] + 1) / max_hval_log)));
211                 else
212                     col_height = h->level_height - (h->histogram[i] * (int64_t)h->level_height + max_hval - 1) / max_hval;
213
214                 for (j = h->level_height - 1; j >= col_height; j--) {
215                     if (h->display_mode) {
216                         for (l = 0; l < h->ncomp; l++)
217                             out->data[l][(j + start) * out->linesize[l] + i] = h->fg_color[l];
218                     } else {
219                         out->data[k][(j + start) * out->linesize[k] + i] = 255;
220                     }
221                 }
222                 for (j = h->level_height + h->scale_height - 1; j >= h->level_height; j--)
223                     out->data[k][(j + start) * out->linesize[k] + i] = i;
224             }
225
226             memset(h->histogram, 0, 256 * sizeof(unsigned));
227         }
228         break;
229     case MODE_WAVEFORM:
230         if (h->waveform_mode) {
231             for (k = 0; k < h->ncomp; k++) {
232                 int offset = k * 256 * h->display_mode;
233                 for (i = 0; i < inlink->w; i++) {
234                     for (j = 0; j < inlink->h; j++) {
235                         int pos = (offset +
236                                    in->data[k][j * in->linesize[k] + i]) *
237                                   out->linesize[k] + i;
238                         unsigned value = out->data[k][pos];
239                         value = FFMIN(value + h->step, 255);
240                         out->data[k][pos] = value;
241                     }
242                 }
243             }
244         } else {
245             for (k = 0; k < h->ncomp; k++) {
246                 int offset = k * 256 * h->display_mode;
247                 for (i = 0; i < inlink->h; i++) {
248                     src = in ->data[k] + i * in ->linesize[k];
249                     dst = out->data[k] + i * out->linesize[k];
250                     for (j = 0; j < inlink->w; j++) {
251                         int pos = src[j] + offset;
252                         unsigned value = dst[pos];
253                         value = FFMIN(value + h->step, 255);
254                         dst[pos] = value;
255                     }
256                 }
257             }
258         }
259         break;
260     case MODE_COLOR:
261         for (i = 0; i < inlink->h; i++) {
262             int iw1 = i * in->linesize[1];
263             int iw2 = i * in->linesize[2];
264             for (j = 0; j < inlink->w; j++) {
265                 int pos = in->data[1][iw1 + j] * out->linesize[0] + in->data[2][iw2 + j];
266                 if (out->data[0][pos] < 255)
267                     out->data[0][pos]++;
268             }
269         }
270         for (i = 0; i < 256; i++) {
271             dst = out->data[0] + i * out->linesize[0];
272             for (j = 0; j < 256; j++) {
273                 if (!dst[j]) {
274                     out->data[1][i * out->linesize[0] + j] = i;
275                     out->data[2][i * out->linesize[0] + j] = j;
276                 }
277             }
278         }
279         break;
280     case MODE_COLOR2:
281         for (i = 0; i < inlink->h; i++) {
282             int iw1 = i * in->linesize[1];
283             int iw2 = i * in->linesize[2];
284             for (j = 0; j < inlink->w; j++) {
285                 int u = in->data[1][iw1 + j];
286                 int v = in->data[2][iw2 + j];
287                 int pos = u * out->linesize[0] + v;
288                 if (!out->data[0][pos])
289                     out->data[0][pos] = FFABS(128 - u) + FFABS(128 - v);
290                 out->data[1][pos] = u;
291                 out->data[2][pos] = v;
292             }
293         }
294         break;
295     default:
296         av_assert0(0);
297     }
298
299     av_frame_free(&in);
300     return ff_filter_frame(outlink, out);
301 }
302
303 static const AVFilterPad inputs[] = {
304     {
305         .name         = "default",
306         .type         = AVMEDIA_TYPE_VIDEO,
307         .filter_frame = filter_frame,
308         .config_props = config_input,
309     },
310     { NULL }
311 };
312
313 static const AVFilterPad outputs[] = {
314     {
315         .name         = "default",
316         .type         = AVMEDIA_TYPE_VIDEO,
317         .config_props = config_output,
318     },
319     { NULL }
320 };
321
322 AVFilter avfilter_vf_histogram = {
323     .name          = "histogram",
324     .description   = NULL_IF_CONFIG_SMALL("Compute and draw a histogram."),
325     .priv_size     = sizeof(HistogramContext),
326     .query_formats = query_formats,
327     .inputs        = inputs,
328     .outputs       = outputs,
329     .priv_class    = &histogram_class,
330 };