[FFmpeg-devel] [PATCH 2/2] avfilter/vf_histogram: improve waveform mode

Marton Balint cus at passwd.hu
Sat Sep 28 15:52:27 CEST 2013


On Sat, 28 Sep 2013, Paul B Mahol wrote:

> On 9/27/13, Marton Balint <cus at passwd.hu> wrote:
>> - faster
>
> How? Are there results and way to reproduce such findings?
>

Sure, here is one benchmark I just made:

time ./ffmpeg -i fate-suite/bink/hol2br.bik -vf \
format=yuv444p,histogram=mode=waveform -f null -

Old:
real	0m0.129s
user	0m0.109s
sys	0m0.019s

New:
real    0m0.110s
user    0m0.093s
sys     0m0.016s

>> - handles more pixel formats
>
> Well that it is not nice for output, as subsampled pixel format output would
> look worse.
>

I tried to keep the original design of the filter by keeping the input and 
the output pixel format the same.

Forcing the the output format to an unsubsampled format can be done, but 
if the source is subsampled, you may not have the resolution to do that 
without upscaling.

I think the speed benefit of not forcing a colorspace conversion (a 
software upscaling) and producing sensible output at the same time is 
great. The filter can actually now work realtime for me on my HD videos.

If anybody prefers the waveform of an upscaled chroma plain in full 
resolution, he can still do that by forcing the input format of the filter 
to yuv444p.

Look, if it were up to me, I'd only draw the waveform of a single plane 
but always IN AV_PIX_FMT_GRAY8 format with the proper resolution for the 
plane. But this would obviously mess up the current design of the 
histogram filter, so I had two chices. Make the best of the histogram 
filter, or create a separate waveform filter (which probably will never be 
accepted into ffmpeg because we already have a waveform filter).

>> - added parameter to mirror the waveform (high values are shown on top in
>> column mode)
>
> I do not like when bunch of unrelated changes are put into single patch.
> What is point of this mirror thing? Isn't there already filter(s) that do that?
>

Mirror mode ensures that high luminance/chrominance values are shown on 
the top of the waveform in column mode, which is I think how most consumer 
scopes work.

In parade mode you have to mirror the waveform of each plane individually, 
not the whole frame, so a simple flip filter won't work.

I can split the patch to two parts, if you prefer, but I thought since it 
adds minimal extra complexity and touches new code anyway, it is not 
necessary.

Regards,
Marton

