FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
vf_histogram.c
Go to the documentation of this file.
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 
36 };
37 
38 typedef struct HistogramContext {
39  const AVClass *class; ///< AVClass context for log and options purpose
41  unsigned histogram[256];
42  int ncomp;
43  const uint8_t *bg_color;
44  const uint8_t *fg_color;
47  int step;
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[] = {
82 };
83 
84 static const enum AVPixelFormat levels_pix_fmts[] = {
87 };
88 
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 
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:
129  break;
130  default:
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 
323  .name = "histogram",
324  .description = NULL_IF_CONFIG_SMALL("Compute and draw a histogram."),
325  .priv_size = sizeof(HistogramContext),
327  .inputs = inputs,
328  .outputs = outputs,
329  .priv_class = &histogram_class,
330 };