LCOV - code coverage report
Current view: top level - libavfilter - vf_vmafmotion.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 129 0.0 %
Date: 2017-12-12 11:08:38 Functions: 0 15 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2017 Ronald S. Bultje <rsbultje@gmail.com>
       3             :  * Copyright (c) 2017 Ashish Pratap Singh <ashk43712@gmail.com>
       4             :  *
       5             :  * This file is part of FFmpeg.
       6             :  *
       7             :  * FFmpeg is free software; you can redistribute it and/or
       8             :  * modify it under the terms of the GNU Lesser General Public
       9             :  * License as published by the Free Software Foundation; either
      10             :  * version 2.1 of the License, or (at your option) any later version.
      11             :  *
      12             :  * FFmpeg is distributed in the hope that it will be useful,
      13             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15             :  * Lesser General Public License for more details.
      16             :  *
      17             :  * You should have received a copy of the GNU Lesser General Public
      18             :  * License along with FFmpeg; if not, write to the Free Software
      19             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      20             :  */
      21             : 
      22             : /**
      23             :  * @file
      24             :  * Calculate VMAF Motion score.
      25             :  */
      26             : 
      27             : #include "libavutil/opt.h"
      28             : #include "libavutil/pixdesc.h"
      29             : #include "avfilter.h"
      30             : #include "drawutils.h"
      31             : #include "formats.h"
      32             : #include "internal.h"
      33             : #include "vmaf_motion.h"
      34             : 
      35             : #define BIT_SHIFT 15
      36             : 
      37             : static const float FILTER_5[5] = {
      38             :     0.054488685,
      39             :     0.244201342,
      40             :     0.402619947,
      41             :     0.244201342,
      42             :     0.054488685
      43             : };
      44             : 
      45             : typedef struct VMAFMotionContext {
      46             :     const AVClass *class;
      47             :     VMAFMotionData data;
      48             :     FILE *stats_file;
      49             :     char *stats_file_str;
      50             : } VMAFMotionContext;
      51             : 
      52             : #define OFFSET(x) offsetof(VMAFMotionContext, x)
      53             : #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
      54             : 
      55             : static const AVOption vmafmotion_options[] = {
      56             :     {"stats_file", "Set file where to store per-frame difference information", OFFSET(stats_file_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
      57             :     { NULL }
      58             : };
      59             : 
      60             : AVFILTER_DEFINE_CLASS(vmafmotion);
      61             : 
      62           0 : static uint64_t image_sad(const uint16_t *img1, const uint16_t *img2, int w,
      63             :                           int h, ptrdiff_t _img1_stride, ptrdiff_t _img2_stride)
      64             : {
      65           0 :     ptrdiff_t img1_stride = _img1_stride / sizeof(*img1);
      66           0 :     ptrdiff_t img2_stride = _img2_stride / sizeof(*img2);
      67           0 :     uint64_t sum = 0;
      68             :     int i, j;
      69             : 
      70           0 :     for (i = 0; i < h; i++) {
      71           0 :         for (j = 0; j < w; j++) {
      72           0 :             sum += abs(img1[j] - img2[j]);
      73             :         }
      74           0 :         img1 += img1_stride;
      75           0 :         img2 += img2_stride;
      76             :     }
      77             : 
      78           0 :     return sum;
      79             : }
      80             : 
      81           0 : static void convolution_x(const uint16_t *filter, int filt_w, const uint16_t *src,
      82             :                           uint16_t *dst, int w, int h, ptrdiff_t _src_stride,
      83             :                           ptrdiff_t _dst_stride)
      84             : {
      85           0 :     ptrdiff_t src_stride = _src_stride / sizeof(*src);
      86           0 :     ptrdiff_t dst_stride = _dst_stride / sizeof(*dst);
      87           0 :     int radius = filt_w / 2;
      88           0 :     int borders_left = radius;
      89           0 :     int borders_right = w - (filt_w - radius);
      90             :     int i, j, k;
      91           0 :     int sum = 0;
      92             : 
      93           0 :     for (i = 0; i < h; i++) {
      94           0 :         for (j = 0; j < borders_left; j++) {
      95           0 :             sum = 0;
      96           0 :             for (k = 0; k < filt_w; k++) {
      97           0 :                 int j_tap = FFABS(j - radius + k);
      98           0 :                 if (j_tap >= w) {
      99           0 :                     j_tap = w - (j_tap - w + 1);
     100             :                 }
     101           0 :                 sum += filter[k] * src[i * src_stride + j_tap];
     102             :             }
     103           0 :             dst[i * dst_stride + j] = sum >> BIT_SHIFT;
     104             :         }
     105             : 
     106           0 :         for (j = borders_left; j < borders_right; j++) {
     107           0 :             int sum = 0;
     108           0 :             for (k = 0; k < filt_w; k++) {
     109           0 :                 sum += filter[k] * src[i * src_stride + j - radius + k];
     110             :             }
     111           0 :             dst[i * dst_stride + j] = sum >> BIT_SHIFT;
     112             :         }
     113             : 
     114           0 :         for (j = borders_right; j < w; j++) {
     115           0 :             sum = 0;
     116           0 :             for (k = 0; k < filt_w; k++) {
     117           0 :                 int j_tap = FFABS(j - radius + k);
     118           0 :                 if (j_tap >= w) {
     119           0 :                     j_tap = w - (j_tap - w + 1);
     120             :                 }
     121           0 :                 sum += filter[k] * src[i * src_stride + j_tap];
     122             :             }
     123           0 :             dst[i * dst_stride + j] = sum >> BIT_SHIFT;
     124             :         }
     125             :     }
     126           0 : }
     127             : 
     128             : #define conv_y_fn(type, bits) \
     129             : static void convolution_y_##bits##bit(const uint16_t *filter, int filt_w, \
     130             :                                       const uint8_t *_src, uint16_t *dst, \
     131             :                                       int w, int h, ptrdiff_t _src_stride, \
     132             :                                       ptrdiff_t _dst_stride) \
     133             : { \
     134             :     const type *src = (const type *) _src; \
     135             :     ptrdiff_t src_stride = _src_stride / sizeof(*src); \
     136             :     ptrdiff_t dst_stride = _dst_stride / sizeof(*dst); \
     137             :     int radius = filt_w / 2; \
     138             :     int borders_top = radius; \
     139             :     int borders_bottom = h - (filt_w - radius); \
     140             :     int i, j, k; \
     141             :     int sum = 0; \
     142             :     \
     143             :     for (i = 0; i < borders_top; i++) { \
     144             :         for (j = 0; j < w; j++) { \
     145             :             sum = 0; \
     146             :             for (k = 0; k < filt_w; k++) { \
     147             :                 int i_tap = FFABS(i - radius + k); \
     148             :                 if (i_tap >= h) { \
     149             :                     i_tap = h - (i_tap - h + 1); \
     150             :                 } \
     151             :                 sum += filter[k] * src[i_tap * src_stride + j]; \
     152             :             } \
     153             :             dst[i * dst_stride + j] = sum >> bits; \
     154             :         } \
     155             :     } \
     156             :     for (i = borders_top; i < borders_bottom; i++) { \
     157             :         for (j = 0; j < w; j++) { \
     158             :             sum = 0; \
     159             :             for (k = 0; k < filt_w; k++) { \
     160             :                 sum += filter[k] * src[(i - radius + k) * src_stride + j]; \
     161             :             } \
     162             :             dst[i * dst_stride + j] = sum >> bits; \
     163             :         } \
     164             :     } \
     165             :     for (i = borders_bottom; i < h; i++) { \
     166             :         for (j = 0; j < w; j++) { \
     167             :             sum = 0; \
     168             :             for (k = 0; k < filt_w; k++) { \
     169             :                 int i_tap = FFABS(i - radius + k); \
     170             :                 if (i_tap >= h) { \
     171             :                     i_tap = h - (i_tap - h + 1); \
     172             :                 } \
     173             :                 sum += filter[k] * src[i_tap * src_stride + j]; \
     174             :             } \
     175             :             dst[i * dst_stride + j] = sum >> bits; \
     176             :         } \
     177             :     } \
     178             : }
     179             : 
     180           0 : conv_y_fn(uint8_t, 8);
     181           0 : conv_y_fn(uint16_t, 10);
     182             : 
     183           0 : static void vmafmotiondsp_init(VMAFMotionDSPContext *dsp, int bpp) {
     184           0 :     dsp->convolution_x = convolution_x;
     185           0 :     dsp->convolution_y = bpp == 10 ? convolution_y_10bit : convolution_y_8bit;
     186           0 :     dsp->sad = image_sad;
     187           0 : }
     188             : 
     189           0 : double ff_vmafmotion_process(VMAFMotionData *s, AVFrame *ref)
     190             : {
     191             :     double score;
     192             : 
     193           0 :     s->vmafdsp.convolution_y(s->filter, 5, ref->data[0], s->temp_data,
     194           0 :                              s->width, s->height, ref->linesize[0], s->stride);
     195           0 :     s->vmafdsp.convolution_x(s->filter, 5, s->temp_data, s->blur_data[0],
     196             :                              s->width, s->height, s->stride, s->stride);
     197             : 
     198           0 :     if (!s->nb_frames) {
     199           0 :         score = 0.0;
     200             :     } else {
     201           0 :         uint64_t sad = s->vmafdsp.sad(s->blur_data[1], s->blur_data[0],
     202             :                                       s->width, s->height, s->stride, s->stride);
     203             :         // the output score is always normalized to 8 bits
     204           0 :         score = (double) (sad * 1.0 / (s->width * s->height << (BIT_SHIFT - 8)));
     205             :     }
     206             : 
     207           0 :     FFSWAP(uint16_t *, s->blur_data[0], s->blur_data[1]);
     208           0 :     s->nb_frames++;
     209           0 :     s->motion_sum += score;
     210             : 
     211           0 :     return score;
     212             : }
     213             : 
     214           0 : static void set_meta(AVDictionary **metadata, const char *key, float d)
     215             : {
     216             :     char value[128];
     217           0 :     snprintf(value, sizeof(value), "%0.2f", d);
     218           0 :     av_dict_set(metadata, key, value, 0);
     219           0 : }
     220             : 
     221           0 : static void do_vmafmotion(AVFilterContext *ctx, AVFrame *ref)
     222             : {
     223           0 :     VMAFMotionContext *s = ctx->priv;
     224             :     double score;
     225             : 
     226           0 :     score = ff_vmafmotion_process(&s->data, ref);
     227           0 :     set_meta(&ref->metadata, "lavfi.vmafmotion.score", score);
     228           0 :     if (s->stats_file) {
     229           0 :         fprintf(s->stats_file,
     230             :                 "n:%"PRId64" motion:%0.2lf\n", s->data.nb_frames, score);
     231             :     }
     232           0 : }
     233             : 
     234             : 
     235           0 : int ff_vmafmotion_init(VMAFMotionData *s,
     236             :                        int w, int h, enum AVPixelFormat fmt)
     237             : {
     238             :     size_t data_sz;
     239             :     int i;
     240           0 :     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
     241             : 
     242           0 :     s->width = w;
     243           0 :     s->height = h;
     244           0 :     s->stride = FFALIGN(w * sizeof(uint16_t), 32);
     245             : 
     246           0 :     data_sz = (size_t) s->stride * h;
     247           0 :     if (!(s->blur_data[0] = av_malloc(data_sz)) ||
     248           0 :         !(s->blur_data[1] = av_malloc(data_sz)) ||
     249           0 :         !(s->temp_data    = av_malloc(data_sz))) {
     250           0 :         return AVERROR(ENOMEM);
     251             :     }
     252             : 
     253           0 :     for (i = 0; i < 5; i++) {
     254           0 :         s->filter[i] = lrint(FILTER_5[i] * (1 << BIT_SHIFT));
     255             :     }
     256             : 
     257           0 :     vmafmotiondsp_init(&s->vmafdsp, desc->comp[0].depth);
     258             : 
     259           0 :     return 0;
     260             : }
     261             : 
     262           0 : static int query_formats(AVFilterContext *ctx)
     263             : {
     264           0 :     AVFilterFormats *fmts_list = NULL;
     265             :     int format, ret;
     266             : 
     267           0 :     for (format = 0; av_pix_fmt_desc_get(format); format++) {
     268           0 :         const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(format);
     269           0 :         if (!(desc->flags & (AV_PIX_FMT_FLAG_RGB | AV_PIX_FMT_FLAG_HWACCEL | AV_PIX_FMT_FLAG_BITSTREAM | AV_PIX_FMT_FLAG_PAL)) &&
     270           0 :             (desc->flags & AV_PIX_FMT_FLAG_PLANAR || desc->nb_components == 1) &&
     271           0 :             (!(desc->flags & AV_PIX_FMT_FLAG_BE) == !HAVE_BIGENDIAN || desc->comp[0].depth == 8) &&
     272           0 :             (desc->comp[0].depth == 8 || desc->comp[0].depth == 10) &&
     273           0 :             (ret = ff_add_format(&fmts_list, format)) < 0)
     274           0 :             return ret;
     275             :     }
     276             : 
     277           0 :     return ff_set_common_formats(ctx, fmts_list);
     278             : }
     279             : 
     280           0 : static int config_input_ref(AVFilterLink *inlink)
     281             : {
     282           0 :     AVFilterContext *ctx  = inlink->dst;
     283           0 :     VMAFMotionContext *s = ctx->priv;
     284             : 
     285           0 :     return ff_vmafmotion_init(&s->data, ctx->inputs[0]->w,
     286           0 :                               ctx->inputs[0]->h, ctx->inputs[0]->format);
     287             : }
     288             : 
     289           0 : double ff_vmafmotion_uninit(VMAFMotionData *s)
     290             : {
     291           0 :     av_free(s->blur_data[0]);
     292           0 :     av_free(s->blur_data[1]);
     293           0 :     av_free(s->temp_data);
     294             : 
     295           0 :     return s->nb_frames > 0 ? s->motion_sum / s->nb_frames : 0.0;
     296             : }
     297             : 
     298           0 : static int filter_frame(AVFilterLink *inlink, AVFrame *ref)
     299             : {
     300           0 :     AVFilterContext *ctx = inlink->dst;
     301           0 :     do_vmafmotion(ctx, ref);
     302           0 :     return ff_filter_frame(ctx->outputs[0], ref);
     303             : }
     304             : 
     305           0 : static av_cold int init(AVFilterContext *ctx)
     306             : {
     307           0 :     VMAFMotionContext *s = ctx->priv;
     308             : 
     309           0 :     if (s->stats_file_str) {
     310           0 :         if (!strcmp(s->stats_file_str, "-")) {
     311           0 :             s->stats_file = stdout;
     312             :         } else {
     313           0 :             s->stats_file = fopen(s->stats_file_str, "w");
     314           0 :             if (!s->stats_file) {
     315           0 :                 int err = AVERROR(errno);
     316             :                 char buf[128];
     317           0 :                 av_strerror(err, buf, sizeof(buf));
     318           0 :                 av_log(ctx, AV_LOG_ERROR, "Could not open stats file %s: %s\n",
     319             :                        s->stats_file_str, buf);
     320           0 :                 return err;
     321             :             }
     322             :         }
     323             :     }
     324             : 
     325           0 :     return 0;
     326             : }
     327             : 
     328           0 : static av_cold void uninit(AVFilterContext *ctx)
     329             : {
     330           0 :     VMAFMotionContext *s = ctx->priv;
     331           0 :     double avg_motion = ff_vmafmotion_uninit(&s->data);
     332             : 
     333           0 :     if (s->data.nb_frames > 0) {
     334           0 :         av_log(ctx, AV_LOG_INFO, "VMAF Motion avg: %.3f\n", avg_motion);
     335             :     }
     336             : 
     337           0 :     if (s->stats_file && s->stats_file != stdout)
     338           0 :         fclose(s->stats_file);
     339           0 : }
     340             : 
     341             : static const AVFilterPad vmafmotion_inputs[] = {
     342             :     {
     343             :         .name         = "reference",
     344             :         .type         = AVMEDIA_TYPE_VIDEO,
     345             :         .filter_frame = filter_frame,
     346             :         .config_props = config_input_ref,
     347             :     },
     348             :     { NULL }
     349             : };
     350             : 
     351             : static const AVFilterPad vmafmotion_outputs[] = {
     352             :     {
     353             :         .name          = "default",
     354             :         .type          = AVMEDIA_TYPE_VIDEO,
     355             :     },
     356             :     { NULL }
     357             : };
     358             : 
     359             : AVFilter ff_vf_vmafmotion = {
     360             :     .name          = "vmafmotion",
     361             :     .description   = NULL_IF_CONFIG_SMALL("Calculate the VMAF Motion score."),
     362             :     .init          = init,
     363             :     .uninit        = uninit,
     364             :     .query_formats = query_formats,
     365             :     .priv_size     = sizeof(VMAFMotionContext),
     366             :     .priv_class    = &vmafmotion_class,
     367             :     .inputs        = vmafmotion_inputs,
     368             :     .outputs       = vmafmotion_outputs,
     369             : };

Generated by: LCOV version 1.13