[FFmpeg-devel] [PATCH] lavfi: add thumb video filter.

Michael Niedermayer michaelni at gmx.at
Wed Nov 30 06:05:39 CET 2011


On Tue, Nov 29, 2011 at 05:07:09PM +0100, Clément Bœsch wrote:
> From: Clément Bœsch <clement.boesch at smartjog.com>
> 
> ---
> Hi,
> 
> This is somewhat a proof of concept for smart thumbnail selection with the video
> filters. The algorithm might not be the best but raise interesting results (see
> doxy in the main file for reference).
>
> I'm not sure the way frames are buffered is the best (actually it isn't for
> sure), but I have nothing better to propose right now; and I'm still confused
> with the filters API...
[...]
> +static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
> +{
> +    int i, j;
> +    const uint8_t *p;
> +    AVFilterContext *ctx = inlink->dst;
> +    ThumbContext *thumb = ctx->priv;
> +    int *hist = thumb->histogram + thumb->n * HIST_SZ;
> +    AVFilterBufferRef *picref = inlink->cur_buf;
> +
> +    p = picref->data[0] + y * picref->linesize[0];
> +    for (j = 0; j < h; j++) {
> +        for (i = 0; i < inlink->w; i++) {
> +            hist[0*256 + p[i*3    ]]++;
> +            hist[1*256 + p[i*3 + 1]]++;
> +            hist[2*256 + p[i*3 + 2]]++;
> +        }
> +        p += picref->linesize[0];
> +    }

> +    avfilter_default_draw_slice(inlink, y, h, slice_dir);

this should not be here
it should be called from end frame when the correct frame has been
selected


> +}
> +
> +/**
> + * @brief        compute Root-mean-square deviation to estimate "closeness"
> + * @param hist   color distribution histogram
> + * @param median average color distribution histogram
> + * @return       root mean squared error
> + */
> +static float frame_rmse(const int *hist, const float *median)
> +{
> +    int i;
> +    float err, mean_sq_err = 0;
> +    for (i = 0; i < HIST_SZ; i++) {
> +        err = median[i] - (float)hist[i];
> +        mean_sq_err += err*err / HIST_SZ;
> +    }
> +    return sqrtf(mean_sq_err);
> +}
> +
> +static void end_frame(AVFilterLink *inlink)
> +{
> +    int i, j;
> +    float avg[HIST_SZ] = {0}, rmse, min_rmse = -1;
> +    int best_frame = 0;
> +    ThumbContext *thumb = inlink->dst->priv;
> +
> +    // keep a reference of each frame
> +    thumb->frames[thumb->n] = avfilter_ref_buffer(inlink->cur_buf, AV_PERM_READ);
> +
> +    // no selection until the buffer of N frames is filled up
> +    if (thumb->n < thumb->n_frames - 1) {
> +        thumb->n++;
> +        return;
> +    }
> +
> +    // average histogram of the N frames
> +    for (j = 0; j < FF_ARRAY_ELEMS(avg); j++)
> +        for (i = 0; i < thumb->n_frames; i++)
> +            avg[j] += (float)thumb->histogram[i*HIST_SZ + j] / thumb->n_frames;
> +
> +    // find the frame closer to the average using RMSE
> +    for (i = 0; i < thumb->n_frames; i++) {
> +        rmse = frame_rmse(&thumb->histogram[i*HIST_SZ], avg);
> +        if (i == 0 || rmse < min_rmse)
> +            best_frame = i, min_rmse = rmse;
> +    }
> +
> +    // free and reset everything (except the best frame buffer)
> +    for (i = 0; i < thumb->n_frames; i++) {
> +        if (i == best_frame)
> +            continue;
> +        avfilter_unref_buffer(thumb->frames[i]);
> +        thumb->frames[i] = NULL;
> +    }
> +    memset(thumb->histogram, 0, HIST_SZ * thumb->n_frames);
> +    thumb->n = 0;
> +

> +    // raise the chosen one
> +    inlink->cur_buf = thumb->frames[best_frame];
> +    avfilter_default_end_frame(inlink);

this looks wrong, does it even return the correct frame ?

the code here should call avfilter_start_frame / draw_slice/end_frame



> +}
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> +    ThumbContext *thumb = ctx->priv;
> +    av_freep(&thumb->histogram);
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> +    static const enum PixelFormat pix_fmts[] = {
> +        PIX_FMT_RGB24, PIX_FMT_BGR24,
> +        PIX_FMT_NONE
> +    };
> +    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
> +    return 0;
> +}
> +
> +AVFilter avfilter_vf_thumb = {
> +    .name          = "thumb",
> +    .description   = NULL_IF_CONFIG_SMALL("Thumbnail selection filter"),
> +    .priv_size     = sizeof(ThumbContext),
> +    .init          = init,
> +    .uninit        = uninit,
> +    .query_formats = query_formats,
> +    .inputs    = (const AVFilterPad[])  {{  .name             = "default",
> +                                            .type             = AVMEDIA_TYPE_VIDEO,
> +                                            .get_video_buffer = avfilter_null_get_video_buffer,

> +                                            .start_frame      = avfilter_null_start_frame,

avfilter_start_frame can not be propagated, you need a really empty
function. 

                                                  
> +                                            .draw_slice       = draw_slice,
> +                                            .end_frame        = end_frame,
> +                                        },{ .name = NULL }},

AV_PERM_PRESERVE is missing


[...]
-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

While the State exists there can be no freedom; when there is freedom there
will be no State. -- Vladimir Lenin
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20111130/7d49b01b/attachment.asc>


More information about the ffmpeg-devel mailing list