Commit 29a92c01 authored by Paul B Mahol's avatar Paul B Mahol

histogram filter

Signed-off-by: 's avatarPaul B Mahol <onemda@gmail.com>
parent e5212354
......@@ -13,6 +13,7 @@ version <next>:
- allpass, bass, bandpass, bandreject, biquad, equalizer, highpass, lowpass
and treble audio filter
- improved showspectrum filter, with multichannel support and sox-like colors
- histogram filter
version 1.1:
......
......@@ -3098,6 +3098,89 @@ the histogram. Possible values are @code{none}, @code{weak} or
@code{strong}. It defaults to @code{none}.
@end table
@section histogram
Compute and draw a color distribution histogram for the input video.
The computed histogram is a representation of distribution of color components
in an image.
The filter accepts the following named parameters:
@table @option
@item mode
Set histogram mode.
It accepts the following values:
@table @samp
@item levels
standard histogram that display color components distribution in an image.
Displays color graph for each color component. Shows distribution
of the Y, U, V, A or G, B, R components, depending on input format,
in current frame. Bellow each graph is color component scale meter.
@item color
chroma values in vectorscope, if brighter more such chroma values are
distributed in an image.
Displays chroma values (U/V color placement) in two dimensional graph
(which is called a vectorscope). It can be used to read of the hue and
saturation of the current frame. At a same time it is a histogram.
The whiter a pixel in the vectorscope, the more pixels of the input frame
correspond to that pixel (that is the more pixels have this chroma value).
The V component is displayed on the horizontal (X) axis, with the leftmost
side being V = 0 and the rightmost side being V = 255.
The U component is displayed on the vertical (Y) axis, with the top
representing U = 0 and the bottom representing U = 255.
The position of a white pixel in the graph corresponds to the chroma value
of a pixel of the input clip. So the graph can be used to read of the
hue (color flavor) and the saturation (the dominance of the hue in the color).
As the hue of a color changes, it moves around the square. At the center of
the square, the saturation is zero, which means that the corresponding pixel
has no color. If you increase the amount of a specific color, while leaving
the other colors unchanged, the saturation increases, and you move towards
the edge of the square.
@item color2
chroma values in vectorscope, similar as @code{color} but actual chroma values
are displayed.
@item waveform
per row/column luminance graph. In row mode the left side represents luma = 0
and right side represents luma = 255. In column mode top side represends
luma = 0 and bottom side represents luma = 255.
@end table
Default value is @code{levels}.
@item level_height
Set height of level in @code{levels}. Default value is @code{200}.
Allowed range is [50, 2048].
@item scale_height
Set height of color scale in @code{levels}. Default value is @code{12}.
Allowed range is [0, 40].
@item step
Set step for @code{waveform} mode. Smaller values are useful to find out how much
of same luminance values across input rows/columns are distributed.
Default value is @code{10}. Allowed range is [1, 255].
@item waveform_mode
Set mode for @code{waveform}. Can be either @code{row}, or @code{column}.
Default is @code{row}.
@subsection Examples
@itemize
@item
Calculate and draw histogram:
@example
ffplay -i input -vf histogram
@end example
@end itemize
@section hqdn3d
High precision/quality 3d denoise filter. This filter aims to reduce
......
......@@ -122,6 +122,7 @@ OBJS-$(CONFIG_GEQ_FILTER) += vf_geq.o
OBJS-$(CONFIG_GRADFUN_FILTER) += vf_gradfun.o
OBJS-$(CONFIG_HFLIP_FILTER) += vf_hflip.o
OBJS-$(CONFIG_HISTEQ_FILTER) += vf_histeq.o
OBJS-$(CONFIG_HISTOGRAM_FILTER) += vf_histogram.o
OBJS-$(CONFIG_HQDN3D_FILTER) += vf_hqdn3d.o
OBJS-$(CONFIG_HUE_FILTER) += vf_hue.o
OBJS-$(CONFIG_IDET_FILTER) += vf_idet.o
......
......@@ -116,6 +116,7 @@ void avfilter_register_all(void)
REGISTER_FILTER(GRADFUN, gradfun, vf);
REGISTER_FILTER(HFLIP, hflip, vf);
REGISTER_FILTER(HISTEQ, histeq, vf);
REGISTER_FILTER(HISTOGRAM, histogram, vf);
REGISTER_FILTER(HQDN3D, hqdn3d, vf);
REGISTER_FILTER(HUE, hue, vf);
REGISTER_FILTER(IDET, idet, vf);
......
......@@ -29,8 +29,8 @@
#include "libavutil/avutil.h"
#define LIBAVFILTER_VERSION_MAJOR 3
#define LIBAVFILTER_VERSION_MINOR 35
#define LIBAVFILTER_VERSION_MICRO 101
#define LIBAVFILTER_VERSION_MINOR 36
#define LIBAVFILTER_VERSION_MICRO 100
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
LIBAVFILTER_VERSION_MINOR, \
......
/*
* Copyright (c) 2012-2013 Paul B Mahol
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "libavutil/avassert.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
#include "libavutil/pixdesc.h"
#include "avfilter.h"
#include "formats.h"
#include "internal.h"
#include "video.h"
enum HistogramMode {
MODE_LEVELS,
MODE_WAVEFORM,
MODE_COLOR,
MODE_COLOR2,
MODE_NB
};
typedef struct HistogramContext {
const AVClass *class; ///< AVClass context for log and options purpose
enum HistogramMode mode;
unsigned histogram[256];
unsigned max_hval;
int ncomp;
const uint8_t *bg_color;
const uint8_t *fg_color;
int level_height;
int scale_height;
int step;
int waveform_mode;
} HistogramContext;
#define OFFSET(x) offsetof(HistogramContext, x)
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
static const AVOption histogram_options[] = {
{ "mode", "set histogram mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_LEVELS}, 0, MODE_NB-1, FLAGS, "mode"},
{ "levels", "standard histogram", 0, AV_OPT_TYPE_CONST, {.i64=MODE_LEVELS}, 0, 0, FLAGS, "mode" },
{ "waveform", "per row/column luminance graph", 0, AV_OPT_TYPE_CONST, {.i64=MODE_WAVEFORM}, 0, 0, FLAGS, "mode" },
{ "color", "chroma values in vectorscope", 0, AV_OPT_TYPE_CONST, {.i64=MODE_COLOR}, 0, 0, FLAGS, "mode" },
{ "color2", "chroma values in vectorscope", 0, AV_OPT_TYPE_CONST, {.i64=MODE_COLOR2}, 0, 0, FLAGS, "mode" },
{ "level_height", "set level height", OFFSET(level_height), AV_OPT_TYPE_INT, {.i64=200}, 50, 2048, FLAGS},
{ "scale_height", "set scale height", OFFSET(scale_height), AV_OPT_TYPE_INT, {.i64=12}, 0, 40, FLAGS},
{ "step", "set waveform step value", OFFSET(step), AV_OPT_TYPE_INT, {.i64=10}, 1, 255, FLAGS},
{ "waveform_mode", "set waveform mode", OFFSET(waveform_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "waveform_mode"},
{ "row", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "waveform_mode" },
{ "column", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "waveform_mode" },
{ NULL },
};
AVFILTER_DEFINE_CLASS(histogram);
static av_cold int init(AVFilterContext *ctx, const char *args)
{
HistogramContext *h = ctx->priv;
int ret;
h->class = &histogram_class;
av_opt_set_defaults(h);
if ((ret = (av_set_options_string(h, args, "=", ":"))) < 0)
return ret;
return 0;
}
static const enum AVPixelFormat color_pix_fmts[] = {
AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVJ444P,
AV_PIX_FMT_NONE
};
static const enum AVPixelFormat levels_pix_fmts[] = {
AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVJ444P,
AV_PIX_FMT_GRAY8, AV_PIX_FMT_GBRP, AV_PIX_FMT_NONE
};
static int query_formats(AVFilterContext *ctx)
{
HistogramContext *h = ctx->priv;
const enum AVPixelFormat *pix_fmts;
switch (h->mode) {
case MODE_LEVELS:
pix_fmts = levels_pix_fmts;
break;
case MODE_WAVEFORM:
case MODE_COLOR:
case MODE_COLOR2:
pix_fmts = color_pix_fmts;
break;
default:
av_assert0(0);
}
ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
return 0;
}
static const uint8_t black_yuva_color[4] = { 0, 127, 127, 255 };
static const uint8_t black_gbrp_color[4] = { 0, 0, 0, 255 };
static const uint8_t white_yuva_color[4] = { 255, 127, 127, 255 };
static const uint8_t white_gbrp_color[4] = { 255, 255, 255, 255 };
static int config_input(AVFilterLink *inlink)
{
HistogramContext *h = inlink->dst->priv;
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
h->ncomp = desc->nb_components;
switch (inlink->format) {
case AV_PIX_FMT_GBRP:
h->bg_color = black_gbrp_color;
h->fg_color = white_gbrp_color;
break;
default:
h->bg_color = black_yuva_color;
h->fg_color = white_yuva_color;
}
return 0;
}
static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
HistogramContext *h = ctx->priv;
switch (h->mode) {
case MODE_LEVELS:
outlink->w = 256;
outlink->h = (h->level_height + h->scale_height) * h->ncomp;
break;
case MODE_WAVEFORM:
if (h->waveform_mode)
outlink->h = 256;
else
outlink->w = 256;
break;
case MODE_COLOR:
case MODE_COLOR2:
outlink->h = outlink->w = 256;
break;
default:
av_assert0(0);
}
outlink->sample_aspect_ratio = (AVRational){1,1};
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *in)
{
HistogramContext *h = inlink->dst->priv;
AVFilterContext *ctx = inlink->dst;
AVFilterLink *outlink = ctx->outputs[0];
AVFilterBufferRef *out;
const uint8_t *src;
uint8_t *dst;
int i, j, k, l, ret;
out = ff_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
if (!out) {
avfilter_unref_bufferp(&in);
return AVERROR(ENOMEM);
}
out->pts = in->pts;
out->pos = in->pos;
for (k = 0; k < h->ncomp; k++)
for (i = 0; i < outlink->h; i++)
memset(out->data[k] + i * out->linesize[k], h->bg_color[k], outlink->w);
switch (h->mode) {
case MODE_LEVELS:
for (k = 0; k < h->ncomp; k++) {
for (i = 0; i < in->video->h; i++) {
src = in->data[k] + i * in->linesize[k];
for (j = 0; j < in->video->w; j++)
h->histogram[src[j]]++;
}
for (i = 0; i < 256; i++)
h->max_hval = FFMAX(h->max_hval, h->histogram[i]);
int start = k * (h->level_height + h->scale_height);
for (i = 0; i < outlink->w; i++) {
int col_height = h->level_height - (float)h->histogram[i] / h->max_hval * h->level_height;
for (j = h->level_height - 1; j >= col_height; j--)
for (l = 0; l < h->ncomp; l++)
out->data[l][(j + start) * out->linesize[l] + i] = h->fg_color[l];
for (j = h->level_height + h->scale_height - 1; j >= h->level_height; j--)
out->data[k][(j + start) * out->linesize[k] + i] = i;
}
memset(h->histogram, 0, 256 * sizeof(unsigned));
h->max_hval = 0;
}
break;
case MODE_WAVEFORM:
if (h->waveform_mode) {
for (i = 0; i < inlink->w; i++) {
for (j = 0; j < inlink->h; j++) {
int pos = in->data[0][j * in->linesize[0] + i] * out->linesize[0] + i;
unsigned value = out->data[0][pos];
value = FFMIN(value + h->step, 255);
out->data[0][pos] = value;
}
}
} else {
for (i = 0; i < inlink->h; i++) {
src = in ->data[0] + i * in ->linesize[0];
dst = out->data[0] + i * out->linesize[0];
for (j = 0; j < inlink->w; j++) {
int pos = src[j];
unsigned value = dst[pos];
value = FFMIN(value + h->step, 255);
dst[pos] = value;
}
}
}
break;
case MODE_COLOR:
for (i = 0; i < inlink->h; i++) {
int iw1 = i * in->linesize[1];
int iw2 = i * in->linesize[2];
for (j = 0; j < inlink->w; j++) {
int pos = in->data[1][iw1 + j] * out->linesize[0] + in->data[2][iw2 + j];
if (out->data[0][pos] < 255)
out->data[0][pos]++;
}
}
for (i = 0; i < 256; i++) {
dst = out->data[0] + i * out->linesize[0];
for (j = 0; j < 256; j++) {
if (!dst[j]) {
out->data[1][i * out->linesize[0] + j] = i;
out->data[2][i * out->linesize[0] + j] = j;
}
}
}
break;
case MODE_COLOR2:
for (i = 0; i < inlink->h; i++) {
int iw1 = i * in->linesize[1];
int iw2 = i * in->linesize[2];
for (j = 0; j < inlink->w; j++) {
int u = in->data[1][iw1 + j];
int v = in->data[2][iw2 + j];
int pos = u * out->linesize[0] + v;
if (!out->data[0][pos])
out->data[0][pos] = FFABS(128 - u) + FFABS(128 - v);
out->data[1][pos] = u;
out->data[2][pos] = v;
}
}
break;
default:
av_assert0(0);
}
ret = ff_filter_frame(outlink, out);
avfilter_unref_bufferp(&in);
if (ret < 0)
return ret;
return 0;
}
static av_cold void uninit(AVFilterContext *ctx)
{
HistogramContext *h = ctx->priv;
av_opt_free(h);
}
static const AVFilterPad inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.filter_frame = filter_frame,
.config_props = config_input,
.min_perms = AV_PERM_READ,
},
{ NULL }
};
static const AVFilterPad outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.config_props = config_output,
},
{ NULL }
};
AVFilter avfilter_vf_histogram = {
.name = "histogram",
.description = NULL_IF_CONFIG_SMALL("Compute and draw a histogram."),
.priv_size = sizeof(HistogramContext),
.init = init,
.uninit = uninit,
.query_formats = query_formats,
.inputs = inputs,
.outputs = outputs,
.priv_class = &histogram_class,
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment