2 * Copyright (c) 2012-2013 Paul B Mahol
4 * This file is part of FFmpeg.
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.
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.
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
21 #include "libavutil/avassert.h"
22 #include "libavutil/opt.h"
23 #include "libavutil/parseutils.h"
24 #include "libavutil/pixdesc.h"
38 typedef struct HistogramContext
{
39 const AVClass
*class; ///< AVClass context for log and options purpose
40 enum HistogramMode mode
;
41 unsigned histogram
[256];
43 const uint8_t *bg_color
;
44 const uint8_t *fg_color
;
52 const AVPixFmtDescriptor
*desc
;
55 #define OFFSET(x) offsetof(HistogramContext, x)
56 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
58 static const AVOption histogram_options
[] = {
59 { "mode", "set histogram mode", OFFSET(mode
), AV_OPT_TYPE_INT
, {.i64
=MODE_LEVELS
}, 0, MODE_NB
-1, FLAGS
, "mode"},
60 { "levels", "standard histogram", 0, AV_OPT_TYPE_CONST
, {.i64
=MODE_LEVELS
}, 0, 0, FLAGS
, "mode" },
61 { "waveform", "per row/column luminance graph", 0, AV_OPT_TYPE_CONST
, {.i64
=MODE_WAVEFORM
}, 0, 0, FLAGS
, "mode" },
62 { "color", "chroma values in vectorscope", 0, AV_OPT_TYPE_CONST
, {.i64
=MODE_COLOR
}, 0, 0, FLAGS
, "mode" },
63 { "color2", "chroma values in vectorscope", 0, AV_OPT_TYPE_CONST
, {.i64
=MODE_COLOR2
}, 0, 0, FLAGS
, "mode" },
64 { "level_height", "set level height", OFFSET(level_height
), AV_OPT_TYPE_INT
, {.i64
=200}, 50, 2048, FLAGS
},
65 { "scale_height", "set scale height", OFFSET(scale_height
), AV_OPT_TYPE_INT
, {.i64
=12}, 0, 40, FLAGS
},
66 { "step", "set waveform step value", OFFSET(step
), AV_OPT_TYPE_INT
, {.i64
=10}, 1, 255, FLAGS
},
67 { "waveform_mode", "set waveform mode", OFFSET(waveform_mode
), AV_OPT_TYPE_INT
, {.i64
=0}, 0, 1, FLAGS
, "waveform_mode"},
68 { "row", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=0}, 0, 0, FLAGS
, "waveform_mode" },
69 { "column", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=1}, 0, 0, FLAGS
, "waveform_mode" },
70 { "waveform_mirror", "set waveform mirroring", OFFSET(waveform_mirror
), AV_OPT_TYPE_INT
, {.i64
=0}, 0, 1, FLAGS
, "waveform_mirror"},
71 { "display_mode", "set display mode", OFFSET(display_mode
), AV_OPT_TYPE_INT
, {.i64
=1}, 0, 1, FLAGS
, "display_mode"},
72 { "parade", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=1}, 0, 0, FLAGS
, "display_mode" },
73 { "overlay", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=0}, 0, 0, FLAGS
, "display_mode" },
74 { "levels_mode", "set levels mode", OFFSET(levels_mode
), AV_OPT_TYPE_INT
, {.i64
=0}, 0, 1, FLAGS
, "levels_mode"},
75 { "linear", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=0}, 0, 0, FLAGS
, "levels_mode" },
76 { "logarithmic", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=1}, 0, 0, FLAGS
, "levels_mode" },
80 AVFILTER_DEFINE_CLASS(histogram
);
82 static const enum AVPixelFormat color_pix_fmts
[] = {
83 AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUVA444P
, AV_PIX_FMT_YUVJ444P
,
87 static const enum AVPixelFormat levels_pix_fmts
[] = {
88 AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUVA444P
, AV_PIX_FMT_YUVJ444P
,
89 AV_PIX_FMT_GRAY8
, AV_PIX_FMT_GBRP
, AV_PIX_FMT_GBRAP
, AV_PIX_FMT_NONE
92 static const enum AVPixelFormat waveform_pix_fmts
[] = {
93 AV_PIX_FMT_GBRP
, AV_PIX_FMT_GBRAP
,
94 AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUV420P
,
95 AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV440P
,
96 AV_PIX_FMT_YUV411P
, AV_PIX_FMT_YUV410P
,
97 AV_PIX_FMT_YUVJ440P
, AV_PIX_FMT_YUVJ411P
, AV_PIX_FMT_YUVJ420P
,
98 AV_PIX_FMT_YUVJ422P
, AV_PIX_FMT_YUVJ444P
,
99 AV_PIX_FMT_YUVA444P
, AV_PIX_FMT_YUVA422P
, AV_PIX_FMT_YUVA420P
,
104 static int query_formats(AVFilterContext
*ctx
)
106 HistogramContext
*h
= ctx
->priv
;
107 const enum AVPixelFormat
*pix_fmts
;
111 pix_fmts
= waveform_pix_fmts
;
114 pix_fmts
= levels_pix_fmts
;
118 pix_fmts
= color_pix_fmts
;
124 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
129 static const uint8_t black_yuva_color
[4] = { 0, 127, 127, 255 };
130 static const uint8_t black_gbrp_color
[4] = { 0, 0, 0, 255 };
131 static const uint8_t white_yuva_color
[4] = { 255, 127, 127, 255 };
132 static const uint8_t white_gbrp_color
[4] = { 255, 255, 255, 255 };
134 static int config_input(AVFilterLink
*inlink
)
136 HistogramContext
*h
= inlink
->dst
->priv
;
138 h
->desc
= av_pix_fmt_desc_get(inlink
->format
);
139 h
->ncomp
= h
->desc
->nb_components
;
141 switch (inlink
->format
) {
142 case AV_PIX_FMT_GBRAP
:
143 case AV_PIX_FMT_GBRP
:
144 h
->bg_color
= black_gbrp_color
;
145 h
->fg_color
= white_gbrp_color
;
148 h
->bg_color
= black_yuva_color
;
149 h
->fg_color
= white_yuva_color
;
155 static int config_output(AVFilterLink
*outlink
)
157 AVFilterContext
*ctx
= outlink
->src
;
158 HistogramContext
*h
= ctx
->priv
;
163 outlink
->h
= (h
->level_height
+ h
->scale_height
) * FFMAX(h
->ncomp
* h
->display_mode
, 1);
166 if (h
->waveform_mode
)
167 outlink
->h
= 256 * FFMAX(h
->ncomp
* h
->display_mode
, 1);
169 outlink
->w
= 256 * FFMAX(h
->ncomp
* h
->display_mode
, 1);
173 outlink
->h
= outlink
->w
= 256;
179 outlink
->sample_aspect_ratio
= (AVRational
){1,1};
184 static void gen_waveform(HistogramContext
*h
, AVFrame
*inpicref
, AVFrame
*outpicref
,
185 int component
, int intensity
, int offset
, int col_mode
)
187 const int plane
= h
->desc
->comp
[component
].plane
;
188 const int mirror
= h
->waveform_mirror
;
189 const int is_chroma
= (component
== 1 || component
== 2);
190 const int shift_w
= (is_chroma
? h
->desc
->log2_chroma_w
: 0);
191 const int shift_h
= (is_chroma
? h
->desc
->log2_chroma_h
: 0);
192 const int src_linesize
= inpicref
->linesize
[plane
];
193 const int dst_linesize
= outpicref
->linesize
[plane
];
194 const int dst_signed_linesize
= dst_linesize
* (mirror
== 1 ? -1 : 1);
195 uint8_t *src_data
= inpicref
->data
[plane
];
196 uint8_t *dst_data
= outpicref
->data
[plane
] + (col_mode
? (offset
>> shift_h
) * dst_linesize
: offset
>> shift_w
);
197 uint8_t * const dst_bottom_line
= dst_data
+ dst_linesize
* ((256 >> shift_h
) - 1);
198 uint8_t * const dst_line
= (mirror
? dst_bottom_line
: dst_data
);
199 const uint8_t max
= 255 - intensity
;
200 const int src_h
= FF_CEIL_RSHIFT(inpicref
->height
, shift_h
);
201 const int src_w
= FF_CEIL_RSHIFT(inpicref
->width
, shift_w
);
205 if (!col_mode
&& mirror
)
206 dst_data
+= 256 >> shift_w
;
207 for (y
= 0; y
< src_h
; y
++) {
208 const uint8_t *src_data_end
= src_data
+ src_w
;
210 for (p
= src_data
; p
< src_data_end
; p
++) {
213 target
= dst
++ + dst_signed_linesize
* (*p
>> shift_h
);
216 target
= dst_data
- (*p
>> shift_w
);
218 target
= dst_data
+ (*p
>> shift_w
);
221 *target
+= intensity
;
225 src_data
+= src_linesize
;
226 dst_data
+= dst_linesize
;
231 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
233 HistogramContext
*h
= inlink
->dst
->priv
;
234 AVFilterContext
*ctx
= inlink
->dst
;
235 AVFilterLink
*outlink
= ctx
->outputs
[0];
241 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
244 return AVERROR(ENOMEM
);
249 for (k
= 0; k
< h
->ncomp
; k
++) {
250 const int is_chroma
= (k
== 1 || k
== 2);
251 const int dst_h
= FF_CEIL_RSHIFT(outlink
->h
, (is_chroma
? h
->desc
->log2_chroma_h
: 0));
252 const int dst_w
= FF_CEIL_RSHIFT(outlink
->w
, (is_chroma
? h
->desc
->log2_chroma_w
: 0));
253 for (i
= 0; i
< dst_h
; i
++)
254 memset(out
->data
[h
->desc
->comp
[k
].plane
] +
255 i
* out
->linesize
[h
->desc
->comp
[k
].plane
],
256 h
->bg_color
[k
], dst_w
);
261 for (k
= 0; k
< h
->ncomp
; k
++) {
262 const int p
= h
->desc
->comp
[k
].plane
;
263 const int start
= k
* (h
->level_height
+ h
->scale_height
) * h
->display_mode
;
265 unsigned max_hval
= 0;
267 for (i
= 0; i
< in
->height
; i
++) {
268 src
= in
->data
[p
] + i
* in
->linesize
[p
];
269 for (j
= 0; j
< in
->width
; j
++)
270 h
->histogram
[src
[j
]]++;
273 for (i
= 0; i
< 256; i
++)
274 max_hval
= FFMAX(max_hval
, h
->histogram
[i
]);
275 max_hval_log
= log2(max_hval
+ 1);
277 for (i
= 0; i
< outlink
->w
; i
++) {
281 col_height
= round(h
->level_height
* (1. - (log2(h
->histogram
[i
] + 1) / max_hval_log
)));
283 col_height
= h
->level_height
- (h
->histogram
[i
] * (int64_t)h
->level_height
+ max_hval
- 1) / max_hval
;
285 for (j
= h
->level_height
- 1; j
>= col_height
; j
--) {
286 if (h
->display_mode
) {
287 for (l
= 0; l
< h
->ncomp
; l
++)
288 out
->data
[l
][(j
+ start
) * out
->linesize
[l
] + i
] = h
->fg_color
[l
];
290 out
->data
[p
][(j
+ start
) * out
->linesize
[p
] + i
] = 255;
293 for (j
= h
->level_height
+ h
->scale_height
- 1; j
>= h
->level_height
; j
--)
294 out
->data
[p
][(j
+ start
) * out
->linesize
[p
] + i
] = i
;
297 memset(h
->histogram
, 0, 256 * sizeof(unsigned));
301 for (k
= 0; k
< h
->ncomp
; k
++) {
302 const int offset
= k
* 256 * h
->display_mode
;
303 gen_waveform(h
, in
, out
, k
, h
->step
, offset
, h
->waveform_mode
);
307 for (i
= 0; i
< inlink
->h
; i
++) {
308 const int iw1
= i
* in
->linesize
[1];
309 const int iw2
= i
* in
->linesize
[2];
310 for (j
= 0; j
< inlink
->w
; j
++) {
311 const int pos
= in
->data
[1][iw1
+ j
] * out
->linesize
[0] + in
->data
[2][iw2
+ j
];
312 if (out
->data
[0][pos
] < 255)
316 for (i
= 0; i
< 256; i
++) {
317 dst
= out
->data
[0] + i
* out
->linesize
[0];
318 for (j
= 0; j
< 256; j
++) {
320 out
->data
[1][i
* out
->linesize
[0] + j
] = i
;
321 out
->data
[2][i
* out
->linesize
[0] + j
] = j
;
327 for (i
= 0; i
< inlink
->h
; i
++) {
328 const int iw1
= i
* in
->linesize
[1];
329 const int iw2
= i
* in
->linesize
[2];
330 for (j
= 0; j
< inlink
->w
; j
++) {
331 const int u
= in
->data
[1][iw1
+ j
];
332 const int v
= in
->data
[2][iw2
+ j
];
333 const int pos
= u
* out
->linesize
[0] + v
;
334 if (!out
->data
[0][pos
])
335 out
->data
[0][pos
] = FFABS(128 - u
) + FFABS(128 - v
);
336 out
->data
[1][pos
] = u
;
337 out
->data
[2][pos
] = v
;
346 return ff_filter_frame(outlink
, out
);
349 static const AVFilterPad inputs
[] = {
352 .type
= AVMEDIA_TYPE_VIDEO
,
353 .filter_frame
= filter_frame
,
354 .config_props
= config_input
,
359 static const AVFilterPad outputs
[] = {
362 .type
= AVMEDIA_TYPE_VIDEO
,
363 .config_props
= config_output
,
368 AVFilter ff_vf_histogram
= {
370 .description
= NULL_IF_CONFIG_SMALL("Compute and draw a histogram."),
371 .priv_size
= sizeof(HistogramContext
),
372 .query_formats
= query_formats
,
375 .priv_class
= &histogram_class
,