FFmpeg
vf_ssim360.c
Go to the documentation of this file.
1 /**
2  * Copyright (c) 2015-2021, Facebook, Inc.
3  * All rights reserved.
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 /* Computes the Structural Similarity Metric between two 360 video streams.
23  * original SSIM algorithm:
24  * Z. Wang, A. C. Bovik, H. R. Sheikh and E. P. Simoncelli,
25  * "Image quality assessment: From error visibility to structural similarity,"
26  * IEEE Transactions on Image Processing, vol. 13, no. 4, pp. 600-612, Apr. 2004.
27  *
28  * To improve speed, this implementation uses the standard approximation of
29  * overlapped 8x8 block sums, rather than the original gaussian weights.
30  *
31  * To address warping from 360 projections for videos with same
32  * projection and resolution, the 8x8 blocks sampled are weighted by
33  * their location in the image.
34  *
35  * To apply SSIM across projections and video sizes, we render the video on to
36  * a flat "tape" from which the 8x8 are selected and compared.
37  */
38 
39 /*
40  * @file
41  * Calculate the SSIM between two input 360 videos.
42  */
43 
44 #include <math.h>
45 
46 #include "libavutil/avstring.h"
47 #include "libavutil/file_open.h"
48 #include "libavutil/mem.h"
49 #include "libavutil/opt.h"
50 #include "libavutil/pixdesc.h"
51 
52 #include "avfilter.h"
53 #include "drawutils.h"
54 #include "filters.h"
55 #include "framesync.h"
56 
57 #define RIGHT 0
58 #define LEFT 1
59 #define TOP 2
60 #define BOTTOM 3
61 #define FRONT 4
62 #define BACK 5
63 
64 #define DEFAULT_HEATMAP_W 32
65 #define DEFAULT_HEATMAP_H 16
66 
67 #define M_PI_F ((float)M_PI)
68 #define M_PI_2_F ((float)M_PI_2)
69 #define M_PI_4_F ((float)M_PI_4)
70 #define M_SQRT2_F ((float)M_SQRT2)
71 
72 #define DEFAULT_EXPANSION_COEF 1.01f
73 
74 #define BARREL_THETA_RANGE (DEFAULT_EXPANSION_COEF * 2.0f * M_PI_F)
75 #define BARREL_PHI_RANGE (DEFAULT_EXPANSION_COEF * M_PI_2_F)
76 
77 // Use fixed-point with 16 bit precision for fast bilinear math
78 #define FIXED_POINT_PRECISION 16
79 
80 // Use 1MB per channel for the histogram to get 5-digit precise SSIM value
81 #define SSIM360_HIST_SIZE 131072
82 
83 // The last number is a marker < 0 to mark end of list
84 static const double PERCENTILE_LIST[] = {
85  1.0, 0.9, 0.8, 0.7, 0.6,
86  0.5, 0.4, 0.3, 0.2, 0.1, 0, -1
87 };
88 
89 typedef enum StereoFormat {
94 } StereoFormat;
95 
96 typedef enum Projection {
103 } Projection;
104 
105 typedef struct Map2D {
106  int w, h;
107  double *value;
108 } Map2D;
109 
110 typedef struct HeatmapList {
112  struct HeatmapList *next;
113 } HeatmapList;
114 
115 typedef struct SampleParams {
116  int stride;
124  float expand_coef;
125 } SampleParams;
126 
127 typedef struct BilinearMap {
128  // Indices to the 4 samples to compute bilinear
129  int tli;
130  int tri;
131  int bli;
132  int bri;
133 
134  // Fixed point factors with which the above 4 sample vector's
135  // dot product needs to be computed for the final bilinear value
136  int tlf;
137  int trf;
138  int blf;
139  int brf;
140 } BilinearMap;
141 
142 typedef struct SSIM360Context {
143  const AVClass *class;
144 
146  // Stats file configuration
147  FILE *stats_file;
149 
150  // Component properties
152  double coefs[4];
153  char comps[4];
154  int max;
155 
156  // Channel configuration & properties
158 
159  int is_rgb;
160  uint8_t rgba_map[4];
161 
162  // Standard SSIM computation configuration & workspace
164 
165  int *temp;
166  uint64_t nb_ssim_frames;
167  uint64_t nb_net_frames;
168  double ssim360[4], ssim360_total;
169  double *ssim360_hist[4];
170  double ssim360_hist_net[4];
171  double ssim360_percentile_sum[4][256];
172 
173  // 360 projection configuration & workspace
178  float ref_pad;
179  float main_pad;
180  int use_tape;
181  char *heatmap_str;
184 
191  int tape_length[4];
194  float angular_resolution[4][2];
196  uint8_t *main, int main_stride,
197  uint8_t *ref, int ref_stride,
198  int width, int height, void *temp,
199  int max, Map2D density);
201 
202 #define OFFSET(x) offsetof(SSIM360Context, x)
203 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
204 
205 static const AVOption ssim360_options[] = {
206  { "stats_file", "Set file where to store per-frame difference information",
207  OFFSET(stats_file_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
208  { "f", "Set file where to store per-frame difference information",
209  OFFSET(stats_file_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
210 
211  { "compute_chroma",
212  "Specifies if non-luma channels must be computed",
213  OFFSET(compute_chroma), AV_OPT_TYPE_INT, {.i64 = 1},
214  0, 1, .flags = FLAGS },
215 
216  { "frame_skip_ratio",
217  "Specifies the number of frames to be skipped from evaluation, for every evaluated frame",
218  OFFSET(frame_skip_ratio), AV_OPT_TYPE_INT, {.i64 = 0},
219  0, 1000000, .flags = FLAGS },
220 
221  { "ref_projection", "projection of the reference video",
222  OFFSET(ref_projection), AV_OPT_TYPE_INT, {.i64 = PROJECTION_EQUIRECT},
223  0, PROJECTION_N - 1, .flags = FLAGS, .unit = "projection" },
224 
225  { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_EQUIRECT}, 0, 0, FLAGS, .unit = "projection" },
226  { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_EQUIRECT}, 0, 0, FLAGS, .unit = "projection" },
227  { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_CUBEMAP32}, 0, 0, FLAGS, .unit = "projection" },
228  { "c2x3", "cubemap 2x3", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_CUBEMAP23}, 0, 0, FLAGS, .unit = "projection" },
229  { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_BARREL}, 0, 0, FLAGS, .unit = "projection" },
230  { "barrelsplit", "barrel split facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_BARREL_SPLIT}, 0, 0, FLAGS, .unit = "projection" },
231 
232  { "main_projection", "projection of the main video",
233  OFFSET(main_projection), AV_OPT_TYPE_INT, {.i64 = PROJECTION_N},
234  0, PROJECTION_N, .flags = FLAGS, .unit = "projection" },
235 
236  { "ref_stereo", "stereo format of the reference video",
237  OFFSET(ref_stereo_format), AV_OPT_TYPE_INT, {.i64 = STEREO_FORMAT_MONO},
238  0, STEREO_FORMAT_N - 1, .flags = FLAGS, .unit = "stereo_format" },
239 
240  { "mono", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = STEREO_FORMAT_MONO }, 0, 0, FLAGS, .unit = "stereo_format" },
241  { "tb", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = STEREO_FORMAT_TB }, 0, 0, FLAGS, .unit = "stereo_format" },
242  { "lr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = STEREO_FORMAT_LR }, 0, 0, FLAGS, .unit = "stereo_format" },
243 
244  { "main_stereo", "stereo format of main video",
245  OFFSET(main_stereo_format), AV_OPT_TYPE_INT, {.i64 = STEREO_FORMAT_N},
246  0, STEREO_FORMAT_N, .flags = FLAGS, .unit = "stereo_format" },
247 
248  { "ref_pad",
249  "Expansion (padding) coefficient for each cube face of the reference video",
250  OFFSET(ref_pad), AV_OPT_TYPE_FLOAT, {.dbl = .0f}, 0, 10, .flags = FLAGS },
251 
252  { "main_pad",
253  "Expansion (padding) coefficient for each cube face of the main video",
254  OFFSET(main_pad), AV_OPT_TYPE_FLOAT, {.dbl = .0f}, 0, 10, .flags = FLAGS },
255 
256  { "use_tape",
257  "Specifies if the tape based SSIM 360 algorithm must be used independent of the input video types",
258  OFFSET(use_tape), AV_OPT_TYPE_INT, {.i64 = 0},
259  0, 1, .flags = FLAGS },
260 
261  { "heatmap_str",
262  "Heatmap data for view-based evaluation. For heatmap file format, please refer to EntSphericalVideoHeatmapData.",
263  OFFSET(heatmap_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, .flags = FLAGS },
264 
265  { "default_heatmap_width",
266  "Default heatmap dimension. Will be used when dimension is not specified in heatmap data.",
267  OFFSET(default_heatmap_w), AV_OPT_TYPE_INT, {.i64 = 32}, 1, 4096, .flags = FLAGS },
268 
269  { "default_heatmap_height",
270  "Default heatmap dimension. Will be used when dimension is not specified in heatmap data.",
271  OFFSET(default_heatmap_h), AV_OPT_TYPE_INT, {.i64 = 16}, 1, 4096, .flags = FLAGS },
272 
273  { NULL }
274 };
275 
277 
278 static void set_meta(AVDictionary **metadata, const char *key, char comp, float d)
279 {
280  char value[128];
281  snprintf(value, sizeof(value), "%0.2f", d);
282  if (comp) {
283  char key2[128];
284  snprintf(key2, sizeof(key2), "%s%c", key, comp);
285  av_dict_set(metadata, key2, value, 0);
286  } else {
288  }
289 }
290 
291 static void map_uninit(Map2D *map)
292 {
293  av_freep(&map->value);
294 }
295 
296 static int map_init(Map2D *map, int w, int h)
297 {
298  map->value = av_calloc(h * w, sizeof(*map->value));
299  if (!map->value)
300  return AVERROR(ENOMEM);
301 
302  map->h = h;
303  map->w = w;
304 
305  return 0;
306 }
307 
308 static void map_list_free(HeatmapList **pl)
309 {
310  HeatmapList *l = *pl;
311 
312  while (l) {
313  HeatmapList *next = l->next;
314  map_uninit(&l->map);
315  av_freep(&l);
316  l = next;
317  }
318 
319  *pl = NULL;
320 }
321 
322 static int map_alloc(HeatmapList **pl, int w, int h)
323 {
324  HeatmapList *l;
325  int ret;
326 
327  l = av_mallocz(sizeof(*l));
328  if (!l)
329  return AVERROR(ENOMEM);
330 
331  ret = map_init(&l->map, w, h);
332  if (ret < 0) {
333  av_freep(&l);
334  return ret;
335  }
336 
337  *pl = l;
338  return 0;
339 }
340 
341 static void
342 ssim360_4x4xn_16bit(const uint8_t *main8, ptrdiff_t main_stride,
343  const uint8_t *ref8, ptrdiff_t ref_stride,
344  int64_t (*sums)[4], int width)
345 {
346  const uint16_t *main16 = (const uint16_t *)main8;
347  const uint16_t *ref16 = (const uint16_t *)ref8;
348 
349  main_stride >>= 1;
350  ref_stride >>= 1;
351 
352  for (int z = 0; z < width; z++) {
353  uint64_t s1 = 0, s2 = 0, ss = 0, s12 = 0;
354 
355  for (int y = 0; y < 4; y++) {
356  for (int x = 0; x < 4; x++) {
357  unsigned a = main16[x + y * main_stride];
358  unsigned b = ref16[x + y * ref_stride];
359 
360  s1 += a;
361  s2 += b;
362  ss += a*a;
363  ss += b*b;
364  s12 += a*b;
365  }
366  }
367 
368  sums[z][0] = s1;
369  sums[z][1] = s2;
370  sums[z][2] = ss;
371  sums[z][3] = s12;
372  main16 += 4;
373  ref16 += 4;
374  }
375 }
376 
377 static void
378 ssim360_4x4xn_8bit(const uint8_t *main, ptrdiff_t main_stride,
379  const uint8_t *ref, ptrdiff_t ref_stride,
380  int (*sums)[4], int width)
381 {
382  for (int z = 0; z < width; z++) {
383  uint32_t s1 = 0, s2 = 0, ss = 0, s12 = 0;
384 
385  for (int y = 0; y < 4; y++) {
386  for (int x = 0; x < 4; x++) {
387  int a = main[x + y * main_stride];
388  int b = ref[x + y * ref_stride];
389 
390  s1 += a;
391  s2 += b;
392  ss += a*a;
393  ss += b*b;
394  s12 += a*b;
395  }
396  }
397 
398  sums[z][0] = s1;
399  sums[z][1] = s2;
400  sums[z][2] = ss;
401  sums[z][3] = s12;
402  main += 4;
403  ref += 4;
404  }
405 }
406 
407 static float ssim360_end1x(int64_t s1, int64_t s2, int64_t ss, int64_t s12, int max)
408 {
409  int64_t ssim_c1 = (int64_t)(.01 * .01 * max * max * 64 + .5);
410  int64_t ssim_c2 = (int64_t)(.03 * .03 * max * max * 64 * 63 + .5);
411 
412  int64_t fs1 = s1;
413  int64_t fs2 = s2;
414  int64_t fss = ss;
415  int64_t fs12 = s12;
416  int64_t vars = fss * 64 - fs1 * fs1 - fs2 * fs2;
417  int64_t covar = fs12 * 64 - fs1 * fs2;
418 
419  return (float)(2 * fs1 * fs2 + ssim_c1) * (float)(2 * covar + ssim_c2)
420  / ((float)(fs1 * fs1 + fs2 * fs2 + ssim_c1) * (float)(vars + ssim_c2));
421 }
422 
423 static float ssim360_end1(int s1, int s2, int ss, int s12)
424 {
425  static const int ssim_c1 = (int)(.01*.01*255*255*64 + .5);
426  static const int ssim_c2 = (int)(.03*.03*255*255*64*63 + .5);
427 
428  int fs1 = s1;
429  int fs2 = s2;
430  int fss = ss;
431  int fs12 = s12;
432  int vars = fss * 64 - fs1 * fs1 - fs2 * fs2;
433  int covar = fs12 * 64 - fs1 * fs2;
434 
435  return (float)(2 * fs1 * fs2 + ssim_c1) * (float)(2 * covar + ssim_c2)
436  / ((float)(fs1 * fs1 + fs2 * fs2 + ssim_c1) * (float)(vars + ssim_c2));
437 }
438 
439 static double
440 ssim360_endn_16bit(const int64_t (*sum0)[4], const int64_t (*sum1)[4],
441  int width, int max,
442  double *density_map, int map_width, double *total_weight)
443 {
444  double ssim360 = 0.0, weight;
445 
446  for (int i = 0; i < width; i++) {
447  weight = density_map ? density_map[(int) ((0.5 + i) / width * map_width)] : 1.0;
448  ssim360 += weight * ssim360_end1x(
449  sum0[i][0] + sum0[i + 1][0] + sum1[i][0] + sum1[i + 1][0],
450  sum0[i][1] + sum0[i + 1][1] + sum1[i][1] + sum1[i + 1][1],
451  sum0[i][2] + sum0[i + 1][2] + sum1[i][2] + sum1[i + 1][2],
452  sum0[i][3] + sum0[i + 1][3] + sum1[i][3] + sum1[i + 1][3],
453  max);
454  *total_weight += weight;
455  }
456  return ssim360;
457 }
458 
459 static double
460 ssim360_endn_8bit(const int (*sum0)[4], const int (*sum1)[4], int width,
461  double *density_map, int map_width, double *total_weight)
462 {
463  double ssim360 = 0.0, weight;
464 
465  for (int i = 0; i < width; i++) {
466  weight = density_map ? density_map[(int) ((0.5 + i) / width * map_width)] : 1.0;
467  ssim360 += weight * ssim360_end1(
468  sum0[i][0] + sum0[i + 1][0] + sum1[i][0] + sum1[i + 1][0],
469  sum0[i][1] + sum0[i + 1][1] + sum1[i][1] + sum1[i + 1][1],
470  sum0[i][2] + sum0[i + 1][2] + sum1[i][2] + sum1[i + 1][2],
471  sum0[i][3] + sum0[i + 1][3] + sum1[i][3] + sum1[i + 1][3]);
472  *total_weight += weight;
473  }
474  return ssim360;
475 }
476 
477 static double
478 ssim360_plane_16bit(uint8_t *main, int main_stride,
479  uint8_t *ref, int ref_stride,
480  int width, int height, void *temp,
481  int max, Map2D density)
482 {
483  int z = 0;
484  double ssim360 = 0.0;
485  int64_t (*sum0)[4] = temp;
486  int64_t (*sum1)[4] = sum0 + (width >> 2) + 3;
487  double total_weight = 0.0;
488 
489  width >>= 2;
490  height >>= 2;
491 
492  for (int y = 1; y < height; y++) {
493  for (; z <= y; z++) {
494  FFSWAP(void*, sum0, sum1);
495  ssim360_4x4xn_16bit(&main[4 * z * main_stride], main_stride,
496  &ref[4 * z * ref_stride], ref_stride,
497  sum0, width);
498  }
499  ssim360 += ssim360_endn_16bit(
500  (const int64_t (*)[4])sum0, (const int64_t (*)[4])sum1,
501  width - 1, max,
502  density.value ? density.value + density.w * ((int) ((z - 1.0) / height * density.h)) : NULL,
503  density.w, &total_weight);
504  }
505 
506  return (double) (ssim360 / total_weight);
507 }
508 
509 static double
510 ssim360_plane_8bit(uint8_t *main, int main_stride,
511  uint8_t *ref, int ref_stride,
512  int width, int height, void *temp,
513  int max, Map2D density)
514 {
515  int z = 0;
516  double ssim360 = 0.0;
517  int (*sum0)[4] = temp;
518  int (*sum1)[4] = sum0 + (width >> 2) + 3;
519  double total_weight = 0.0;
520 
521  width >>= 2;
522  height >>= 2;
523 
524  for (int y = 1; y < height; y++) {
525  for (; z <= y; z++) {
526  FFSWAP(void*, sum0, sum1);
528  &main[4 * z * main_stride], main_stride,
529  &ref[4 * z * ref_stride], ref_stride,
530  sum0, width);
531  }
532  ssim360 += ssim360_endn_8bit(
533  (const int (*)[4])sum0, (const int (*)[4])sum1, width - 1,
534  density.value ? density.value + density.w * ((int) ((z - 1.0) / height * density.h)) : NULL,
535  density.w, &total_weight);
536  }
537 
538  return (double) (ssim360 / total_weight);
539 }
540 
541 static double ssim360_db(double ssim360, double weight)
542 {
543  return 10 * log10(weight / (weight - ssim360));
544 }
545 
546 static int get_bilinear_sample(const uint8_t *data, BilinearMap *m, int max_value)
547 {
548  static const int fixed_point_half = 1 << (FIXED_POINT_PRECISION - 1);
549  static const int inv_byte_mask = UINT_MAX << 8;
550 
551  int tl, tr, bl, br, v;
552 
553  if (max_value & inv_byte_mask) {
554  uint16_t *data16 = (uint16_t *)data;
555  tl = data16[m->tli];
556  tr = data16[m->tri];
557  bl = data16[m->bli];
558  br = data16[m->bri];
559  } else {
560  tl = data[m->tli];
561  tr = data[m->tri];
562  bl = data[m->bli];
563  br = data[m->bri];
564  }
565 
566  v = m->tlf * tl +
567  m->trf * tr +
568  m->blf * bl +
569  m->brf * br;
570 
571  // Round by half, and revert the fixed-point offset
572  return ((v + fixed_point_half) >> FIXED_POINT_PRECISION) & max_value;
573 }
574 
575 static void
576 ssim360_4x4x2_tape(const uint8_t *main, BilinearMap *main_maps,
577  const uint8_t *ref, BilinearMap *ref_maps,
578  int offset_y, int max_value, int (*sums)[4])
579 {
580  int offset_x = 0;
581 
582  // Two blocks along the width
583  for (int z = 0; z < 2; z++) {
584  int s1 = 0, s2 = 0, ss = 0, s12 = 0;
585 
586  // 4 pixel block from (offset_x, offset_y)
587  for (int y = offset_y; y < offset_y + 4; y++) {
588  int y_stride = y << 3;
589  for (int x = offset_x; x < offset_x + 4; x++) {
590  int map_index = x + y_stride;
591  int a = get_bilinear_sample(main, main_maps + map_index, max_value);
592  int b = get_bilinear_sample(ref, ref_maps + map_index, max_value);
593 
594  s1 += a;
595  s2 += b;
596  ss += a*a;
597  ss += b*b;
598  s12 += a*b;
599  }
600  }
601 
602  sums[z][0] = s1;
603  sums[z][1] = s2;
604  sums[z][2] = ss;
605  sums[z][3] = s12;
606 
607  offset_x += 4;
608  }
609 }
610 
612 {
613  int floor_theta_by_2pi, floor_theta_by_pi;
614 
615  // Convert theta to range [0, 2*pi]
616  floor_theta_by_2pi = (int)(theta / (2.0f * M_PI_F)) - (theta < 0.0f);
617  theta -= 2.0f * M_PI_F * floor_theta_by_2pi;
618 
619  // Convert theta to range [-pi, pi]
620  floor_theta_by_pi = theta / M_PI_F;
621  theta -= 2.0f * M_PI_F * floor_theta_by_pi;
622  return FFMIN(M_PI_F, FFMAX(-M_PI_F, theta));
623 }
624 
625 static float get_heat(HeatmapList *heatmaps, float angular_resoluation, float norm_tape_pos)
626 {
627  float pitch, yaw, norm_pitch, norm_yaw;
628  int w, h;
629 
630  if (!heatmaps)
631  return 1.0f;
632 
633  pitch = asinf(norm_tape_pos*2);
634  yaw = M_PI_2_F * pitch / angular_resoluation;
636 
637  // normalize into [0,1]
638  norm_pitch = 1.0f - (pitch / M_PI_F + 0.5f);
639  norm_yaw = yaw / 2.0f / M_PI_F + 0.5f;
640 
641  // get heat on map
642  w = FFMIN(heatmaps->map.w - 1, FFMAX(0, heatmaps->map.w * norm_yaw));
643  h = FFMIN(heatmaps->map.h - 1, FFMAX(0, heatmaps->map.h * norm_pitch));
644  return heatmaps->map.value[h * heatmaps->map.w + w];
645 }
646 
647 static double
648 ssim360_tape(uint8_t *main, BilinearMap *main_maps,
649  uint8_t *ref, BilinearMap *ref_maps,
650  int tape_length, int max_value, void *temp,
651  double *ssim360_hist, double *ssim360_hist_net,
652  float angular_resolution, HeatmapList *heatmaps)
653 {
654  int horizontal_block_count = 2;
655  int vertical_block_count = tape_length >> 2;
656 
657  int z = 0, y;
658  // Since the tape will be very long and we need to average over all 8x8 blocks, use double
659  double ssim360 = 0.0;
660  double sum_weight = 0.0;
661 
662  int (*sum0)[4] = temp;
663  int (*sum1)[4] = sum0 + horizontal_block_count + 3;
664 
665  for (y = 1; y < vertical_block_count; y++) {
666  int fs1, fs2, fss, fs12, hist_index;
667  float norm_tape_pos, weight;
668  double sample_ssim360;
669 
670  for (; z <= y; z++) {
671  FFSWAP(void*, sum0, sum1);
672  ssim360_4x4x2_tape(main, main_maps, ref, ref_maps, z*4, max_value, sum0);
673  }
674 
675  // Given we have only one 8x8 block, following sums fit within 26 bits even for 10bit videos
676  fs1 = sum0[0][0] + sum0[1][0] + sum1[0][0] + sum1[1][0];
677  fs2 = sum0[0][1] + sum0[1][1] + sum1[0][1] + sum1[1][1];
678  fss = sum0[0][2] + sum0[1][2] + sum1[0][2] + sum1[1][2];
679  fs12 = sum0[0][3] + sum0[1][3] + sum1[0][3] + sum1[1][3];
680 
681  if (max_value > 255) {
682  // Since we need high precision to multiply fss / fs12 by 64, use double
683  double ssim_c1_d = .01*.01*64*max_value*max_value;
684  double ssim_c2_d = .03*.03*64*63*max_value*max_value;
685 
686  double vars = 64. * fss - 1. * fs1 * fs1 - 1. * fs2 * fs2;
687  double covar = 64. * fs12 - 1.*fs1 * fs2;
688  sample_ssim360 = (2. * fs1 * fs2 + ssim_c1_d) * (2. * covar + ssim_c2_d)
689  / ((1. * fs1 * fs1 + 1. * fs2 * fs2 + ssim_c1_d) * (1. * vars + ssim_c2_d));
690  } else {
691  static const int ssim_c1 = (int)(.01*.01*255*255*64 + .5);
692  static const int ssim_c2 = (int)(.03*.03*255*255*64*63 + .5);
693 
694  int vars = fss * 64 - fs1 * fs1 - fs2 * fs2;
695  int covar = fs12 * 64 - fs1 * fs2;
696  sample_ssim360 = (double)(2 * fs1 * fs2 + ssim_c1) * (double)(2 * covar + ssim_c2)
697  / ((double)(fs1 * fs1 + fs2 * fs2 + ssim_c1) * (double)(vars + ssim_c2));
698  }
699 
700  hist_index = (int)(sample_ssim360 * ((double)SSIM360_HIST_SIZE - .5));
701  hist_index = av_clip(hist_index, 0, SSIM360_HIST_SIZE - 1);
702 
703  norm_tape_pos = (y - 0.5f) / (vertical_block_count - 1.0f) - 0.5f;
704  // weight from an input heatmap if available, otherwise weight = 1.0
705  weight = get_heat(heatmaps, angular_resolution, norm_tape_pos);
706  ssim360_hist[hist_index] += weight;
707  *ssim360_hist_net += weight;
708 
709  ssim360 += (sample_ssim360 * weight);
710  sum_weight += weight;
711  }
712 
713  return ssim360 / sum_weight;
714 }
715 
716 static void compute_bilinear_map(SampleParams *p, BilinearMap *m, float x, float y)
717 {
718  float fixed_point_scale = (float)(1 << FIXED_POINT_PRECISION);
719 
720  // All operations in here will fit in the 22 bit mantissa of floating point,
721  // since the fixed point precision is well under 22 bits
722  float x_image = av_clipf(x * p->x_image_range, 0, p->x_image_range) + p->x_image_offset;
723  float y_image = av_clipf(y * p->y_image_range, 0, p->y_image_range) + p->y_image_offset;
724 
725  int x_floor = x_image;
726  int y_floor = y_image;
727  float x_diff = x_image - x_floor;
728  float y_diff = y_image - y_floor;
729 
730  int x_ceil = x_floor + (x_diff > 1e-6);
731  int y_ceil = y_floor + (y_diff > 1e-6);
732  float x_inv_diff = 1.0f - x_diff;
733  float y_inv_diff = 1.0f - y_diff;
734 
735  // Indices of the 4 samples from source frame
736  m->tli = x_floor + y_floor * p->stride;
737  m->tri = x_ceil + y_floor * p->stride;
738  m->bli = x_floor + y_ceil * p->stride;
739  m->bri = x_ceil + y_ceil * p->stride;
740 
741  // Scale to be applied to each of the 4 samples from source frame
742  m->tlf = x_inv_diff * y_inv_diff * fixed_point_scale;
743  m->trf = x_diff * y_inv_diff * fixed_point_scale;
744  m->blf = x_inv_diff * y_diff * fixed_point_scale;
745  m->brf = x_diff * y_diff * fixed_point_scale;
746 }
747 
748 static void get_equirect_map(float phi, float theta, float *x, float *y)
749 {
750  *x = 0.5f + theta / (2.0f * M_PI_F);
751  // y increases downwards
752  *y = 0.5f - phi / M_PI_F;
753 }
754 
755 static void get_barrel_map(float phi, float theta, float *x, float *y)
756 {
757  float abs_phi = FFABS(phi);
758 
759  if (abs_phi <= M_PI_4_F) {
760  // Equirect region
761  *x = 0.8f * (0.5f + theta / BARREL_THETA_RANGE);
762  // y increases downwards
763  *y = 0.5f - phi / BARREL_PHI_RANGE;
764  } else {
765  // Radial ratio on a unit circle = cot(abs_phi) / (expansion_cefficient).
766  // Using cos(abs_phi)/sin(abs_phi) explicitly to avoid division by zero
767  float radial_ratio = cosf(abs_phi) / (sinf(abs_phi) * DEFAULT_EXPANSION_COEF);
768  float circle_x = radial_ratio * sinf(theta);
769  float circle_y = radial_ratio * cosf(theta);
770  float offset_y = 0.25f;
771  if (phi < 0) {
772  // Bottom circle: theta increases clockwise, and front is upward
773  circle_y *= -1.0f;
774  offset_y += 0.5f;
775  }
776 
777  *x = 0.8f + 0.1f * (1.0f + circle_x);
778  *y = offset_y + 0.25f * circle_y;
779  }
780 }
781 
782 static void get_barrel_split_map(float phi, float theta, float expand_coef, float *x, float *y)
783 {
784  float abs_phi = FFABS(phi);
785 
786  // Front Face [-PI/2, PI/2] -> [0,1].
787  // Back Face [PI/2, PI] and [-PI, -PI/2] -> [1, 2]
788  float radian_pi_theta = theta / M_PI_F + 0.5f;
789  int vFace;
790 
791  if (radian_pi_theta < 0.0f)
792  radian_pi_theta += 2.0f;
793 
794  // Front face at top (= 0), back face at bottom (= 1).
795  vFace = radian_pi_theta >= 1.0f;
796 
797  if (abs_phi <= M_PI_4_F) {
798  // Equirect region
799  *x = 2.0f / 3.0f * (0.5f + (radian_pi_theta - vFace - 0.5f) / expand_coef);
800  // y increases downwards
801  *y = 0.25f + 0.5f * vFace - phi / (M_PI_F * expand_coef);
802  } else {
803  // Radial ratio on a unit circle = cot(abs_phi) / (expansion_cefficient).
804  // Using cos(abs_phi)/sin(abs_phi) explicitly to avoid division by zero
805  float radial_ratio = cosf(abs_phi) / (sinf(abs_phi) * expand_coef);
806  float circle_x = radial_ratio * sinf(theta);
807  float circle_y = radial_ratio * cosf(theta);
808  float offset_y = 0.25f;
809 
810  if (vFace == 1) {
811  // Back Face: Flip
812  circle_x *= -1.0f;
813  circle_y = (circle_y >= 0.0f) ? (1 - circle_y) : (-1 - circle_y);
814  offset_y += 0.5f;
815 
816  // Bottom circle: theta increases clockwise
817  if (phi < 0)
818  circle_y *= -1.0f;
819  } else {
820  // Front Face
821  // Bottom circle: theta increases clockwise
822  if (phi < 0)
823  circle_y *= -1.0f;
824  }
825 
826  *x = 2.0f / 3.0f + 0.5f / 3.0f * (1.0f + circle_x);
827  *y = offset_y + 0.25f * circle_y / expand_coef; // y direction of expand_coeff (margin)
828  }
829 }
830 
831 // Returns cube face, and provided face_x & face_y will range from [0, 1]
832 static int get_cubemap_face_map(float axis_vec_x, float axis_vec_y, float axis_vec_z, float *face_x, float *face_y)
833 {
834  // To check if phi, theta hits the top / bottom faces, we check the hit point of
835  // the axis vector on planes y = 1 and y = -1, and see if x & z are within [-1, 1]
836 
837  // 0.577 < 1 / sqrt(3), which is less than the smallest sin(phi) falling on top/bottom faces
838  // This angle check will save computation from unnecessarily checking the top/bottom faces
839  if (FFABS(axis_vec_y) > 0.577f) {
840  float x_hit = axis_vec_x / FFABS(axis_vec_y);
841  float z_hit = axis_vec_z / axis_vec_y;
842 
843  if (FFABS(x_hit) <= 1.f && FFABS(z_hit) <= 1.f) {
844  *face_x = x_hit;
845  // y increases downwards
846  *face_y = z_hit;
847  return axis_vec_y > 0 ? TOP : BOTTOM;
848  }
849  }
850 
851  // Check for left / right faces
852  if (FFABS(axis_vec_x) > 0.577f) {
853  float z_hit = -axis_vec_z / axis_vec_x;
854  float y_hit = axis_vec_y / FFABS(axis_vec_x);
855 
856  if (FFABS(z_hit) <= 1.f && FFABS(y_hit) <= 1.f) {
857  *face_x = z_hit;
858  // y increases downwards
859  *face_y = -y_hit;
860  return axis_vec_x > 0 ? RIGHT : LEFT;
861  }
862  }
863 
864  // Front / back faces
865  *face_x = axis_vec_x / axis_vec_z;
866  // y increases downwards
867  *face_y = -axis_vec_y / FFABS(axis_vec_z);
868 
869  return axis_vec_z > 0 ? FRONT : BACK;
870 }
871 
872 static void get_cubemap32_map(float phi, float theta, float *x, float *y)
873 {
874  // face_projection_map maps each cube face to an index representing the face on the projection
875  // The indices 0->5 for cubemap 32 goes as:
876  // [0, 1, 2] as row 1, left to right
877  // [3, 4, 5] as row 2, left to right
878  static const int face_projection_map[] = {
879  [RIGHT] = 0, [LEFT] = 1, [TOP] = 2,
880  [BOTTOM] = 3, [FRONT] = 4, [BACK] = 5,
881  };
882 
883  float axis_vec_x = cosf(phi) * sinf(theta);
884  float axis_vec_y = sinf(phi);
885  float axis_vec_z = cosf(phi) * cosf(theta);
886  float face_x = 0, face_y = 0;
887  int face_index = get_cubemap_face_map(axis_vec_x, axis_vec_y, axis_vec_z, &face_x, &face_y);
888 
889  float x_offset = 1.f / 3.f * (face_projection_map[face_index] % 3);
890  float y_offset = .5f * (face_projection_map[face_index] / 3);
891 
892  *x = x_offset + (face_x / DEFAULT_EXPANSION_COEF + 1.f) / 6.f;
893  *y = y_offset + (face_y / DEFAULT_EXPANSION_COEF + 1.f) / 4.f;
894 }
895 
896 static void get_rotated_cubemap_map(float phi, float theta, float expand_coef, float *x, float *y)
897 {
898  // face_projection_map maps each cube face to an index representing the face on the projection
899  // The indices 0->5 for rotated cubemap goes as:
900  // [0, 1] as row 1, left to right
901  // [2, 3] as row 2, left to right
902  // [4, 5] as row 3, left to right
903  static const int face_projection_map[] = {
904  [LEFT] = 0, [TOP] = 1,
905  [FRONT] = 2, [BACK] = 3,
906  [RIGHT] = 4, [BOTTOM] = 5,
907  };
908 
909  float axis_yaw_vec_x, axis_yaw_vec_y, axis_yaw_vec_z;
910  float axis_pitch_vec_z, axis_pitch_vec_y;
911  float x_offset, y_offset;
912  float face_x = 0, face_y = 0;
913  int face_index;
914 
915  // Unrotate the cube and fix the face map:
916  // First undo the 45 degree yaw
917  theta += M_PI_4_F;
918 
919  // Now we are looking at the middle of an edge. So convert to axis vector & undo the pitch
920  axis_yaw_vec_x = cosf(phi) * sinf(theta);
921  axis_yaw_vec_y = sinf(phi);
922  axis_yaw_vec_z = cosf(phi) * cosf(theta);
923 
924  // The pitch axis is along +x, and has value of -45 degree. So, only y and z components change
925  axis_pitch_vec_z = (axis_yaw_vec_z - axis_yaw_vec_y) / M_SQRT2_F;
926  axis_pitch_vec_y = (axis_yaw_vec_y + axis_yaw_vec_z) / M_SQRT2_F;
927 
928  face_index = get_cubemap_face_map(axis_yaw_vec_x, axis_pitch_vec_y, axis_pitch_vec_z, &face_x, &face_y);
929 
930  // Correct for the orientation of the axes on the faces
931  if (face_index == LEFT || face_index == FRONT || face_index == RIGHT) {
932  // x increases downwards & y increases towards left
933  float upright_y = face_y;
934  face_y = face_x;
935  face_x = -upright_y;
936  } else if (face_index == TOP || face_index == BOTTOM) {
937  // turn the face upside-down for top and bottom
938  face_x *= -1.f;
939  face_y *= -1.f;
940  }
941 
942  x_offset = .5f * (face_projection_map[face_index] & 1);
943  y_offset = 1.f / 3.f * (face_projection_map[face_index] >> 1);
944 
945  *x = x_offset + (face_x / expand_coef + 1.f) / 4.f;
946  *y = y_offset + (face_y / expand_coef + 1.f) / 6.f;
947 }
948 
949 static void get_projected_map(float phi, float theta, SampleParams *p, BilinearMap *m)
950 {
951  float x = 0, y = 0;
952  switch(p->projection) {
953 // TODO: Calculate for CDS
955  get_rotated_cubemap_map(phi, theta, p->expand_coef, &x, &y);
956  break;
958  get_cubemap32_map(phi, theta, &x, &y);
959  break;
960  case PROJECTION_BARREL:
961  get_barrel_map(phi, theta, &x, &y);
962  break;
964  get_barrel_split_map(phi, theta, p->expand_coef, &x, &y);
965  break;
966  // Assume PROJECTION_EQUIRECT as the default
967  case PROJECTION_EQUIRECT:
968  default:
969  get_equirect_map(phi, theta, &x, &y);
970  break;
971  }
972  compute_bilinear_map(p, m, x, y);
973 }
974 
975 static int tape_supports_projection(int projection)
976 {
977  switch(projection) {
980  case PROJECTION_BARREL:
982  case PROJECTION_EQUIRECT:
983  return 1;
984  default:
985  return 0;
986  }
987 }
988 
989 static float get_tape_angular_resolution(int projection, float expand_coef, int image_width, int image_height)
990 {
991  // NOTE: The angular resolution of a projected sphere is defined as
992  // the maximum possible horizontal angle of a pixel on the equator.
993  // We apply an intentional bias to the horizon as opposed to the meridian,
994  // since the view direction of most content is rarely closer to the poles
995 
996  switch(projection) {
997 // TODO: Calculate for CDS
999  // Approximating atanf(pixel_width / (half_edge_width * sqrt2)) = pixel_width / (half_face_width * sqrt2)
1000  return expand_coef / (M_SQRT2_F * image_width / 4.f);
1001  case PROJECTION_CUBEMAP32:
1002  // Approximating atanf(pixel_width / half_face_width) = pixel_width / half_face_width
1003  return DEFAULT_EXPANSION_COEF / (image_width / 6.f);
1004  case PROJECTION_BARREL:
1005  return FFMAX(BARREL_THETA_RANGE / (0.8f * image_width), BARREL_PHI_RANGE / image_height);
1007  return FFMAX((expand_coef * M_PI_F) / (2.0f / 3.0f * image_width),
1008  expand_coef * M_PI_2_F / (image_height / 2.0f));
1009  // Assume PROJECTION_EQUIRECT as the default
1010  case PROJECTION_EQUIRECT:
1011  default:
1012  return FFMAX(2.0f * M_PI_F / image_width, M_PI_F / image_height);
1013  }
1014 }
1015 
1016 static int
1018  int plane, int eye,
1019  SampleParams *ref_sample_params,
1020  SampleParams *main_sample_params)
1021 {
1022  int ref_image_width = ref_sample_params->x_image_range + 1;
1023  int ref_image_height = ref_sample_params->y_image_range + 1;
1024 
1025  float angular_resolution =
1026  get_tape_angular_resolution(s->ref_projection, 1.f + s->ref_pad,
1027  ref_image_width, ref_image_height);
1028 
1029  float conversion_factor = M_PI_2_F / (angular_resolution * angular_resolution);
1030  float start_phi = -M_PI_2_F + 4.0f * angular_resolution;
1031  float start_x = conversion_factor * sinf(start_phi);
1032  float end_phi = M_PI_2_F - 3.0f * angular_resolution;
1033  float end_x = conversion_factor * sinf(end_phi);
1034  float x_range = end_x - start_x;
1035 
1036  // Ensure tape length is a multiple of 4, for full SSIM block coverage
1037  float tape_length_f = ROUNDED_DIV(x_range, 4);
1038  int tape_length;
1039 
1040  if (!(tape_length_f > 0.f) || tape_length_f > INT_MAX / 4.0f)
1041  return AVERROR(EINVAL);
1042 
1043  tape_length = s->tape_length[plane] = (int)tape_length_f << 2;
1044 
1045  s->ref_tape_map[plane][eye] = av_malloc_array(tape_length, 8 * sizeof(BilinearMap));
1046  s->main_tape_map[plane][eye] = av_malloc_array(tape_length, 8 * sizeof(BilinearMap));
1047  if (!s->ref_tape_map[plane][eye] || !s->main_tape_map[plane][eye])
1048  return AVERROR(ENOMEM);
1049 
1050  s->angular_resolution[plane][eye] = angular_resolution;
1051 
1052  // For easy memory access, we navigate the tape lengthwise on y
1053  for (int y_index = 0; y_index < tape_length; y_index ++) {
1054  int y_stride = y_index << 3;
1055 
1056  float x = start_x + x_range * (y_index / (tape_length - 1.0f));
1057  // phi will be in range [-pi/2, pi/2]
1058  float mid_phi = asinf(x / conversion_factor);
1059 
1060  float theta = mid_phi * M_PI_2_F / angular_resolution;
1062 
1063  for (int x_index = 0; x_index < 8; x_index ++) {
1064  float phi = mid_phi + angular_resolution * (3.0f - x_index);
1065  int tape_index = y_stride + x_index;
1066  get_projected_map(phi, theta, ref_sample_params, &s->ref_tape_map [plane][eye][tape_index]);
1067  get_projected_map(phi, theta, main_sample_params, &s->main_tape_map[plane][eye][tape_index]);
1068  }
1069  }
1070 
1071  return 0;
1072 }
1073 
1075 {
1076  // A tape is a long segment with 8 pixels thickness, with the angular center at the middle (below 4th pixel).
1077  // When it takes a full loop around a sphere, it will overlap the starting point at half the width from above.
1078  int ref_stereo_format = s->ref_stereo_format;
1079  int main_stereo_format = s->main_stereo_format;
1080  int are_both_stereo = (main_stereo_format != STEREO_FORMAT_MONO) && (ref_stereo_format != STEREO_FORMAT_MONO);
1081  int min_eye_count = 1 + are_both_stereo;
1082  int ret;
1083 
1084  for (int i = 0; i < s->nb_components; i ++) {
1085  int ref_width = s->ref_planewidth[i];
1086  int ref_height = s->ref_planeheight[i];
1087  int main_width = s->main_planewidth[i];
1088  int main_height = s->main_planeheight[i];
1089 
1090  int is_ref_LR = (ref_stereo_format == STEREO_FORMAT_LR);
1091  int is_ref_TB = (ref_stereo_format == STEREO_FORMAT_TB);
1092  int is_main_LR = (main_stereo_format == STEREO_FORMAT_LR);
1093  int is_main_TB = (main_stereo_format == STEREO_FORMAT_TB);
1094 
1095  int ref_image_width = is_ref_LR ? ref_width >> 1 : ref_width;
1096  int ref_image_height = is_ref_TB ? ref_height >> 1 : ref_height;
1097  int main_image_width = is_main_LR ? main_width >> 1 : main_width;
1098  int main_image_height = is_main_TB ? main_height >> 1 : main_height;
1099 
1100  for (int eye = 0; eye < min_eye_count; eye ++) {
1101  SampleParams ref_sample_params = {
1102  .stride = ref->linesize[i],
1103  .planewidth = ref_width,
1104  .planeheight = ref_height,
1105  .x_image_range = ref_image_width - 1,
1106  .y_image_range = ref_image_height - 1,
1107  .x_image_offset = is_ref_LR * eye * ref_image_width,
1108  .y_image_offset = is_ref_TB * eye * ref_image_height,
1109  .projection = s->ref_projection,
1110  .expand_coef = 1.f + s->ref_pad,
1111  };
1112 
1113  SampleParams main_sample_params = {
1114  .stride = main->linesize[i],
1115  .planewidth = main_width,
1116  .planeheight = main_height,
1117  .x_image_range = main_image_width - 1,
1118  .y_image_range = main_image_height - 1,
1119  .x_image_offset = is_main_LR * eye * main_image_width,
1120  .y_image_offset = is_main_TB * eye * main_image_height,
1121  .projection = s->main_projection,
1122  .expand_coef = 1.f + s->main_pad,
1123  };
1124 
1125  ret = generate_eye_tape_map(s, i, eye, &ref_sample_params, &main_sample_params);
1126  if (ret < 0)
1127  return ret;
1128  }
1129  }
1130 
1131  return 0;
1132 }
1133 
1135 {
1136  AVFilterContext *ctx = fs->parent;
1137  SSIM360Context *s = ctx->priv;
1138  AVFrame *master, *ref;
1140  double c[4], ssim360v = 0.0, ssim360p50 = 0.0;
1141  int ret;
1142  int need_frame_skip = s->nb_net_frames % (s->frame_skip_ratio + 1);
1143  HeatmapList* h_ptr = NULL;
1144 
1146  if (ret < 0)
1147  return ret;
1148 
1149  s->nb_net_frames++;
1150 
1151  if (need_frame_skip)
1152  return ff_filter_frame(ctx->outputs[0], master);
1153 
1154  metadata = &master->metadata;
1155 
1156  if (s->use_tape && !s->tape_length[0]) {
1158  if (ret < 0)
1159  return ret;
1160  }
1161 
1162  for (int i = 0; i < s->nb_components; i++) {
1163  if (s->use_tape) {
1164  c[i] = ssim360_tape(master->data[i], s->main_tape_map[i][0],
1165  ref->data[i], s->ref_tape_map [i][0],
1166  s->tape_length[i], s->max, s->temp,
1167  s->ssim360_hist[i], &s->ssim360_hist_net[i],
1168  s->angular_resolution[i][0], s->heatmaps);
1169 
1170  if (s->ref_tape_map[i][1]) {
1171  c[i] += ssim360_tape(master->data[i], s->main_tape_map[i][1],
1172  ref->data[i], s->ref_tape_map[i][1],
1173  s->tape_length[i], s->max, s->temp,
1174  s->ssim360_hist[i], &s->ssim360_hist_net[i],
1175  s->angular_resolution[i][1], s->heatmaps);
1176  c[i] /= 2.f;
1177  }
1178  } else {
1179  c[i] = s->ssim360_plane(master->data[i], master->linesize[i],
1180  ref->data[i], ref->linesize[i],
1181  s->ref_planewidth[i], s->ref_planeheight[i],
1182  s->temp, s->max, s->density);
1183  }
1184 
1185  s->ssim360[i] += c[i];
1186  ssim360v += s->coefs[i] * c[i];
1187  }
1188 
1189  s->nb_ssim_frames++;
1190  if (s->heatmaps) {
1191  map_uninit(&s->heatmaps->map);
1192  h_ptr = s->heatmaps;
1193  s->heatmaps = s->heatmaps->next;
1194  av_freep(&h_ptr);
1195  }
1196  s->ssim360_total += ssim360v;
1197 
1198  // Record percentiles from histogram and attach metadata when using tape
1199  if (s->use_tape) {
1200  int hist_indices[4];
1201  double hist_weight[4];
1202 
1203  for (int i = 0; i < s->nb_components; i++) {
1204  hist_indices[i] = SSIM360_HIST_SIZE - 1;
1205  hist_weight[i] = 0;
1206  }
1207 
1208  for (int p = 0; PERCENTILE_LIST[p] >= 0.0; p ++) {
1209  for (int i = 0; i < s->nb_components; i++) {
1210  double target_weight, ssim360p;
1211 
1212  // Target weight = total number of samples above the specified percentile
1213  target_weight = (1. - PERCENTILE_LIST[p]) * s->ssim360_hist_net[i];
1214  target_weight = FFMAX(target_weight, 1);
1215  while(hist_indices[i] >= 0 && hist_weight[i] < target_weight) {
1216  hist_weight[i] += s->ssim360_hist[i][hist_indices[i]];
1217  hist_indices[i] --;
1218  }
1219 
1220  ssim360p = (double)(hist_indices[i] + 1) / (double)(SSIM360_HIST_SIZE - 1);
1221  if (PERCENTILE_LIST[p] == 0.5)
1222  ssim360p50 += s->coefs[i] * ssim360p;
1223  s->ssim360_percentile_sum[i][p] += ssim360p;
1224  }
1225  }
1226 
1227  for (int i = 0; i < s->nb_components; i++) {
1228  memset(s->ssim360_hist[i], 0, SSIM360_HIST_SIZE * sizeof(double));
1229  s->ssim360_hist_net[i] = 0;
1230  }
1231 
1232  for (int i = 0; i < s->nb_components; i++) {
1233  int cidx = s->is_rgb ? s->rgba_map[i] : i;
1234  set_meta(metadata, "lavfi.ssim360.", s->comps[i], c[cidx]);
1235  }
1236 
1237  // Use p50 as the aggregated value
1238  set_meta(metadata, "lavfi.ssim360.All", 0, ssim360p50);
1239  set_meta(metadata, "lavfi.ssim360.dB", 0, ssim360_db(ssim360p50, 1.0));
1240 
1241  if (s->stats_file) {
1242  fprintf(s->stats_file, "n:%"PRId64" ", s->nb_ssim_frames);
1243 
1244  for (int i = 0; i < s->nb_components; i++) {
1245  int cidx = s->is_rgb ? s->rgba_map[i] : i;
1246  fprintf(s->stats_file, "%c:%f ", s->comps[i], c[cidx]);
1247  }
1248 
1249  fprintf(s->stats_file, "All:%f (%f)\n", ssim360p50, ssim360_db(ssim360p50, 1.0));
1250  }
1251  }
1252 
1253  return ff_filter_frame(ctx->outputs[0], master);
1254 }
1255 
1256 static int parse_heatmaps(void *logctx, HeatmapList **proot,
1257  const char *data, int w, int h)
1258 {
1259  HeatmapList *root = NULL;
1260  HeatmapList **next = &root;
1261 
1262  int ret;
1263 
1264  // skip video id line
1265  data = strchr(data, '\n');
1266  if (!data) {
1267  av_log(logctx, AV_LOG_ERROR, "Invalid heatmap syntax\n");
1268  return AVERROR(EINVAL);
1269  }
1270  data++;
1271 
1272  while (*data) {
1273  HeatmapList *cur;
1274  char *line = av_get_token(&data, "\n");
1275  char *saveptr, *val;
1276  int i;
1277 
1278  if (!line) {
1279  ret = AVERROR(ENOMEM);
1280  goto fail;
1281  }
1282 
1283  // first value is frame id
1284  av_strtok(line, ",", &saveptr);
1285 
1286  ret = map_alloc(next, w, h);
1287  if (ret < 0)
1288  goto line_fail;
1289 
1290  cur = *next;
1291  next = &cur->next;
1292 
1293  i = 0;
1294  while ((val = av_strtok(NULL, ",", &saveptr))) {
1295  if (i >= w * h) {
1296  av_log(logctx, AV_LOG_ERROR, "Too many entries in a heat map\n");
1297  ret = AVERROR(EINVAL);
1298  goto line_fail;
1299  }
1300 
1301  cur->map.value[i++] = atof(val);
1302  }
1303 
1304 line_fail:
1305  av_freep(&line);
1306  if (ret < 0)
1307  goto fail;
1308  }
1309 
1310  *proot = root;
1311 
1312  return 0;
1313 fail:
1314  map_list_free(&root);
1315  return ret;
1316 }
1317 
1319 {
1320  SSIM360Context *s = ctx->priv;
1321  int err;
1322 
1323  if (s->stats_file_str) {
1324  if (!strcmp(s->stats_file_str, "-")) {
1325  s->stats_file = stdout;
1326  } else {
1327  s->stats_file = avpriv_fopen_utf8(s->stats_file_str, "w");
1328  if (!s->stats_file) {
1329  err = AVERROR(errno);
1330  av_log(ctx, AV_LOG_ERROR, "Could not open stats file %s: %s\n",
1331  s->stats_file_str, av_err2str(err));
1332  return err;
1333  }
1334  }
1335  }
1336 
1337  if (s->use_tape && s->heatmap_str) {
1338  err = parse_heatmaps(ctx, &s->heatmaps, s->heatmap_str,
1339  s->default_heatmap_w, s->default_heatmap_h);
1340  if (err < 0)
1341  return err;
1342  }
1343 
1344  s->fs.on_event = do_ssim360;
1345  return 0;
1346 }
1347 
1349 {
1351  AVFilterContext *ctx = inlink->dst;
1352  SSIM360Context *s = ctx->priv;
1353 
1354  s->main_planeheight[0] = inlink->h;
1355  s->main_planeheight[3] = inlink->h;
1356  s->main_planeheight[1] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
1357  s->main_planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
1358 
1359  s->main_planewidth[0] = inlink->w;
1360  s->main_planewidth[3] = inlink->w;
1361  s->main_planewidth[1] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
1362  s->main_planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
1363 
1364  // If main projection is unindentified, assume it is same as reference
1365  if (s->main_projection == PROJECTION_N)
1366  s->main_projection = s->ref_projection;
1367 
1368  // If main stereo format is unindentified, assume it is same as reference
1369  if (s->main_stereo_format == STEREO_FORMAT_N)
1370  s->main_stereo_format = s->ref_stereo_format;
1371 
1372  return 0;
1373 }
1374 
1375 static int generate_density_map(SSIM360Context *s, int w, int h)
1376 {
1377  double d, r_square, cos_square;
1378  int ow, oh, ret;
1379 
1380  ret = map_init(&s->density, w, h);
1381  if (ret < 0)
1382  return ret;
1383 
1384  switch (s->ref_stereo_format) {
1385  case STEREO_FORMAT_TB:
1386  h >>= 1;
1387  break;
1388  case STEREO_FORMAT_LR:
1389  w >>= 1;
1390  break;
1391  }
1392 
1393  switch (s->ref_projection) {
1394  case PROJECTION_EQUIRECT:
1395  for (int i = 0; i < h; i++) {
1396  d = cos(((0.5 + i) / h - 0.5) * M_PI);
1397  for (int j = 0; j < w; j++)
1398  s->density.value[i * w + j] = d;
1399  }
1400  break;
1401  case PROJECTION_CUBEMAP32:
1402  // for one quater of a face
1403  for (int i = 0; i < h / 4; i++) {
1404  for (int j = 0; j < w / 6; j++) {
1405  // r = normalized distance to the face center
1406  r_square =
1407  (0.5 + i) / (h / 2) * (0.5 + i) / (h / 2) +
1408  (0.5 + j) / (w / 3) * (0.5 + j) / (w / 3);
1410  cos_square = 0.25 / (r_square + 0.25);
1411  d = pow(cos_square, 1.5);
1412 
1413  for (int face = 0; face < 6; face++) {
1414  // center of a face
1415  switch (face) {
1416  case 0:
1417  oh = h / 4;
1418  ow = w / 6;
1419  break;
1420  case 1:
1421  oh = h / 4;
1422  ow = w / 6 + w / 3;
1423  break;
1424  case 2:
1425  oh = h / 4;
1426  ow = w / 6 + 2 * w / 3;
1427  break;
1428  case 3:
1429  oh = h / 4 + h / 2;
1430  ow = w / 6;
1431  break;
1432  case 4:
1433  oh = h / 4 + h / 2;
1434  ow = w / 6 + w / 3;
1435  break;
1436  case 5:
1437  oh = h / 4 + h / 2;
1438  ow = w / 6 + 2 * w / 3;
1439  break;
1440  }
1441  s->density.value[(oh - 1 - i) * w + ow - 1 - j] = d;
1442  s->density.value[(oh - 1 - i) * w + ow + j] = d;
1443  s->density.value[(oh + i) * w + ow - 1 - j] = d;
1444  s->density.value[(oh + i) * w + ow + j] = d;
1445  }
1446  }
1447  }
1448  break;
1449  case PROJECTION_CUBEMAP23:
1450  // for one quater of a face
1451  for (int i = 0; i < h / 6; i++) {
1452  for (int j = 0; j < w / 4; j++) {
1453  // r = normalized distance to the face center
1454  r_square =
1455  (0.5 + i) / (h / 3) * (0.5 + i) / (h / 3) +
1456  (0.5 + j) / (w / 2) * (0.5 + j) / (w / 2);
1457  r_square /= (1.f + s->ref_pad) * (1.f + s->ref_pad);
1458  cos_square = 0.25 / (r_square + 0.25);
1459  d = pow(cos_square, 1.5);
1460 
1461  for (int face = 0; face < 6; face++) {
1462  // center of a face
1463  switch (face) {
1464  case 0:
1465  ow = w / 4;
1466  oh = h / 6;
1467  break;
1468  case 1:
1469  ow = w / 4;
1470  oh = h / 6 + h / 3;
1471  break;
1472  case 2:
1473  ow = w / 4;
1474  oh = h / 6 + 2 * h / 3;
1475  break;
1476  case 3:
1477  ow = w / 4 + w / 2;
1478  oh = h / 6;
1479  break;
1480  case 4:
1481  ow = w / 4 + w / 2;
1482  oh = h / 6 + h / 3;
1483  break;
1484  case 5:
1485  ow = w / 4 + w / 2;
1486  oh = h / 6 + 2 * h / 3;
1487  break;
1488  }
1489  s->density.value[(oh - 1 - i) * w + ow - 1 - j] = d;
1490  s->density.value[(oh - 1 - i) * w + ow + j] = d;
1491  s->density.value[(oh + i) * w + ow - 1 - j] = d;
1492  s->density.value[(oh + i) * w + ow + j] = d;
1493  }
1494  }
1495  }
1496  break;
1497  case PROJECTION_BARREL:
1498  // side face
1499  for (int i = 0; i < h; i++) {
1500  for (int j = 0; j < w * 4 / 5; j++) {
1501  d = cos(((0.5 + i) / h - 0.5) * DEFAULT_EXPANSION_COEF * M_PI_2);
1502  s->density.value[i * w + j] = d * d * d;
1503  }
1504  }
1505  // top and bottom
1506  for (int i = 0; i < h; i++) {
1507  for (int j = w * 4 / 5; j < w; j++) {
1508  double dx = DEFAULT_EXPANSION_COEF * (0.5 + j - w * 0.90) / (w * 0.10);
1509  double dx_squared = dx * dx;
1510 
1511  double top_dy = DEFAULT_EXPANSION_COEF * (0.5 + i - h * 0.25) / (h * 0.25);
1512  double top_dy_squared = top_dy * top_dy;
1513 
1514  double bottom_dy = DEFAULT_EXPANSION_COEF * (0.5 + i - h * 0.75) / (h * 0.25);
1515  double bottom_dy_squared = bottom_dy * bottom_dy;
1516 
1517  // normalized distance to the circle center
1518  r_square = (i < h / 2 ? top_dy_squared : bottom_dy_squared) + dx_squared;
1519  if (r_square > 1.0)
1520  continue;
1521 
1522  cos_square = 1.0 / (r_square + 1.0);
1523  d = pow(cos_square, 1.5);
1524  s->density.value[i * w + j] = d;
1525  }
1526  }
1527  break;
1528  default:
1529  // TODO: SSIM360_v1
1530  for (int i = 0; i < h; i++) {
1531  for (int j = 0; j < w; j++)
1532  s->density.value[i * w + j] = 0;
1533  }
1534  }
1535 
1536  switch (s->ref_stereo_format) {
1537  case STEREO_FORMAT_TB:
1538  for (int i = 0; i < h; i++) {
1539  for (int j = 0; j < w; j++)
1540  s->density.value[(i + h) * w + j] = s->density.value[i * w + j];
1541  }
1542  break;
1543  case STEREO_FORMAT_LR:
1544  for (int i = 0; i < h; i++) {
1545  for (int j = 0; j < w; j++)
1546  s->density.value[i * w + j + w] = s->density.value[i * w + j];
1547  }
1548  }
1549 
1550  return 0;
1551 }
1552 
1554 {
1556  AVFilterContext *ctx = inlink->dst;
1557  SSIM360Context *s = ctx->priv;
1558  int sum = 0;
1559 
1560  s->nb_components = desc->nb_components;
1561 
1562  s->ref_planeheight[0] = inlink->h;
1563  s->ref_planeheight[3] = inlink->h;
1564  s->ref_planeheight[1] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
1565  s->ref_planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
1566 
1567  s->ref_planewidth[0] = inlink->w;
1568  s->ref_planewidth[3] = inlink->w;
1569  s->ref_planewidth[1] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
1570  s->ref_planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
1571 
1572  s->is_rgb = ff_fill_rgba_map(s->rgba_map, inlink->format) >= 0;
1573  s->comps[0] = s->is_rgb ? 'R' : 'Y';
1574  s->comps[1] = s->is_rgb ? 'G' : 'U';
1575  s->comps[2] = s->is_rgb ? 'B' : 'V';
1576  s->comps[3] = 'A';
1577 
1578  // If chroma computation is disabled, and the format is YUV, skip U & V channels
1579  if (!s->is_rgb && !s->compute_chroma)
1580  s->nb_components = 1;
1581 
1582  s->max = (1 << desc->comp[0].depth) - 1;
1583 
1584  s->ssim360_plane = desc->comp[0].depth > 8 ? ssim360_plane_16bit : ssim360_plane_8bit;
1585 
1586  for (int i = 0; i < s->nb_components; i++)
1587  sum += s->ref_planeheight[i] * s->ref_planewidth[i];
1588  for (int i = 0; i < s->nb_components; i++)
1589  s->coefs[i] = (double) s->ref_planeheight[i] * s->ref_planewidth[i] / sum;
1590 
1591  return 0;
1592 }
1593 
1594 static int config_output(AVFilterLink *outlink)
1595 {
1596  AVFilterContext *ctx = outlink->src;
1597  SSIM360Context *s = ctx->priv;
1598  AVFilterLink *mainlink = ctx->inputs[0];
1599  AVFilterLink *reflink = ctx->inputs[0];
1600  FilterLink *il = ff_filter_link(mainlink);
1601  FilterLink *ol = ff_filter_link(outlink);
1603  int ret;
1604 
1605  // Use tape algorithm if any of frame sizes, projections or stereo format are not equal
1606  if (ctx->inputs[0]->w != ctx->inputs[1]->w || ctx->inputs[0]->h != ctx->inputs[1]->h ||
1607  s->ref_projection != s->main_projection || s->ref_stereo_format != s->main_stereo_format)
1608  s->use_tape = 1;
1609 
1610  // Finally, if we have decided to / forced to use tape, check if tape supports both input and output projection
1611  if (s->use_tape &&
1612  !(tape_supports_projection(s->main_projection) &&
1613  tape_supports_projection(s->ref_projection))) {
1614  av_log(ctx, AV_LOG_ERROR, "Projection is unsupported for the tape based algorithm\n");
1615  return AVERROR(EINVAL);
1616  }
1617 
1618  if (s->use_tape) {
1619  // s->temp will be allocated for the tape width = 8. The tape is long downwards
1620  s->temp = av_malloc_array((2 * 8 + 12), sizeof(*s->temp));
1621  if (!s->temp)
1622  return AVERROR(ENOMEM);
1623 
1624  memset(s->ssim360_percentile_sum, 0, sizeof(s->ssim360_percentile_sum));
1625 
1626  for (int i = 0; i < s->nb_components; i++) {
1627  FF_ALLOCZ_TYPED_ARRAY(s->ssim360_hist[i], SSIM360_HIST_SIZE);
1628  if (!s->ssim360_hist[i])
1629  return AVERROR(ENOMEM);
1630  }
1631  } else {
1632  s->temp = av_malloc_array((2 * reflink->w + 12), sizeof(*s->temp) * (1 + (desc->comp[0].depth > 8)));
1633  if (!s->temp)
1634  return AVERROR(ENOMEM);
1635 
1636  if (!s->density.value) {
1637  ret = generate_density_map(s, reflink->w, reflink->h);
1638  if (ret < 0)
1639  return ret;
1640  }
1641  }
1642 
1644  if (ret < 0)
1645  return ret;
1646 
1647  outlink->w = mainlink->w;
1648  outlink->h = mainlink->h;
1649  outlink->time_base = mainlink->time_base;
1650  outlink->sample_aspect_ratio = mainlink->sample_aspect_ratio;
1651  ol->frame_rate = il->frame_rate;
1652 
1653  s->fs.opt_shortest = 1;
1654  s->fs.opt_repeatlast = 1;
1655 
1656  ret = ff_framesync_configure(&s->fs);
1657  if (ret < 0)
1658  return ret;
1659 
1660  return 0;
1661 }
1662 
1664 {
1665  SSIM360Context *s = ctx->priv;
1666  return ff_framesync_activate(&s->fs);
1667 }
1668 
1670 {
1671  SSIM360Context *s = ctx->priv;
1672 
1673  if (s->nb_ssim_frames > 0) {
1674  char buf[256];
1675  buf[0] = 0;
1676  // Log average SSIM360 values
1677  for (int i = 0; i < s->nb_components; i++) {
1678  int c = s->is_rgb ? s->rgba_map[i] : i;
1679  av_strlcatf(buf, sizeof(buf), " %c:%f (%f)", s->comps[i], s->ssim360[c] / s->nb_ssim_frames,
1680  ssim360_db(s->ssim360[c], s->nb_ssim_frames));
1681  }
1682  av_log(ctx, AV_LOG_INFO, "SSIM360%s All:%f (%f)\n", buf,
1683  s->ssim360_total / s->nb_ssim_frames, ssim360_db(s->ssim360_total, s->nb_ssim_frames));
1684 
1685  // Log percentiles from histogram when using tape
1686  if (s->use_tape) {
1687  for (int p = 0; PERCENTILE_LIST[p] >= 0.0; p++) {
1688  buf[0] = 0;
1689  for (int i = 0; i < s->nb_components; i++) {
1690  int c = s->is_rgb ? s->rgba_map[i] : i;
1691  double ssim360p = s->ssim360_percentile_sum[i][p] / (double)(s->nb_ssim_frames);
1692  av_strlcatf(buf, sizeof(buf), " %c:%f (%f)", s->comps[c], ssim360p, ssim360_db(ssim360p, 1));
1693  }
1694  av_log(ctx, AV_LOG_INFO, "SSIM360_p%d%s\n", (int)(PERCENTILE_LIST[p] * 100.), buf);
1695  }
1696  }
1697  }
1698 
1699  // free density map
1700  map_uninit(&s->density);
1701 
1702  map_list_free(&s->heatmaps);
1703 
1704  for (int i = 0; i < s->nb_components; i++) {
1705  for (int eye = 0; eye < 2; eye++) {
1706  av_freep(&s->ref_tape_map[i][eye]);
1707  av_freep(&s->main_tape_map[i][eye]);
1708  }
1709  av_freep(&s->ssim360_hist[i]);
1710  }
1711 
1712  ff_framesync_uninit(&s->fs);
1713 
1714  if (s->stats_file && s->stats_file != stdout)
1715  fclose(s->stats_file);
1716 
1717  av_freep(&s->temp);
1718 }
1719 
1720 #define PF(suf) AV_PIX_FMT_YUV420##suf, AV_PIX_FMT_YUV422##suf, AV_PIX_FMT_YUV444##suf, AV_PIX_FMT_GBR##suf
1721 static const enum AVPixelFormat ssim360_pixfmts[] = {
1728  PF(P9), PF(P10), PF(P12), PF(P14), PF(P16),
1730 };
1731 #undef PF
1732 
1733 static const AVFilterPad ssim360_inputs[] = {
1734  {
1735  .name = "main",
1736  .type = AVMEDIA_TYPE_VIDEO,
1737  .config_props = config_input_main,
1738  },
1739  {
1740  .name = "reference",
1741  .type = AVMEDIA_TYPE_VIDEO,
1742  .config_props = config_input_ref,
1743  },
1744 };
1745 
1746 static const AVFilterPad ssim360_outputs[] = {
1747  {
1748  .name = "default",
1749  .type = AVMEDIA_TYPE_VIDEO,
1750  .config_props = config_output,
1751  },
1752 };
1753 
1755  .p.name = "ssim360",
1756  .p.description = NULL_IF_CONFIG_SMALL("Calculate the SSIM between two 360 video streams."),
1757  .p.priv_class = &ssim360_class,
1758  .preinit = ssim360_framesync_preinit,
1759  .init = init,
1760  .uninit = uninit,
1761  .activate = activate,
1762  .priv_size = sizeof(SSIM360Context),
1766 };
tape_supports_projection
static int tape_supports_projection(int projection)
Definition: vf_ssim360.c:975
FF_ALLOCZ_TYPED_ARRAY
#define FF_ALLOCZ_TYPED_ARRAY(p, nelem)
Definition: internal.h:78
SSIM360Context::temp
int * temp
Definition: vf_ssim360.c:165
HeatmapList
Definition: vf_ssim360.c:110
map_uninit
static void map_uninit(Map2D *map)
Definition: vf_ssim360.c:291
map_init
static int map_init(Map2D *map, int w, int h)
Definition: vf_ssim360.c:296
ff_framesync_configure
int ff_framesync_configure(FFFrameSync *fs)
Configure a frame sync structure.
Definition: framesync.c:137
AVPixelFormat
AVPixelFormat
Pixel format.
Definition: pixfmt.h:71
SampleParams::expand_coef
float expand_coef
Definition: vf_ssim360.c:124
M_PI_4_F
#define M_PI_4_F
Definition: vf_ssim360.c:69
av_clip
#define av_clip
Definition: common.h:100
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
opt.h
SSIM360Context::is_rgb
int is_rgb
Definition: vf_ssim360.c:159
SSIM360Context::nb_ssim_frames
uint64_t nb_ssim_frames
Definition: vf_ssim360.c:166
ff_framesync_uninit
void ff_framesync_uninit(FFFrameSync *fs)
Free all memory currently allocated.
Definition: framesync.c:301
comp
static void comp(unsigned char *dst, ptrdiff_t dst_stride, unsigned char *src, ptrdiff_t src_stride, int add)
Definition: eamad.c:79
ff_filter_frame
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1067
av_pix_fmt_desc_get
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:3456
SSIM360Context::ref_projection
int ref_projection
Definition: vf_ssim360.c:174
get_projected_map
static void get_projected_map(float phi, float theta, SampleParams *p, BilinearMap *m)
Definition: vf_ssim360.c:949
SSIM360Context::heatmaps
HeatmapList * heatmaps
Definition: vf_ssim360.c:186
STEREO_FORMAT_MONO
@ STEREO_FORMAT_MONO
Definition: vf_ssim360.c:92
int64_t
long long int64_t
Definition: coverity.c:34
compute_bilinear_map
static void compute_bilinear_map(SampleParams *p, BilinearMap *m, float x, float y)
Definition: vf_ssim360.c:716
inlink
The exact code depends on how similar the blocks are and how related they are to the and needs to apply these operations to the correct inlink or outlink if there are several Macros are available to factor that when no extra processing is inlink
Definition: filter_design.txt:212
SSIM360Context::comps
char comps[4]
Definition: vf_ssim360.c:153
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:434
HeatmapList::next
struct HeatmapList * next
Definition: vf_ssim360.c:112
pixdesc.h
FRONT
#define FRONT
Definition: vf_ssim360.c:61
M_PI_2
#define M_PI_2
Definition: mathematics.h:73
SSIM360Context::coefs
double coefs[4]
Definition: vf_ssim360.c:152
AVOption
AVOption.
Definition: opt.h:429
SSIM360Context::main_projection
int main_projection
Definition: vf_ssim360.c:175
b
#define b
Definition: input.c:42
ssim360_plane_8bit
static double ssim360_plane_8bit(uint8_t *main, int main_stride, uint8_t *ref, int ref_stride, int width, int height, void *temp, int max, Map2D density)
Definition: vf_ssim360.c:510
SampleParams
Definition: vf_ssim360.c:115
filters.h
data
const char data[16]
Definition: mxf.c:149
ssim360_db
static double ssim360_db(double ssim360, double weight)
Definition: vf_ssim360.c:541
SSIM360Context::ref_pad
float ref_pad
Definition: vf_ssim360.c:178
SSIM360Context::main_stereo_format
int main_stereo_format
Definition: vf_ssim360.c:177
Map2D::w
int w
Definition: vf_ssim360.c:106
AV_PIX_FMT_YUV440P
@ AV_PIX_FMT_YUV440P
planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples)
Definition: pixfmt.h:106
max
#define max(a, b)
Definition: cuda_runtime.h:33
SampleParams::y_image_offset
int y_image_offset
Definition: vf_ssim360.c:120
AVDictionary
Definition: dict.c:32
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
AVFilter::name
const char * name
Filter name.
Definition: avfilter.h:220
get_tape_angular_resolution
static float get_tape_angular_resolution(int projection, float expand_coef, int image_width, int image_height)
Definition: vf_ssim360.c:989
FFFrameSync
Frame sync structure.
Definition: framesync.h:168
SSIM360Context::ssim360_total
double ssim360_total
Definition: vf_ssim360.c:168
av_strlcatf
size_t av_strlcatf(char *dst, size_t size, const char *fmt,...)
Definition: avstring.c:103
ssim360_inputs
static const AVFilterPad ssim360_inputs[]
Definition: vf_ssim360.c:1733
get_cubemap_face_map
static int get_cubemap_face_map(float axis_vec_x, float axis_vec_y, float axis_vec_z, float *face_x, float *face_y)
Definition: vf_ssim360.c:832
get_barrel_map
static void get_barrel_map(float phi, float theta, float *x, float *y)
Definition: vf_ssim360.c:755
ssim360_end1
static float ssim360_end1(int s1, int s2, int ss, int s12)
Definition: vf_ssim360.c:423
SSIM360_HIST_SIZE
#define SSIM360_HIST_SIZE
Definition: vf_ssim360.c:81
SSIM360Context::ssim360
double ssim360[4]
Definition: vf_ssim360.c:168
SSIM360Context::default_heatmap_w
int default_heatmap_w
Definition: vf_ssim360.c:182
SSIM360Context::ref_planeheight
int ref_planeheight[4]
Definition: vf_ssim360.c:188
SSIM360Context::stats_file
FILE * stats_file
Definition: vf_ssim360.c:147
FRAMESYNC_DEFINE_CLASS
FRAMESYNC_DEFINE_CLASS(ssim360, SSIM360Context, fs)
BilinearMap
Definition: vf_ssim360.c:127
cosf
#define cosf(x)
Definition: libm.h:80
fail
#define fail()
Definition: checkasm.h:224
FILTER_PIXFMTS_ARRAY
#define FILTER_PIXFMTS_ARRAY(array)
Definition: filters.h:244
PROJECTION_BARREL
@ PROJECTION_BARREL
Definition: vf_ssim360.c:99
weight
const h264_weight_func weight
Definition: h264dsp_init.c:33
val
static double val(void *priv, double ch)
Definition: aeval.c:77
SSIM360Context::max
int max
Definition: vf_ssim360.c:154
uninit
static av_cold void uninit(AVFilterContext *ctx)
Definition: vf_ssim360.c:1669
get_cubemap32_map
static void get_cubemap32_map(float phi, float theta, float *x, float *y)
Definition: vf_ssim360.c:872
ss
#define ss(width, name, subs,...)
Definition: cbs_vp9.c:202
BilinearMap::brf
int brf
Definition: vf_ssim360.c:139
SSIM360Context::angular_resolution
float angular_resolution[4][2]
Definition: vf_ssim360.c:194
AVFilterPad
A filter pad used for either input or output.
Definition: filters.h:40
ssim360_options
static const AVOption ssim360_options[]
Definition: vf_ssim360.c:205
AV_PIX_FMT_YUVJ411P
@ AV_PIX_FMT_YUVJ411P
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) full scale (JPEG), deprecated in favor ...
Definition: pixfmt.h:283
do_ssim360
static int do_ssim360(FFFrameSync *fs)
Definition: vf_ssim360.c:1134
STEREO_FORMAT_TB
@ STEREO_FORMAT_TB
Definition: vf_ssim360.c:90
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
av_cold
#define av_cold
Definition: attributes.h:111
main
int main
Definition: dovi_rpuenc.c:38
init
static av_cold int init(AVFilterContext *ctx)
Definition: vf_ssim360.c:1318
FFFilter
Definition: filters.h:267
get_barrel_split_map
static void get_barrel_split_map(float phi, float theta, float expand_coef, float *x, float *y)
Definition: vf_ssim360.c:782
AV_PIX_FMT_YUVJ422P
@ AV_PIX_FMT_YUVJ422P
planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV422P and setting col...
Definition: pixfmt.h:86
float
float
Definition: af_crystalizer.c:122
s
#define s(width, name)
Definition: cbs_vp9.c:198
FILTER_OUTPUTS
#define FILTER_OUTPUTS(array)
Definition: filters.h:265
SSIM360Context::compute_chroma
int compute_chroma
Definition: vf_ssim360.c:157
AV_CEIL_RSHIFT
#define AV_CEIL_RSHIFT(a, b)
Definition: common.h:60
RIGHT
#define RIGHT
Copyright (c) 2015-2021, Facebook, Inc.
Definition: vf_ssim360.c:57
SSIM360Context::main_planewidth
int main_planewidth[4]
Definition: vf_ssim360.c:189
BilinearMap::tli
int tli
Definition: vf_ssim360.c:129
ff_filter_link
static FilterLink * ff_filter_link(AVFilterLink *link)
Definition: filters.h:199
config_input_main
static int config_input_main(AVFilterLink *inlink)
Definition: vf_ssim360.c:1348
av_strtok
char * av_strtok(char *s, const char *delim, char **saveptr)
Split the string into several tokens which can be accessed by successive calls to av_strtok().
Definition: avstring.c:179
M_SQRT2_F
#define M_SQRT2_F
Definition: vf_ssim360.c:70
FIXED_POINT_PRECISION
#define FIXED_POINT_PRECISION
Definition: vf_ssim360.c:78
SSIM360Context
Definition: vf_ssim360.c:142
SampleParams::x_image_offset
int x_image_offset
Definition: vf_ssim360.c:119
ctx
static AVFormatContext * ctx
Definition: movenc.c:49
SSIM360Context::use_tape
int use_tape
Definition: vf_ssim360.c:180
PROJECTION_EQUIRECT
@ PROJECTION_EQUIRECT
Definition: vf_ssim360.c:101
ssim360_endn_16bit
static double ssim360_endn_16bit(const int64_t(*sum0)[4], const int64_t(*sum1)[4], int width, int max, double *density_map, int map_width, double *total_weight)
Definition: vf_ssim360.c:440
SSIM360Context::main_planeheight
int main_planeheight[4]
Definition: vf_ssim360.c:190
AV_PIX_FMT_YUV420P
@ AV_PIX_FMT_YUV420P
planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
Definition: pixfmt.h:73
BilinearMap::bri
int bri
Definition: vf_ssim360.c:132
key
const char * key
Definition: hwcontext_opencl.c:189
av_mallocz
#define av_mallocz(s)
Definition: tableprint_vlc.h:31
file_open.h
AV_PIX_FMT_YUVJ444P
@ AV_PIX_FMT_YUVJ444P
planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV444P and setting col...
Definition: pixfmt.h:87
SSIM360Context::ref_planewidth
int ref_planewidth[4]
Definition: vf_ssim360.c:187
SSIM360Context::fs
FFFrameSync fs
Definition: vf_ssim360.c:145
FFABS
#define FFABS(a)
Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they are not representable ...
Definition: common.h:74
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:76
Map2D::value
double * value
Definition: vf_ssim360.c:107
SSIM360Context::rgba_map
uint8_t rgba_map[4]
Definition: vf_ssim360.c:160
metadata
Stream codec metadata
Definition: ogg-flac-chained-meta.txt:2
SampleParams::planeheight
int planeheight
Definition: vf_ssim360.c:118
NULL
#define NULL
Definition: coverity.c:32
fs
#define fs(width, name, subs,...)
Definition: cbs_vp9.c:200
SSIM360Context::ssim360_percentile_sum
double ssim360_percentile_sum[4][256]
Definition: vf_ssim360.c:171
vars
static const uint8_t vars[2][12]
Definition: camellia.c:183
SampleParams::projection
int projection
Definition: vf_ssim360.c:123
BilinearMap::trf
int trf
Definition: vf_ssim360.c:137
AV_PIX_FMT_YUVJ420P
@ AV_PIX_FMT_YUVJ420P
planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting col...
Definition: pixfmt.h:85
ROUNDED_DIV
#define ROUNDED_DIV(a, b)
Definition: common.h:58
PROJECTION_CUBEMAP23
@ PROJECTION_CUBEMAP23
Definition: vf_ssim360.c:98
StereoFormat
StereoFormat
Definition: vf_ssim360.c:89
double
double
Definition: af_crystalizer.c:132
M_PI_F
#define M_PI_F
Definition: vf_ssim360.c:67
sinf
#define sinf(x)
Definition: libm.h:421
LEFT
#define LEFT
Definition: vf_ssim360.c:58
av_clipf
av_clipf
Definition: af_crystalizer.c:122
get_heat
static float get_heat(HeatmapList *heatmaps, float angular_resoluation, float norm_tape_pos)
Definition: vf_ssim360.c:625
AV_PIX_FMT_GRAY8
@ AV_PIX_FMT_GRAY8
Y , 8bpp.
Definition: pixfmt.h:81
PERCENTILE_LIST
static const double PERCENTILE_LIST[]
Definition: vf_ssim360.c:84
TOP
#define TOP
Definition: vf_ssim360.c:59
SSIM360Context::nb_components
int nb_components
Definition: vf_ssim360.c:151
c
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
DEFAULT_EXPANSION_COEF
#define DEFAULT_EXPANSION_COEF
Definition: vf_ssim360.c:72
SSIM360Context::ssim360_plane
double(* ssim360_plane)(uint8_t *main, int main_stride, uint8_t *ref, int ref_stride, int width, int height, void *temp, int max, Map2D density)
Definition: vf_ssim360.c:195
SampleParams::y_image_range
int y_image_range
Definition: vf_ssim360.c:122
f
f
Definition: af_crystalizer.c:122
M_PI_2_F
#define M_PI_2_F
Definition: vf_ssim360.c:68
NULL_IF_CONFIG_SMALL
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:94
ff_framesync_init_dualinput
int ff_framesync_init_dualinput(FFFrameSync *fs, AVFilterContext *parent)
Initialize a frame sync structure for dualinput.
Definition: framesync.c:372
PROJECTION_BARREL_SPLIT
@ PROJECTION_BARREL_SPLIT
Definition: vf_ssim360.c:100
master
const char * master
Definition: vf_curves.c:130
height
#define height
Definition: dsp.h:89
ssim360_endn_8bit
static double ssim360_endn_8bit(const int(*sum0)[4], const int(*sum1)[4], int width, double *density_map, int map_width, double *total_weight)
Definition: vf_ssim360.c:460
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
SampleParams::x_image_range
int x_image_range
Definition: vf_ssim360.c:121
av_err2str
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition: error.h:122
for
for(k=2;k<=8;++k)
Definition: h264pred_template.c:424
ssim360_tape
static double ssim360_tape(uint8_t *main, BilinearMap *main_maps, uint8_t *ref, BilinearMap *ref_maps, int tape_length, int max_value, void *temp, double *ssim360_hist, double *ssim360_hist_net, float angular_resolution, HeatmapList *heatmaps)
Definition: vf_ssim360.c:648
ssim360_4x4x2_tape
static void ssim360_4x4x2_tape(const uint8_t *main, BilinearMap *main_maps, const uint8_t *ref, BilinearMap *ref_maps, int offset_y, int max_value, int(*sums)[4])
Definition: vf_ssim360.c:576
generate_tape_maps
static int generate_tape_maps(SSIM360Context *s, AVFrame *main, const AVFrame *ref)
Definition: vf_ssim360.c:1074
FLAGS
#define FLAGS
Definition: vf_ssim360.c:203
PF
#define PF(suf)
Definition: vf_ssim360.c:1720
ssim360_4x4xn_8bit
static void ssim360_4x4xn_8bit(const uint8_t *main, ptrdiff_t main_stride, const uint8_t *ref, ptrdiff_t ref_stride, int(*sums)[4], int width)
Definition: vf_ssim360.c:378
PROJECTION_N
@ PROJECTION_N
Definition: vf_ssim360.c:102
map_alloc
static int map_alloc(HeatmapList **pl, int w, int h)
Definition: vf_ssim360.c:322
ssim360_4x4xn_16bit
static void ssim360_4x4xn_16bit(const uint8_t *main8, ptrdiff_t main_stride, const uint8_t *ref8, ptrdiff_t ref_stride, int64_t(*sums)[4], int width)
Definition: vf_ssim360.c:342
a
The reader does not expect b to be semantically here and if the code is changed by maybe adding a a division or other the signedness will almost certainly be mistaken To avoid this confusion a new type was SUINT is the C unsigned type but it holds a signed int to use the same example SUINT a
Definition: undefined.txt:41
line
Definition: graph2dot.c:48
Map2D::h
int h
Definition: vf_ssim360.c:106
BilinearMap::blf
int blf
Definition: vf_ssim360.c:138
SSIM360Context::stats_file_str
char * stats_file_str
Definition: vf_ssim360.c:148
M_PI
#define M_PI
Definition: mathematics.h:67
AV_LOG_INFO
#define AV_LOG_INFO
Standard information.
Definition: log.h:221
config_input_ref
static int config_input_ref(AVFilterLink *inlink)
Definition: vf_ssim360.c:1553
AV_OPT_TYPE_FLOAT
@ AV_OPT_TYPE_FLOAT
Underlying C type is float.
Definition: opt.h:271
SSIM360Context::frame_skip_ratio
uint64_t frame_skip_ratio
Definition: vf_ssim360.c:163
generate_eye_tape_map
static int generate_eye_tape_map(SSIM360Context *s, int plane, int eye, SampleParams *ref_sample_params, SampleParams *main_sample_params)
Definition: vf_ssim360.c:1017
BACK
#define BACK
Definition: vf_ssim360.c:62
config_output
static int config_output(AVFilterLink *outlink)
Definition: vf_ssim360.c:1594
BilinearMap::tri
int tri
Definition: vf_ssim360.c:130
av_malloc_array
#define av_malloc_array(a, b)
Definition: tableprint_vlc.h:32
SSIM360Context::ssim360_hist
double * ssim360_hist[4]
Definition: vf_ssim360.c:169
value
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf default value
Definition: writing_filters.txt:86
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
AV_PIX_FMT_YUVJ440P
@ AV_PIX_FMT_YUVJ440P
planar YUV 4:4:0 full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV440P and setting color_range
Definition: pixfmt.h:107
AVFilterPad::name
const char * name
Pad name.
Definition: filters.h:46
avpriv_fopen_utf8
FILE * avpriv_fopen_utf8(const char *path, const char *mode)
Open a file using a UTF-8 filename.
Definition: file_open.c:160
av_calloc
void * av_calloc(size_t nmemb, size_t size)
Definition: mem.c:264
OFFSET
#define OFFSET(x)
Definition: vf_ssim360.c:202
BilinearMap::tlf
int tlf
Definition: vf_ssim360.c:136
Map2D
Definition: vf_ssim360.c:105
BARREL_PHI_RANGE
#define BARREL_PHI_RANGE
Definition: vf_ssim360.c:75
SSIM360Context::ref_stereo_format
int ref_stereo_format
Definition: vf_ssim360.c:176
SSIM360Context::tape_length
int tape_length[4]
Definition: vf_ssim360.c:191
ret
ret
Definition: filter_design.txt:187
FFSWAP
#define FFSWAP(type, a, b)
Definition: macros.h:52
parse_heatmaps
static int parse_heatmaps(void *logctx, HeatmapList **proot, const char *data, int w, int h)
Definition: vf_ssim360.c:1256
SSIM360Context::density
Map2D density
Definition: vf_ssim360.c:185
FILTER_INPUTS
#define FILTER_INPUTS(array)
Definition: filters.h:264
get_radius_between_negative_and_positive_pi
static float get_radius_between_negative_and_positive_pi(float theta)
Definition: vf_ssim360.c:611
get_rotated_cubemap_map
static void get_rotated_cubemap_map(float phi, float theta, float expand_coef, float *x, float *y)
Definition: vf_ssim360.c:896
ssim360_pixfmts
static enum AVPixelFormat ssim360_pixfmts[]
Definition: vf_ssim360.c:1721
SSIM360Context::heatmap_str
char * heatmap_str
Definition: vf_ssim360.c:181
STEREO_FORMAT_N
@ STEREO_FORMAT_N
Definition: vf_ssim360.c:93
ssim360_outputs
static const AVFilterPad ssim360_outputs[]
Definition: vf_ssim360.c:1746
framesync.h
SSIM360Context::nb_net_frames
uint64_t nb_net_frames
Definition: vf_ssim360.c:167
AV_PIX_FMT_NONE
@ AV_PIX_FMT_NONE
Definition: pixfmt.h:72
AV_OPT_TYPE_INT
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition: opt.h:259
avfilter.h
av_get_token
char * av_get_token(const char **buf, const char *term)
Unescape the given string until a non escaped terminating char, and return the token corresponding to...
Definition: avstring.c:143
STEREO_FORMAT_LR
@ STEREO_FORMAT_LR
Definition: vf_ssim360.c:91
map_list_free
static void map_list_free(HeatmapList **pl)
Definition: vf_ssim360.c:308
PROJECTION_CUBEMAP32
@ PROJECTION_CUBEMAP32
Definition: vf_ssim360.c:97
SSIM360Context::default_heatmap_h
int default_heatmap_h
Definition: vf_ssim360.c:183
BilinearMap::bli
int bli
Definition: vf_ssim360.c:131
ref
static int ref[MAX_W *MAX_W]
Definition: jpeg2000dwt.c:117
temp
else temp
Definition: vf_mcdeint.c:271
BARREL_THETA_RANGE
#define BARREL_THETA_RANGE
Definition: vf_ssim360.c:74
Windows::Graphics::DirectX::Direct3D11::p
IDirect3DDxgiInterfaceAccess _COM_Outptr_ void ** p
Definition: vsrc_gfxcapture_winrt.hpp:53
AV_PIX_FMT_YUV444P
@ AV_PIX_FMT_YUV444P
planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
Definition: pixfmt.h:78
AVFilterContext
An instance of a filter.
Definition: avfilter.h:274
ssim360_end1x
static float ssim360_end1x(int64_t s1, int64_t s2, int64_t ss, int64_t s12, int max)
Definition: vf_ssim360.c:407
AV_PIX_FMT_GBRP
@ AV_PIX_FMT_GBRP
planar GBR 4:4:4 24bpp
Definition: pixfmt.h:165
SampleParams::planewidth
int planewidth
Definition: vf_ssim360.c:117
desc
const char * desc
Definition: libsvtav1.c:82
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:200
FFFilter::p
AVFilter p
The public AVFilter.
Definition: filters.h:271
AV_PIX_FMT_YUV422P
@ AV_PIX_FMT_YUV422P
planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
Definition: pixfmt.h:77
mem.h
BOTTOM
#define BOTTOM
Definition: vf_ssim360.c:60
AVPixFmtDescriptor
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition: pixdesc.h:69
w
uint8_t w
Definition: llvidencdsp.c:39
map
const VDPAUPixFmtMap * map
Definition: hwcontext_vdpau.c:71
generate_density_map
static int generate_density_map(SSIM360Context *s, int w, int h)
Definition: vf_ssim360.c:1375
SampleParams::stride
int stride
Definition: vf_ssim360.c:116
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
av_dict_set
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags)
Set the given entry in *pm, overwriting an existing entry.
Definition: dict.c:86
AV_PIX_FMT_YUV411P
@ AV_PIX_FMT_YUV411P
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)
Definition: pixfmt.h:80
ff_fill_rgba_map
int ff_fill_rgba_map(uint8_t *rgba_map, enum AVPixelFormat pix_fmt)
Definition: drawutils.c:80
get_bilinear_sample
static int get_bilinear_sample(const uint8_t *data, BilinearMap *m, int max_value)
Definition: vf_ssim360.c:546
ssim360_plane_16bit
static double ssim360_plane_16bit(uint8_t *main, int main_stride, uint8_t *ref, int ref_stride, int width, int height, void *temp, int max, Map2D density)
Definition: vf_ssim360.c:478
AV_PIX_FMT_YUV410P
@ AV_PIX_FMT_YUV410P
planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples)
Definition: pixfmt.h:79
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
set_meta
static void set_meta(AVDictionary **metadata, const char *key, char comp, float d)
Definition: vf_ssim360.c:278
activate
static int activate(AVFilterContext *ctx)
Definition: vf_ssim360.c:1663
h
h
Definition: vp9dsp_template.c:2070
SSIM360Context::main_tape_map
BilinearMap * main_tape_map[4][2]
Definition: vf_ssim360.c:193
ff_framesync_activate
int ff_framesync_activate(FFFrameSync *fs)
Examine the frames in the filter's input and try to produce output.
Definition: framesync.c:352
avstring.h
ff_framesync_dualinput_get
int ff_framesync_dualinput_get(FFFrameSync *fs, AVFrame **f0, AVFrame **f1)
Definition: framesync.c:390
AV_OPT_TYPE_STRING
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
Definition: opt.h:276
width
#define width
Definition: dsp.h:89
drawutils.h
Projection
Projection
Definition: vf_ssim360.c:96
AV_OPT_TYPE_CONST
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
Definition: opt.h:299
snprintf
#define snprintf
Definition: snprintf.h:34
SSIM360Context::main_pad
float main_pad
Definition: vf_ssim360.c:179
SSIM360Context::ref_tape_map
BilinearMap * ref_tape_map[4][2]
Definition: vf_ssim360.c:192
ff_vf_ssim360
const FFFilter ff_vf_ssim360
Definition: vf_ssim360.c:1754
SSIM360Context::ssim360_hist_net
double ssim360_hist_net[4]
Definition: vf_ssim360.c:170
get_equirect_map
static void get_equirect_map(float phi, float theta, float *x, float *y)
Definition: vf_ssim360.c:748
HeatmapList::map
Map2D map
Definition: vf_ssim360.c:111