>>
>> Signed-off-by: Marton Balint <cus at passwd.hu>
>> ---
>>  doc/filters.texi            |  6 +++
>>  libavfilter/version.h       |  2 +-
>>  libavfilter/vf_histogram.c  | 91
>> +++++++++++++++++++++++++++++++--------------
>>  tests/fate/filter-video.mak |  2 +-
>>  4 files changed, 71 insertions(+), 30 deletions(-)
>>
>> diff --git a/doc/filters.texi b/doc/filters.texi
>> index bd39495..dcc1f1a 100644
>> --- a/doc/filters.texi
>> +++ b/doc/filters.texi
>> @@ -4751,6 +4751,12 @@ Default value is @code{10}. Allowed range is [1,
>> 255].
>>  Set mode for @code{waveform}. Can be either @code{row}, or @code{column}.
>>  Default is @code{row}.
>>
>> + at item waveform_mirror
>> +Set mirroring mode for @code{waveform}. @code{0} means unmirrored,
>> @code{1}
>> +means mirrored. In mirrored mode, higher values will be represented on the
>> left
>> +side for @code{row} mode and at the top for @code{column} mode. Default is
>> + at code{0} (unmirrored).
>> +
>>  @item display_mode
>>  Set display mode for @code{waveform} and @code{levels}.
>>  It accepts the following values:
>> diff --git a/libavfilter/version.h b/libavfilter/version.h
>> index 03752d5..4e496a2 100644
>> --- a/libavfilter/version.h
>> +++ b/libavfilter/version.h
>> @@ -30,7 +30,7 @@
>>  #include "libavutil/avutil.h"
>>
>>  #define LIBAVFILTER_VERSION_MAJOR  3
>> -#define LIBAVFILTER_VERSION_MINOR  87
>> +#define LIBAVFILTER_VERSION_MINOR  88
>>  #define LIBAVFILTER_VERSION_MICRO 100
>>
>>  #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR,
>> \
>> diff --git a/libavfilter/vf_histogram.c b/libavfilter/vf_histogram.c
>> index 1dd0b2d..8181f96 100644
>> --- a/libavfilter/vf_histogram.c
>> +++ b/libavfilter/vf_histogram.c
>> @@ -46,6 +46,7 @@ typedef struct HistogramContext {
>>      int            scale_height;
>>      int            step;
>>      int            waveform_mode;
>> +    int            waveform_mirror;
>>      int            display_mode;
>>      int            levels_mode;
>>  } HistogramContext;
>> @@ -65,6 +66,7 @@ static const AVOption histogram_options[] = {
>>      { "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" },
>> +    { "waveform_mirror", "set waveform mirroring", OFFSET(waveform_mirror),
>> AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "waveform_mirror"},
>>      { "display_mode", "set display mode", OFFSET(display_mode),
>> AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "display_mode"},
>>      { "parade",  NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS,
>> "display_mode" },
>>      { "overlay", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS,
>> "display_mode" },
>> @@ -86,6 +88,15 @@ static const enum AVPixelFormat levels_pix_fmts[] = {
>>      AV_PIX_FMT_GRAY8, AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_NONE
>>  };
>>
>> +static const enum AVPixelFormat waveform_pix_fmts[] = {
>> +     AV_PIX_FMT_GBRP,       AV_PIX_FMT_GBRAP,
>> +     AV_PIX_FMT_YUV420P,    AV_PIX_FMT_YUV422P,    AV_PIX_FMT_YUV444P,
>> AV_PIX_FMT_YUV410P,
>> +     AV_PIX_FMT_YUV411P,    AV_PIX_FMT_YUVJ411P,   AV_PIX_FMT_GRAY8,
>> AV_PIX_FMT_YUVJ420P,
>> +     AV_PIX_FMT_YUVJ422P,   AV_PIX_FMT_YUVJ444P,   AV_PIX_FMT_YUV440P,
>> AV_PIX_FMT_YUVJ440P,
>> +     AV_PIX_FMT_YUVA420P,   AV_PIX_FMT_YUVA422P,   AV_PIX_FMT_YUVA444P,
>> AV_PIX_FMT_GRAY8A,
>> +     AV_PIX_FMT_NONE
>> +};
>> +
>>  static int query_formats(AVFilterContext *ctx)
>>  {
>>      HistogramContext *h = ctx->priv;
>> @@ -93,6 +104,8 @@ static int query_formats(AVFilterContext *ctx)
>>
>>      switch (h->mode) {
>>      case MODE_WAVEFORM:
>> +        pix_fmts = waveform_pix_fmts;
>> +        break;
>>      case MODE_LEVELS:
>>          pix_fmts = levels_pix_fmts;
>>          break;
>> @@ -164,6 +177,53 @@ static int config_output(AVFilterLink *outlink)
>>      return 0;
>>  }
>>
>> +static void gen_waveform(int component, int intensity, int mirror, int
>> offset, int col_mode, AVFrame *inpicref, AVFrame *outpicref)
>> +{
>> +    int y;
>> +    uint8_t *p;
>> +    const AVPixFmtDescriptor *desc =
>> av_pix_fmt_desc_get(inpicref->format);
>> +    const int is_chroma = (component == 1 || component == 2);
>> +    const int shift_w = (is_chroma ? desc->log2_chroma_w : 0);
>> +    const int shift_h = (is_chroma ? desc->log2_chroma_h : 0);
>> +    const int src_h = inpicref->height >> shift_h;
>> +    const int src_w = inpicref->width >> shift_w;
>> +    uint8_t *src_data = inpicref->data[desc->comp[component].plane];
>> +    const int src_linesize =
>> inpicref->linesize[desc->comp[component].plane];
>> +    const int dst_linesize =
>> outpicref->linesize[desc->comp[component].plane];
>> +    const int dst_signed_linesize = dst_linesize * (mirror == 1 ? -1 : 1);
>> +    uint8_t *dst_data = outpicref->data[desc->comp[component].plane] +
>> (col_mode ? ((offset >> shift_h) * dst_linesize) : (offset >> shift_w));
>> +    uint8_t * const dst_bottom_line = dst_data + dst_linesize * ((256 >>
>> shift_h) - 1);
>> +    uint8_t * const dst_line = (mirror ? dst_bottom_line : dst_data);
>> +    const uint8_t max = 255 - intensity;
>> +    uint8_t *dst;
>> +
>> +    if (!col_mode && mirror)
>> +        dst_data += 256 >> shift_w;
>> +    for (y = 0; y < src_h; y++) {
>> +        uint8_t *src_data_end = src_data + src_w;
>> +        dst = dst_line;
>> +        for (p = src_data; p < src_data_end; p++) {
>> +            uint8_t *target;
>> +            if (col_mode) {
>> +                target = dst++ + dst_signed_linesize * (*p >> shift_h);
>> +            } else {
>> +                if (mirror) {
>> +                    target = dst_data - (*p >> shift_w);
>> +                } else {
>> +                    target = dst_data + (*p >> shift_w);
>> +                }
>> +            }
>> +            if (*target <= max)
>> +                *target += intensity;
>> +            else
>> +                *target = 255;
>> +        }
>> +        src_data += src_linesize;
>> +        dst_data += dst_linesize;
>> +    }
>> +}
>> +
>> +
>>  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
>>  {
>>      HistogramContext *h   = inlink->dst->priv;
>> @@ -232,34 +292,9 @@ static int filter_frame(AVFilterLink *inlink, AVFrame
>> *in)
>>          }
>>          break;
>>      case MODE_WAVEFORM:
>> -        if (h->waveform_mode) {
>> -            for (k = 0; k < h->ncomp; k++) {
>> -                int offset = k * 256 * h->display_mode;
>> -                for (i = 0; i < inlink->w; i++) {
>> -                    for (j = 0; j < inlink->h; j++) {
>> -                        int pos = (offset +
>> -                                   in->data[k][j * in->linesize[k] + i]) *
>> -                                  out->linesize[k] + i;
>> -                        unsigned value = out->data[k][pos];
>> -                        value = FFMIN(value + h->step, 255);
>> -                        out->data[k][pos] = value;
>> -                    }
>> -                }
>> -            }
>> -        } else {
>> -            for (k = 0; k < h->ncomp; k++) {
>> -                int offset = k * 256 * h->display_mode;
>> -                for (i = 0; i < inlink->h; i++) {
>> -                    src = in ->data[k] + i * in ->linesize[k];
>> -                    dst = out->data[k] + i * out->linesize[k];
>> -                    for (j = 0; j < inlink->w; j++) {
>> -                        int pos = src[j] + offset;
>> -                        unsigned value = dst[pos];
>> -                        value = FFMIN(value + h->step, 255);
>> -                        dst[pos] = value;
>> -                    }
>> -                }
>> -            }
>> +        for (k = 0; k < h->ncomp; k++) {
>> +            int offset = k * 256 * h->display_mode;
>> +            gen_waveform(k, h->step, h->waveform_mirror, offset,
>> h->waveform_mode, in, out);
>>          }
>>          break;
>>      case MODE_COLOR:
>> diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak
>> index 9b9acf7..f257b68 100644
>> --- a/tests/fate/filter-video.mak
>> +++ b/tests/fate/filter-video.mak
>> @@ -57,7 +57,7 @@ FATE_FILTER_VSYNTH-$(CONFIG_HISTOGRAM_FILTER) +=
>> fate-filter-histogram-levels
>>  fate-filter-histogram-levels: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf
>> histogram -flags +bitexact -sws_flags +accurate_rnd+bitexact
>>
>>  FATE_FILTER_VSYNTH-$(CONFIG_HISTOGRAM_FILTER) +=
>> fate-filter-histogram-waveform
>> -fate-filter-histogram-waveform: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf
>> histogram=mode=waveform -flags +bitexact -sws_flags +accurate_rnd+bitexact
>> +fate-filter-histogram-waveform: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf
>> format=yuv444p,histogram=mode=waveform -flags +bitexact -sws_flags
>> +accurate_rnd+bitexact
>>
>>  FATE_FILTER_VSYNTH-$(CONFIG_OVERLAY_FILTER) += fate-filter-overlay
>>  fate-filter-overlay: tests/data/filtergraphs/overlay
>> --
>> 1.8.1.4
>>
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel at ffmpeg.org
>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>


More information about the ffmpeg-devel mailing list