65 #define DEFAULT_HEATMAP_W 32
66 #define DEFAULT_HEATMAP_H 16
68 #define M_PI_F ((float)M_PI)
69 #define M_PI_2_F ((float)M_PI_2)
70 #define M_PI_4_F ((float)M_PI_4)
71 #define M_SQRT2_F ((float)M_SQRT2)
73 #define DEFAULT_EXPANSION_COEF 1.01f
75 #define BARREL_THETA_RANGE (DEFAULT_EXPANSION_COEF * 2.0f * M_PI_F)
76 #define BARREL_PHI_RANGE (DEFAULT_EXPANSION_COEF * M_PI_2_F)
79 #define FIXED_POINT_PRECISION 16
82 #define SSIM360_HIST_SIZE 131072
86 1.0, 0.9, 0.8, 0.7, 0.6,
87 0.5, 0.4, 0.3, 0.2, 0.1, 0, -1
197 uint8_t *
main,
int main_stride,
198 uint8_t *
ref,
int ref_stride,
203 #define OFFSET(x) offsetof(SSIM360Context, x)
204 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
207 {
"stats_file",
"Set file where to store per-frame difference information",
209 {
"f",
"Set file where to store per-frame difference information",
213 "Specifies if non-luma channels must be computed",
215 0, 1, .flags =
FLAGS },
217 {
"frame_skip_ratio",
218 "Specifies the number of frames to be skipped from evaluation, for every evaluated frame",
220 0, 1000000, .flags =
FLAGS },
222 {
"ref_projection",
"projection of the reference video",
233 {
"main_projection",
"projection of the main video",
237 {
"ref_stereo",
"stereo format of the reference video",
245 {
"main_stereo",
"stereo format of main video",
250 "Expansion (padding) coefficient for each cube face of the reference video",
254 "Expansion (padding) coeffiecient for each cube face of the main video",
258 "Specifies if the tape based SSIM 360 algorithm must be used independent of the input video types",
260 0, 1, .flags =
FLAGS },
263 "Heatmap data for view-based evaluation. For heatmap file format, please refer to EntSphericalVideoHeatmapData.",
266 {
"default_heatmap_width",
267 "Default heatmap dimension. Will be used when dimension is not specified in heatmap data.",
270 {
"default_heatmap_height",
271 "Default heatmap dimension. Will be used when dimension is not specified in heatmap data.",
344 const uint8_t *ref8, ptrdiff_t ref_stride,
345 int64_t (*sums)[4],
int width)
347 const uint16_t *main16 = (
const uint16_t *)main8;
348 const uint16_t *ref16 = (
const uint16_t *)ref8;
353 for (
int z = 0; z <
width; z++) {
354 uint64_t
s1 = 0,
s2 = 0,
ss = 0, s12 = 0;
356 for (
int y = 0; y < 4; y++) {
357 for (
int x = 0; x < 4; x++) {
358 unsigned a = main16[x + y * main_stride];
359 unsigned b = ref16[x + y * ref_stride];
380 const uint8_t *
ref, ptrdiff_t ref_stride,
381 int (*sums)[4],
int width)
383 for (
int z = 0; z <
width; z++) {
384 uint32_t
s1 = 0,
s2 = 0,
ss = 0, s12 = 0;
386 for (
int y = 0; y < 4; y++) {
387 for (
int x = 0; x < 4; x++) {
388 int a =
main[x + y * main_stride];
389 int b =
ref[x + y * ref_stride];
410 int64_t ssim_c1 = (int64_t)(.01 * .01 *
max *
max * 64 + .5);
411 int64_t ssim_c2 = (int64_t)(.03 * .03 *
max *
max * 64 * 63 + .5);
417 int64_t
vars = fss * 64 - fs1 * fs1 - fs2 * fs2;
418 int64_t covar = fs12 * 64 - fs1 * fs2;
420 return (
float)(2 * fs1 * fs2 + ssim_c1) * (
float)(2 * covar + ssim_c2)
421 / ((
float)(fs1 * fs1 + fs2 * fs2 + ssim_c1) * (
float)(
vars + ssim_c2));
426 static const int ssim_c1 = (
int)(.01*.01*255*255*64 + .5);
427 static const int ssim_c2 = (
int)(.03*.03*255*255*64*63 + .5);
433 int vars = fss * 64 - fs1 * fs1 - fs2 * fs2;
434 int covar = fs12 * 64 - fs1 * fs2;
436 return (
float)(2 * fs1 * fs2 + ssim_c1) * (
float)(2 * covar + ssim_c2)
437 / ((
float)(fs1 * fs1 + fs2 * fs2 + ssim_c1) * (
float)(
vars + ssim_c2));
443 double *density_map,
int map_width,
double *total_weight)
445 double ssim360 = 0.0,
weight;
448 weight = density_map ? density_map[(
int) ((0.5 +
i) /
width * map_width)] : 1.0;
450 sum0[
i][0] + sum0[
i + 1][0] + sum1[
i][0] + sum1[
i + 1][0],
451 sum0[
i][1] + sum0[
i + 1][1] + sum1[
i][1] + sum1[
i + 1][1],
452 sum0[
i][2] + sum0[
i + 1][2] + sum1[
i][2] + sum1[
i + 1][2],
453 sum0[
i][3] + sum0[
i + 1][3] + sum1[
i][3] + sum1[
i + 1][3],
462 double *density_map,
int map_width,
double *total_weight)
464 double ssim360 = 0.0,
weight;
467 weight = density_map ? density_map[(
int) ((0.5 +
i) /
width * map_width)] : 1.0;
469 sum0[
i][0] + sum0[
i + 1][0] + sum1[
i][0] + sum1[
i + 1][0],
470 sum0[
i][1] + sum0[
i + 1][1] + sum1[
i][1] + sum1[
i + 1][1],
471 sum0[
i][2] + sum0[
i + 1][2] + sum1[
i][2] + sum1[
i + 1][2],
472 sum0[
i][3] + sum0[
i + 1][3] + sum1[
i][3] + sum1[
i + 1][3]);
480 uint8_t *
ref,
int ref_stride,
485 double ssim360 = 0.0;
486 int64_t (*sum0)[4] =
temp;
487 int64_t (*sum1)[4] = sum0 + (
width >> 2) + 3;
488 double total_weight = 0.0;
493 for (
int y = 1; y <
height; y++) {
494 for (; z <= y; z++) {
495 FFSWAP(
void*, sum0, sum1);
497 &
ref[4 * z * ref_stride], ref_stride,
501 (
const int64_t (*)[4])sum0, (
const int64_t (*)[4])sum1,
504 density.
w, &total_weight);
507 return (
double) (ssim360 / total_weight);
512 uint8_t *
ref,
int ref_stride,
517 double ssim360 = 0.0;
519 int (*sum1)[4] = sum0 + (
width >> 2) + 3;
520 double total_weight = 0.0;
525 for (
int y = 1; y <
height; y++) {
526 for (; z <= y; z++) {
527 FFSWAP(
void*, sum0, sum1);
529 &
main[4 * z * main_stride], main_stride,
530 &
ref[4 * z * ref_stride], ref_stride,
534 (
const int (*)[4])sum0, (
const int (*)[4])sum1,
width - 1,
536 density.
w, &total_weight);
539 return (
double) (ssim360 / total_weight);
550 static const int inv_byte_mask = UINT_MAX << 8;
552 int tl, tr, bl, br, v;
554 if (max_value & inv_byte_mask) {
555 uint16_t *data16 = (uint16_t *)
data;
579 int offset_y,
int max_value,
int (*sums)[4])
584 for (
int z = 0; z < 2; z++) {
585 int s1 = 0,
s2 = 0,
ss = 0, s12 = 0;
588 for (
int y = offset_y; y < offset_y + 4; y++) {
589 int y_stride = y << 3;
590 for (
int x = offset_x; x < offset_x + 4; x++) {
591 int map_index = x + y_stride;
614 int floor_theta_by_2pi, floor_theta_by_pi;
617 floor_theta_by_2pi = (
int)(theta / (2.0
f *
M_PI_F)) - (theta < 0.0
f);
618 theta -= 2.0f *
M_PI_F * floor_theta_by_2pi;
621 floor_theta_by_pi = theta /
M_PI_F;
622 theta -= 2.0f *
M_PI_F * floor_theta_by_pi;
628 float pitch, yaw, norm_pitch, norm_yaw;
634 pitch = asinf(norm_tape_pos*2);
635 yaw =
M_PI_2_F * pitch / angular_resoluation;
639 norm_pitch = 1.0f - (pitch /
M_PI_F + 0.5f);
640 norm_yaw = yaw / 2.0f /
M_PI_F + 0.5f;
651 int tape_length,
int max_value,
void *
temp,
652 double *ssim360_hist,
double *ssim360_hist_net,
655 int horizontal_block_count = 2;
656 int vertical_block_count = tape_length >> 2;
660 double ssim360 = 0.0;
661 double sum_weight = 0.0;
664 int (*sum1)[4] = sum0 + horizontal_block_count + 3;
666 for (y = 1; y < vertical_block_count; y++) {
667 int fs1, fs2, fss, fs12, hist_index;
668 float norm_tape_pos,
weight;
669 double sample_ssim360;
671 for (; z <= y; z++) {
672 FFSWAP(
void*, sum0, sum1);
677 fs1 = sum0[0][0] + sum0[1][0] + sum1[0][0] + sum1[1][0];
678 fs2 = sum0[0][1] + sum0[1][1] + sum1[0][1] + sum1[1][1];
679 fss = sum0[0][2] + sum0[1][2] + sum1[0][2] + sum1[1][2];
680 fs12 = sum0[0][3] + sum0[1][3] + sum1[0][3] + sum1[1][3];
682 if (max_value > 255) {
684 double ssim_c1_d = .01*.01*64*max_value*max_value;
685 double ssim_c2_d = .03*.03*64*63*max_value*max_value;
687 double vars = 64. * fss - 1. * fs1 * fs1 - 1. * fs2 * fs2;
688 double covar = 64. * fs12 - 1.*fs1 * fs2;
689 sample_ssim360 = (2. * fs1 * fs2 + ssim_c1_d) * (2. * covar + ssim_c2_d)
690 / ((1. * fs1 * fs1 + 1. * fs2 * fs2 + ssim_c1_d) * (1. *
vars + ssim_c2_d));
692 static const int ssim_c1 = (
int)(.01*.01*255*255*64 + .5);
693 static const int ssim_c2 = (
int)(.03*.03*255*255*64*63 + .5);
695 int vars = fss * 64 - fs1 * fs1 - fs2 * fs2;
696 int covar = fs12 * 64 - fs1 * fs2;
697 sample_ssim360 = (
double)(2 * fs1 * fs2 + ssim_c1) * (
double)(2 * covar + ssim_c2)
704 norm_tape_pos = (y - 0.5f) / (vertical_block_count - 1.0
f) - 0.5f;
707 ssim360_hist[hist_index] +=
weight;
708 *ssim360_hist_net +=
weight;
710 ssim360 += (sample_ssim360 *
weight);
714 return ssim360 / sum_weight;
726 int x_floor = x_image;
727 int y_floor = y_image;
728 float x_diff = x_image - x_floor;
729 float y_diff = y_image - y_floor;
731 int x_ceil = x_floor + (x_diff > 1e-6);
732 int y_ceil = y_floor + (y_diff > 1e-6);
733 float x_inv_diff = 1.0f - x_diff;
734 float y_inv_diff = 1.0f - y_diff;
743 m->
tlf = x_inv_diff * y_inv_diff * fixed_point_scale;
744 m->
trf = x_diff * y_inv_diff * fixed_point_scale;
745 m->
blf = x_inv_diff * y_diff * fixed_point_scale;
746 m->
brf = x_diff * y_diff * fixed_point_scale;
751 *x = 0.5f + theta / (2.0f *
M_PI_F);
758 float abs_phi =
FFABS(phi);
769 float circle_x = radial_ratio *
sinf(theta);
770 float circle_y = radial_ratio *
cosf(theta);
771 float offset_y = 0.25f;
778 *x = 0.8f + 0.1f * (1.0f + circle_x);
779 *y = offset_y + 0.25f * circle_y;
785 float abs_phi =
FFABS(phi);
789 float radian_pi_theta = theta /
M_PI_F + 0.5f;
792 if (radian_pi_theta < 0.0
f)
793 radian_pi_theta += 2.0f;
796 vFace = radian_pi_theta >= 1.0f;
800 *x = 2.0f / 3.0f * (0.5f + (radian_pi_theta - vFace - 0.5f) / expand_coef);
802 *y = 0.25f + 0.5f * vFace - phi / (
M_PI_F * expand_coef);
806 float radial_ratio =
cosf(abs_phi) / (
sinf(abs_phi) * expand_coef);
807 float circle_x = radial_ratio *
sinf(theta);
808 float circle_y = radial_ratio *
cosf(theta);
809 float offset_y = 0.25f;
814 circle_y = (circle_y >= 0.0f) ? (1 - circle_y) : (-1 - circle_y);
827 *x = 2.0f / 3.0f + 0.5f / 3.0f * (1.0f + circle_x);
828 *y = offset_y + 0.25f * circle_y / expand_coef;
833 static int get_cubemap_face_map(
float axis_vec_x,
float axis_vec_y,
float axis_vec_z,
float *face_x,
float *face_y)
840 if (
FFABS(axis_vec_y) > 0.577
f) {
841 float x_hit = axis_vec_x /
FFABS(axis_vec_y);
842 float z_hit = axis_vec_z / axis_vec_y;
853 if (
FFABS(axis_vec_x) > 0.577
f) {
854 float z_hit = -axis_vec_z / axis_vec_x;
855 float y_hit = axis_vec_y /
FFABS(axis_vec_x);
866 *face_x = axis_vec_x / axis_vec_z;
868 *face_y = -axis_vec_y /
FFABS(axis_vec_z);
879 static const int face_projection_map[] = {
884 float axis_vec_x =
cosf(phi) *
sinf(theta);
885 float axis_vec_y =
sinf(phi);
886 float axis_vec_z =
cosf(phi) *
cosf(theta);
887 float face_x = 0, face_y = 0;
890 float x_offset = 1.f / 3.f * (face_projection_map[face_index] % 3);
891 float y_offset = .5f * (face_projection_map[face_index] / 3);
904 static const int face_projection_map[] = {
910 float axis_yaw_vec_x, axis_yaw_vec_y, axis_yaw_vec_z;
911 float axis_pitch_vec_z, axis_pitch_vec_y;
912 float x_offset, y_offset;
913 float face_x = 0, face_y = 0;
921 axis_yaw_vec_x =
cosf(phi) *
sinf(theta);
922 axis_yaw_vec_y =
sinf(phi);
923 axis_yaw_vec_z =
cosf(phi) *
cosf(theta);
926 axis_pitch_vec_z = (axis_yaw_vec_z - axis_yaw_vec_y) /
M_SQRT2_F;
927 axis_pitch_vec_y = (axis_yaw_vec_y + axis_yaw_vec_z) /
M_SQRT2_F;
929 face_index =
get_cubemap_face_map(axis_yaw_vec_x, axis_pitch_vec_y, axis_pitch_vec_z, &face_x, &face_y);
932 if (face_index ==
LEFT || face_index ==
FRONT || face_index ==
RIGHT) {
934 float upright_y = face_y;
937 }
else if (face_index ==
TOP || face_index ==
BOTTOM) {
943 x_offset = .5f * (face_projection_map[face_index] & 1);
944 y_offset = 1.f / 3.f * (face_projection_map[face_index] >> 1);
946 *x = x_offset + (face_x / expand_coef + 1.f) / 4.
f;
947 *y = y_offset + (face_y / expand_coef + 1.f) / 6.
f;
1001 return expand_coef / (
M_SQRT2_F * image_width / 4.f);
1008 return FFMAX((expand_coef *
M_PI_F) / (2.0
f / 3.0
f * image_width),
1009 expand_coef *
M_PI_2_F / (image_height / 2.0
f));
1024 int ref_image_height = ref_sample_params->
y_image_range + 1;
1026 float angular_resolution =
1028 ref_image_width, ref_image_height);
1030 float conversion_factor =
M_PI_2_F / (angular_resolution * angular_resolution);
1031 float start_phi = -
M_PI_2_F + 4.0f * angular_resolution;
1032 float start_x = conversion_factor *
sinf(start_phi);
1033 float end_phi =
M_PI_2_F - 3.0f * angular_resolution;
1034 float end_x = conversion_factor *
sinf(end_phi);
1035 float x_range = end_x - start_x;
1038 int tape_length =
s->tape_length[plane] = ((
int)
ROUNDED_DIV(x_range, 4)) << 2;
1042 if (!
s->ref_tape_map[plane][eye] || !
s->main_tape_map[plane][eye])
1045 s->angular_resolution[plane][eye] = angular_resolution;
1048 for (
int y_index = 0; y_index < tape_length; y_index ++) {
1049 int y_stride = y_index << 3;
1051 float x = start_x + x_range * (y_index / (tape_length - 1.0f));
1053 float mid_phi = asinf(x / conversion_factor);
1055 float theta = mid_phi *
M_PI_2_F / angular_resolution;
1058 for (
int x_index = 0; x_index < 8; x_index ++) {
1059 float phi = mid_phi + angular_resolution * (3.0f - x_index);
1060 int tape_index = y_stride + x_index;
1061 get_projected_map(phi, theta, ref_sample_params, &
s->ref_tape_map [plane][eye][tape_index]);
1062 get_projected_map(phi, theta, main_sample_params, &
s->main_tape_map[plane][eye][tape_index]);
1073 int ref_stereo_format =
s->ref_stereo_format;
1074 int main_stereo_format =
s->main_stereo_format;
1076 int min_eye_count = 1 + are_both_stereo;
1079 for (
int i = 0;
i <
s->nb_components;
i ++) {
1080 int ref_width =
s->ref_planewidth[
i];
1081 int ref_height =
s->ref_planeheight[
i];
1082 int main_width =
s->main_planewidth[
i];
1083 int main_height =
s->main_planeheight[
i];
1090 int ref_image_width = is_ref_LR ? ref_width >> 1 : ref_width;
1091 int ref_image_height = is_ref_TB ? ref_height >> 1 : ref_height;
1092 int main_image_width = is_main_LR ? main_width >> 1 : main_width;
1093 int main_image_height = is_main_TB ? main_height >> 1 : main_height;
1095 for (
int eye = 0; eye < min_eye_count; eye ++) {
1098 .planewidth = ref_width,
1099 .planeheight = ref_height,
1100 .x_image_range = ref_image_width - 1,
1101 .y_image_range = ref_image_height - 1,
1102 .x_image_offset = is_ref_LR * eye * ref_image_width,
1103 .y_image_offset = is_ref_TB * eye * ref_image_height,
1104 .projection =
s->ref_projection,
1105 .expand_coef = 1.f +
s->ref_pad,
1110 .planewidth = main_width,
1111 .planeheight = main_height,
1112 .x_image_range = main_image_width - 1,
1113 .y_image_range = main_image_height - 1,
1114 .x_image_offset = is_main_LR * eye * main_image_width,
1115 .y_image_offset = is_main_TB * eye * main_image_height,
1116 .projection =
s->main_projection,
1117 .expand_coef = 1.f +
s->main_pad,
1135 double c[4], ssim360v = 0.0, ssim360p50 = 0.0;
1137 int need_frame_skip =
s->nb_net_frames % (
s->frame_skip_ratio + 1);
1146 if (need_frame_skip)
1149 metadata = &
master->metadata;
1151 if (
s->use_tape && !
s->tape_length[0]) {
1157 for (
i = 0;
i <
s->nb_components;
i++) {
1160 ref->data[
i],
s->ref_tape_map [
i][0],
1161 s->tape_length[
i],
s->max,
s->temp,
1162 s->ssim360_hist[
i], &
s->ssim360_hist_net[
i],
1163 s->angular_resolution[
i][0],
s->heatmaps);
1165 if (
s->ref_tape_map[
i][1]) {
1167 ref->data[
i],
s->ref_tape_map[
i][1],
1168 s->tape_length[
i],
s->max,
s->temp,
1169 s->ssim360_hist[
i], &
s->ssim360_hist_net[
i],
1170 s->angular_resolution[
i][1],
s->heatmaps);
1176 s->ref_planewidth[
i],
s->ref_planeheight[
i],
1177 s->temp,
s->max,
s->density);
1180 s->ssim360[
i] +=
c[
i];
1181 ssim360v +=
s->coefs[
i] *
c[
i];
1184 s->nb_ssim_frames++;
1187 h_ptr =
s->heatmaps;
1188 s->heatmaps =
s->heatmaps->next;
1191 s->ssim360_total += ssim360v;
1195 int i, p, hist_indices[4];
1196 double hist_weight[4];
1198 for (
i = 0;
i <
s->nb_components;
i++) {
1204 for (
i = 0;
i <
s->nb_components;
i++) {
1205 double target_weight, ssim360p;
1209 target_weight =
FFMAX(target_weight, 1);
1210 while(hist_indices[
i] >= 0 && hist_weight[
i] < target_weight) {
1211 hist_weight[
i] +=
s->ssim360_hist[
i][hist_indices[
i]];
1217 ssim360p50 +=
s->coefs[
i] * ssim360p;
1218 s->ssim360_percentile_sum[
i][p] += ssim360p;
1222 for (
i = 0;
i <
s->nb_components;
i++) {
1224 s->ssim360_hist_net[
i] = 0;
1227 for (
i = 0;
i <
s->nb_components;
i++) {
1228 int cidx =
s->is_rgb ?
s->rgba_map[
i] :
i;
1229 set_meta(metadata,
"lavfi.ssim360.",
s->comps[
i],
c[cidx]);
1233 set_meta(metadata,
"lavfi.ssim360.All", 0, ssim360p50);
1236 if (
s->stats_file) {
1237 fprintf(
s->stats_file,
"n:%"PRId64
" ",
s->nb_ssim_frames);
1239 for (
i = 0;
i <
s->nb_components;
i++) {
1240 int cidx =
s->is_rgb ?
s->rgba_map[
i] :
i;
1241 fprintf(
s->stats_file,
"%c:%f ",
s->comps[
i],
c[cidx]);
1244 fprintf(
s->stats_file,
"All:%f (%f)\n", ssim360p50,
ssim360_db(ssim360p50, 1.0));
1252 const char *
data,
int w,
int h)
1270 char *saveptr, *
val;
1322 if (
s->stats_file_str) {
1323 if (!strcmp(
s->stats_file_str,
"-")) {
1324 s->stats_file = stdout;
1327 if (!
s->stats_file) {
1333 s->stats_file_str, buf);
1339 if (
s->use_tape &&
s->heatmap_str) {
1341 s->default_heatmap_w,
s->default_heatmap_h);
1356 s->main_planeheight[0] =
inlink->h;
1357 s->main_planeheight[3] =
inlink->h;
1361 s->main_planewidth[0] =
inlink->w;
1362 s->main_planewidth[3] =
inlink->w;
1368 s->main_projection =
s->ref_projection;
1372 s->main_stereo_format =
s->ref_stereo_format;
1379 double d, r_square, cos_square;
1386 switch (
s->ref_stereo_format) {
1395 switch (
s->ref_projection) {
1397 for (
int i = 0;
i <
h;
i++) {
1398 d = cos(((0.5 +
i) /
h - 0.5) *
M_PI);
1399 for (
int j = 0; j <
w; j++)
1400 s->density.value[
i *
w + j] =
d;
1405 for (
int i = 0;
i <
h / 4;
i++) {
1406 for (
int j = 0; j <
w / 6; j++) {
1409 (0.5 +
i) / (
h / 2) * (0.5 +
i) / (
h / 2) +
1410 (0.5 + j) / (
w / 3) * (0.5 + j) / (
w / 3);
1412 cos_square = 0.25 / (r_square + 0.25);
1413 d = pow(cos_square, 1.5);
1415 for (
int face = 0; face < 6; face++) {
1428 ow =
w / 6 + 2 *
w / 3;
1440 ow =
w / 6 + 2 *
w / 3;
1443 s->density.value[(oh - 1 -
i) *
w + ow - 1 - j] =
d;
1444 s->density.value[(oh - 1 -
i) *
w + ow + j] =
d;
1445 s->density.value[(oh +
i) *
w + ow - 1 - j] =
d;
1446 s->density.value[(oh +
i) *
w + ow + j] =
d;
1453 for (
int i = 0;
i <
h / 6;
i++) {
1454 for (
int j = 0; j <
w / 4; j++) {
1457 (0.5 +
i) / (
h / 3) * (0.5 +
i) / (
h / 3) +
1458 (0.5 + j) / (
w / 2) * (0.5 + j) / (
w / 2);
1459 r_square /= (1.f +
s->ref_pad) * (1.
f +
s->ref_pad);
1460 cos_square = 0.25 / (r_square + 0.25);
1461 d = pow(cos_square, 1.5);
1463 for (
int face = 0; face < 6; face++) {
1476 oh =
h / 6 + 2 *
h / 3;
1488 oh =
h / 6 + 2 *
h / 3;
1491 s->density.value[(oh - 1 -
i) *
w + ow - 1 - j] =
d;
1492 s->density.value[(oh - 1 -
i) *
w + ow + j] =
d;
1493 s->density.value[(oh +
i) *
w + ow - 1 - j] =
d;
1494 s->density.value[(oh +
i) *
w + ow + j] =
d;
1501 for (
int i = 0;
i <
h;
i++) {
1502 for (
int j = 0; j <
w * 4 / 5; j++) {
1504 s->density.value[
i *
w + j] =
d *
d *
d;
1508 for (
int i = 0;
i <
h;
i++) {
1509 for (
int j =
w * 4 / 5; j <
w; j++) {
1511 double dx_squared = dx * dx;
1514 double top_dy_squared = top_dy * top_dy;
1517 double bottom_dy_squared = bottom_dy * bottom_dy;
1520 r_square = (
i <
h / 2 ? top_dy_squared : bottom_dy_squared) + dx_squared;
1524 cos_square = 1.0 / (r_square + 1.0);
1525 d = pow(cos_square, 1.5);
1526 s->density.value[
i *
w + j] =
d;
1532 for (
int i = 0;
i <
h;
i++) {
1533 for (
int j = 0; j <
w; j++)
1534 s->density.value[
i *
w + j] = 0;
1538 switch (
s->ref_stereo_format) {
1540 for (
int i = 0;
i <
h;
i++) {
1541 for (
int j = 0; j <
w; j++)
1542 s->density.value[(
i +
h) *
w + j] =
s->density.value[
i *
w + j];
1546 for (
int i = 0;
i <
h;
i++) {
1547 for (
int j = 0; j <
w; j++)
1548 s->density.value[
i *
w + j +
w] =
s->density.value[
i *
w + j];
1562 s->nb_components =
desc->nb_components;
1564 s->ref_planeheight[0] =
inlink->h;
1565 s->ref_planeheight[3] =
inlink->h;
1569 s->ref_planewidth[0] =
inlink->w;
1570 s->ref_planewidth[3] =
inlink->w;
1575 s->comps[0] =
s->is_rgb ?
'R' :
'Y';
1576 s->comps[1] =
s->is_rgb ?
'G' :
'U';
1577 s->comps[2] =
s->is_rgb ?
'B' :
'V';
1581 if (!
s->is_rgb && !
s->compute_chroma)
1582 s->nb_components = 1;
1584 s->max = (1 <<
desc->comp[0].depth) - 1;
1588 for (
int i = 0;
i <
s->nb_components;
i++)
1589 sum +=
s->ref_planeheight[
i] *
s->ref_planewidth[
i];
1590 for (
int i = 0;
i <
s->nb_components;
i++)
1591 s->coefs[
i] = (
double)
s->ref_planeheight[
i] *
s->ref_planewidth[
i] / sum;
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)
1624 memset(
s->ssim360_percentile_sum, 0,
sizeof(
s->ssim360_percentile_sum));
1626 for (
int i = 0;
i <
s->nb_components;
i++) {
1628 if (!
s->ssim360_hist[
i])
1636 if (!
s->density.value) {
1647 outlink->
w = mainlink->
w;
1648 outlink->
h = mainlink->
h;
1653 s->fs.opt_shortest = 1;
1654 s->fs.opt_repeatlast = 1;
1673 if (
s->nb_ssim_frames > 0) {
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,
1683 s->ssim360_total /
s->nb_ssim_frames,
ssim360_db(
s->ssim360_total,
s->nb_ssim_frames));
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);
1704 for (
int i = 0;
i <
s->nb_components;
i++) {
1705 for (
int eye = 0; eye < 2; eye++) {
1714 if (
s->stats_file &&
s->stats_file != stdout)
1715 fclose(
s->stats_file);
1720 #define PF(suf) AV_PIX_FMT_YUV420##suf, AV_PIX_FMT_YUV422##suf, AV_PIX_FMT_YUV444##suf, AV_PIX_FMT_GBR##suf
1740 .name =
"reference",
1757 .preinit = ssim360_framesync_preinit,
1762 .priv_class = &ssim360_class,