X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavfilter%2Fvf_v360.c;h=39dd07af6ebafd57a74e6a6a079901dabac53ad3;hb=05d3d09cf5fe5e1aec4ba6107a2da543962d6b6a;hp=741a626bc85a204a65d464876fb8775f624f161a;hpb=b342678bc47b4e502642e11b05215ed4bc554c33;p=ffmpeg diff --git a/libavfilter/vf_v360.c b/libavfilter/vf_v360.c index 741a626bc85..39dd07af6eb 100644 --- a/libavfilter/vf_v360.c +++ b/libavfilter/vf_v360.c @@ -61,6 +61,9 @@ static const AVOption v360_options[] = { { "c6x1", "cubemap 6x1", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_6_1}, 0, 0, FLAGS, "in" }, { "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, "in" }, { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "in" }, + { "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "in" }, + {"rectilinear", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "in" }, + { "gnomonic", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "in" }, { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "in" }, { "fb", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "in" }, { "c1x6", "cubemap 1x6", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_1_6}, 0, 0, FLAGS, "in" }, @@ -68,6 +71,10 @@ static const AVOption v360_options[] = { { "mercator", "mercator", 0, AV_OPT_TYPE_CONST, {.i64=MERCATOR}, 0, 0, FLAGS, "in" }, { "ball", "ball", 0, AV_OPT_TYPE_CONST, {.i64=BALL}, 0, 0, FLAGS, "in" }, { "hammer", "hammer", 0, AV_OPT_TYPE_CONST, {.i64=HAMMER}, 0, 0, FLAGS, "in" }, + {"sinusoidal", "sinusoidal", 0, AV_OPT_TYPE_CONST, {.i64=SINUSOIDAL}, 0, 0, FLAGS, "in" }, + { "fisheye", "fisheye", 0, AV_OPT_TYPE_CONST, {.i64=FISHEYE}, 0, 0, FLAGS, "in" }, + {"cylindrical", "cylindrical", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICAL}, 0, 0, FLAGS, "in" }, + {"tetrahedron", "tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=TETRAHEDRON}, 0, 0, FLAGS, "in" }, { "output", "set output projection", OFFSET(out), AV_OPT_TYPE_INT, {.i64=CUBEMAP_3_2}, 0, NB_PROJECTIONS-1, FLAGS, "out" }, { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" }, { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" }, @@ -85,6 +92,12 @@ static const AVOption v360_options[] = { { "mercator", "mercator", 0, AV_OPT_TYPE_CONST, {.i64=MERCATOR}, 0, 0, FLAGS, "out" }, { "ball", "ball", 0, AV_OPT_TYPE_CONST, {.i64=BALL}, 0, 0, FLAGS, "out" }, { "hammer", "hammer", 0, AV_OPT_TYPE_CONST, {.i64=HAMMER}, 0, 0, FLAGS, "out" }, + {"sinusoidal", "sinusoidal", 0, AV_OPT_TYPE_CONST, {.i64=SINUSOIDAL}, 0, 0, FLAGS, "out" }, + { "fisheye", "fisheye", 0, AV_OPT_TYPE_CONST, {.i64=FISHEYE}, 0, 0, FLAGS, "out" }, + { "pannini", "pannini", 0, AV_OPT_TYPE_CONST, {.i64=PANNINI}, 0, 0, FLAGS, "out" }, + {"cylindrical", "cylindrical", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICAL}, 0, 0, FLAGS, "out" }, + {"perspective", "perspective", 0, AV_OPT_TYPE_CONST, {.i64=PERSPECTIVE}, 0, 0, FLAGS, "out" }, + {"tetrahedron", "tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=TETRAHEDRON}, 0, 0, FLAGS, "out" }, { "interp", "set interpolation method", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=BILINEAR}, 0, NB_INTERP_METHODS-1, FLAGS, "interp" }, { "near", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interp" }, { "nearest", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interp" }, @@ -94,6 +107,10 @@ static const AVOption v360_options[] = { { "cubic", "bicubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BICUBIC}, 0, 0, FLAGS, "interp" }, { "lanc", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interp" }, { "lanczos", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interp" }, + { "sp16", "spline16 interpolation", 0, AV_OPT_TYPE_CONST, {.i64=SPLINE16}, 0, 0, FLAGS, "interp" }, + { "spline16", "spline16 interpolation", 0, AV_OPT_TYPE_CONST, {.i64=SPLINE16}, 0, 0, FLAGS, "interp" }, + { "gauss", "gaussian interpolation", 0, AV_OPT_TYPE_CONST, {.i64=GAUSSIAN}, 0, 0, FLAGS, "interp" }, + { "gaussian", "gaussian interpolation", 0, AV_OPT_TYPE_CONST, {.i64=GAUSSIAN}, 0, 0, FLAGS, "interp" }, { "w", "output width", OFFSET(width), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "w"}, { "h", "output height", OFFSET(height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "h"}, { "in_stereo", "input stereo format", OFFSET(in_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" }, @@ -105,8 +122,10 @@ static const AVOption v360_options[] = { {"out_forder", "output cubemap face order", OFFSET(out_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "out_forder"}, { "in_frot", "input cubemap face rotation", OFFSET(in_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, "in_frot"}, { "out_frot", "output cubemap face rotation",OFFSET(out_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, "out_frot"}, - { "in_pad", "input cubemap pads", OFFSET(in_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 1.f, FLAGS, "in_pad"}, - { "out_pad", "output cubemap pads", OFFSET(out_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 1.f, FLAGS, "out_pad"}, + { "in_pad", "percent input cubemap pads", OFFSET(in_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 1.f, FLAGS, "in_pad"}, + { "out_pad", "percent output cubemap pads", OFFSET(out_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 1.f, FLAGS, "out_pad"}, + { "fin_pad", "fixed input cubemap pads", OFFSET(fin_pad), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, FLAGS, "fin_pad"}, + { "fout_pad", "fixed output cubemap pads", OFFSET(fout_pad), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, FLAGS, "fout_pad"}, { "yaw", "yaw rotation", OFFSET(yaw), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f, FLAGS, "yaw"}, { "pitch", "pitch rotation", OFFSET(pitch), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f, FLAGS, "pitch"}, { "roll", "roll rotation", OFFSET(roll), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f, FLAGS, "roll"}, @@ -121,6 +140,9 @@ static const AVOption v360_options[] = { { "iv_flip", "flip in video vertically", OFFSET(iv_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "iv_flip"}, { "in_trans", "transpose video input", OFFSET(in_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "in_transpose"}, { "out_trans", "transpose video output", OFFSET(out_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "out_transpose"}, + { "ih_fov", "input horizontal field of view",OFFSET(ih_fov), AV_OPT_TYPE_FLOAT, {.dbl=90.f}, 0.00001f, 360.f, FLAGS, "ih_fov"}, + { "iv_fov", "input vertical field of view", OFFSET(iv_fov), AV_OPT_TYPE_FLOAT, {.dbl=45.f}, 0.00001f, 360.f, FLAGS, "iv_fov"}, + { "id_fov", "input diagonal field of view", OFFSET(id_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f, FLAGS, "id_fov"}, { NULL } }; @@ -196,18 +218,19 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_formats(ctx, fmts_list); } -#define DEFINE_REMAP1_LINE(bits, div) \ -static void remap1_##bits##bit_line_c(uint8_t *dst, int width, const uint8_t *src, \ - ptrdiff_t in_linesize, \ - const uint16_t *u, const uint16_t *v, const int16_t *ker) \ -{ \ - const uint##bits##_t *s = (const uint##bits##_t *)src; \ - uint##bits##_t *d = (uint##bits##_t *)dst; \ - \ - in_linesize /= div; \ - \ - for (int x = 0; x < width; x++) \ - d[x] = s[v[x] * in_linesize + u[x]]; \ +#define DEFINE_REMAP1_LINE(bits, div) \ +static void remap1_##bits##bit_line_c(uint8_t *dst, int width, const uint8_t *const src, \ + ptrdiff_t in_linesize, \ + const int16_t *const u, const int16_t *const v, \ + const int16_t *const ker) \ +{ \ + const uint##bits##_t *const s = (const uint##bits##_t *const)src; \ + uint##bits##_t *d = (uint##bits##_t *)dst; \ + \ + in_linesize /= div; \ + \ + for (int x = 0; x < width; x++) \ + d[x] = s[v[x] * in_linesize + u[x]]; \ } DEFINE_REMAP1_LINE( 8, 1) @@ -222,13 +245,14 @@ DEFINE_REMAP1_LINE(16, 2) #define DEFINE_REMAP(ws, bits) \ static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \ { \ - ThreadData *td = (ThreadData*)arg; \ + ThreadData *td = arg; \ const V360Context *s = ctx->priv; \ const AVFrame *in = td->in; \ AVFrame *out = td->out; \ \ for (int stereo = 0; stereo < 1 + s->out_stereo > STEREO_2D; stereo++) { \ for (int plane = 0; plane < s->nb_planes; plane++) { \ + const unsigned map = s->map[plane]; \ const int in_linesize = in->linesize[plane]; \ const int out_linesize = out->linesize[plane]; \ const int uv_linesize = s->uv_linesize[plane]; \ @@ -236,7 +260,8 @@ static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jo const int in_offset_h = stereo ? s->in_offset_h[plane] : 0; \ const int out_offset_w = stereo ? s->out_offset_w[plane] : 0; \ const int out_offset_h = stereo ? s->out_offset_h[plane] : 0; \ - const uint8_t *src = in->data[plane] + in_offset_h * in_linesize + in_offset_w * (bits >> 3); \ + const uint8_t *const src = in->data[plane] + \ + in_offset_h * in_linesize + in_offset_w * (bits >> 3); \ uint8_t *dst = out->data[plane] + out_offset_h * out_linesize + out_offset_w * (bits >> 3); \ const int width = s->pr_width[plane]; \ const int height = s->pr_height[plane]; \ @@ -245,10 +270,9 @@ static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jo const int slice_end = (height * (jobnr + 1)) / nb_jobs; \ \ for (int y = slice_start; y < slice_end; y++) { \ - const unsigned map = s->map[plane]; \ - const uint16_t *u = s->u[map] + y * uv_linesize * ws * ws; \ - const uint16_t *v = s->v[map] + y * uv_linesize * ws * ws; \ - const int16_t *ker = s->ker[map] + y * uv_linesize * ws * ws; \ + const int16_t *const u = s->u[map] + y * uv_linesize * ws * ws; \ + const int16_t *const v = s->v[map] + y * uv_linesize * ws * ws; \ + const int16_t *const ker = s->ker[map] + y * uv_linesize * ws * ws; \ \ s->remap_line(dst + y * out_linesize, width, src, in_linesize, u, v, ker); \ } \ @@ -265,30 +289,31 @@ DEFINE_REMAP(1, 16) DEFINE_REMAP(2, 16) DEFINE_REMAP(4, 16) -#define DEFINE_REMAP_LINE(ws, bits, div) \ -static void remap##ws##_##bits##bit_line_c(uint8_t *dst, int width, const uint8_t *src, \ - ptrdiff_t in_linesize, \ - const uint16_t *u, const uint16_t *v, const int16_t *ker) \ -{ \ - const uint##bits##_t *s = (const uint##bits##_t *)src; \ - uint##bits##_t *d = (uint##bits##_t *)dst; \ - \ - in_linesize /= div; \ - \ - for (int x = 0; x < width; x++) { \ - const uint16_t *uu = u + x * ws * ws; \ - const uint16_t *vv = v + x * ws * ws; \ - const int16_t *kker = ker + x * ws * ws; \ - int tmp = 0; \ - \ - for (int i = 0; i < ws; i++) { \ - for (int j = 0; j < ws; j++) { \ - tmp += kker[i * ws + j] * s[vv[i * ws + j] * in_linesize + uu[i * ws + j]]; \ - } \ - } \ - \ - d[x] = av_clip_uint##bits(tmp >> 14); \ - } \ +#define DEFINE_REMAP_LINE(ws, bits, div) \ +static void remap##ws##_##bits##bit_line_c(uint8_t *dst, int width, const uint8_t *const src, \ + ptrdiff_t in_linesize, \ + const int16_t *const u, const int16_t *const v, \ + const int16_t *const ker) \ +{ \ + const uint##bits##_t *const s = (const uint##bits##_t *const)src; \ + uint##bits##_t *d = (uint##bits##_t *)dst; \ + \ + in_linesize /= div; \ + \ + for (int x = 0; x < width; x++) { \ + const int16_t *const uu = u + x * ws * ws; \ + const int16_t *const vv = v + x * ws * ws; \ + const int16_t *const kker = ker + x * ws * ws; \ + int tmp = 0; \ + \ + for (int i = 0; i < ws; i++) { \ + for (int j = 0; j < ws; j++) { \ + tmp += kker[i * ws + j] * s[vv[i * ws + j] * in_linesize + uu[i * ws + j]]; \ + } \ + } \ + \ + d[x] = av_clip_uint##bits(tmp >> 14); \ + } \ } DEFINE_REMAP_LINE(2, 8, 1) @@ -307,6 +332,8 @@ void ff_v360_init(V360Context *s, int depth) break; case BICUBIC: case LANCZOS: + case SPLINE16: + case GAUSSIAN: s->remap_line = depth <= 8 ? remap4_8bit_line_c : remap4_16bit_line_c; break; } @@ -326,10 +353,10 @@ void ff_v360_init(V360Context *s, int depth) * @param ker ker remap data */ static void nearest_kernel(float du, float dv, const XYRemap *rmap, - uint16_t *u, uint16_t *v, int16_t *ker) + int16_t *u, int16_t *v, int16_t *ker) { - const int i = roundf(dv) + 1; - const int j = roundf(du) + 1; + const int i = lrintf(dv) + 1; + const int j = lrintf(du) + 1; u[0] = rmap->u[i][j]; v[0] = rmap->v[i][j]; @@ -346,7 +373,7 @@ static void nearest_kernel(float du, float dv, const XYRemap *rmap, * @param ker ker remap data */ static void bilinear_kernel(float du, float dv, const XYRemap *rmap, - uint16_t *u, uint16_t *v, int16_t *ker) + int16_t *u, int16_t *v, int16_t *ker) { for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { @@ -389,7 +416,7 @@ static inline void calculate_bicubic_coeffs(float t, float *coeffs) * @param ker ker remap data */ static void bicubic_kernel(float du, float dv, const XYRemap *rmap, - uint16_t *u, uint16_t *v, int16_t *ker) + int16_t *u, int16_t *v, int16_t *ker) { float du_coeffs[4]; float dv_coeffs[4]; @@ -442,7 +469,7 @@ static inline void calculate_lanczos_coeffs(float t, float *coeffs) * @param ker ker remap data */ static void lanczos_kernel(float du, float dv, const XYRemap *rmap, - uint16_t *u, uint16_t *v, int16_t *ker) + int16_t *u, int16_t *v, int16_t *ker) { float du_coeffs[4]; float dv_coeffs[4]; @@ -459,6 +486,101 @@ static void lanczos_kernel(float du, float dv, const XYRemap *rmap, } } +/** + * Calculate 1-dimensional spline16 coefficients. + * + * @param t relative coordinate + * @param coeffs coefficients + */ +static void calculate_spline16_coeffs(float t, float *coeffs) +{ + coeffs[0] = ((-1.f / 3.f * t + 0.8f) * t - 7.f / 15.f) * t; + coeffs[1] = ((t - 9.f / 5.f) * t - 0.2f) * t + 1.f; + coeffs[2] = ((6.f / 5.f - t) * t + 0.8f) * t; + coeffs[3] = ((1.f / 3.f * t - 0.2f) * t - 2.f / 15.f) * t; +} + +/** + * Calculate kernel for spline16 interpolation. + * + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + * @param rmap calculated 4x4 window + * @param u u remap data + * @param v v remap data + * @param ker ker remap data + */ +static void spline16_kernel(float du, float dv, const XYRemap *rmap, + int16_t *u, int16_t *v, int16_t *ker) +{ + float du_coeffs[4]; + float dv_coeffs[4]; + + calculate_spline16_coeffs(du, du_coeffs); + calculate_spline16_coeffs(dv, dv_coeffs); + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + u[i * 4 + j] = rmap->u[i][j]; + v[i * 4 + j] = rmap->v[i][j]; + ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f); + } + } +} + +/** + * Calculate 1-dimensional gaussian coefficients. + * + * @param t relative coordinate + * @param coeffs coefficients + */ +static void calculate_gaussian_coeffs(float t, float *coeffs) +{ + float sum = 0.f; + + for (int i = 0; i < 4; i++) { + const float x = t - (i - 1); + if (x == 0.f) { + coeffs[i] = 1.f; + } else { + coeffs[i] = expf(-2.f * x * x) * expf(-x * x / 2.f); + } + sum += coeffs[i]; + } + + for (int i = 0; i < 4; i++) { + coeffs[i] /= sum; + } +} + +/** + * Calculate kernel for gaussian interpolation. + * + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + * @param rmap calculated 4x4 window + * @param u u remap data + * @param v v remap data + * @param ker ker remap data + */ +static void gaussian_kernel(float du, float dv, const XYRemap *rmap, + int16_t *u, int16_t *v, int16_t *ker) +{ + float du_coeffs[4]; + float dv_coeffs[4]; + + calculate_gaussian_coeffs(du, du_coeffs); + calculate_gaussian_coeffs(dv, dv_coeffs); + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + u[i * 4 + j] = rmap->u[i][j]; + v[i * 4 + j] = rmap->v[i][j]; + ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f); + } + } +} + /** * Modulo operation with only positive remainders. * @@ -724,16 +846,18 @@ static void normalize_vector(float *vec) * @param vf vertical cubemap coordinate [0, 1) * @param face face of cubemap * @param vec coordinates on sphere + * @param scalew scale for uf + * @param scaleh scale for vf */ static void cube_to_xyz(const V360Context *s, float uf, float vf, int face, - float *vec) + float *vec, float scalew, float scaleh) { const int direction = s->out_cubemap_direction_order[face]; float l_x, l_y, l_z; - uf /= (1.f - s->out_pad); - vf /= (1.f - s->out_pad); + uf /= scalew; + vf /= scaleh; rotate_cube_face_inverse(&uf, &vf, s->out_cubemap_face_rotation[face]); @@ -1063,6 +1187,9 @@ static void cube3x2_to_xyz(const V360Context *s, int i, int j, int width, int height, float *vec) { + const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_width / 3.f) : 1.f - s->out_pad; + const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_height / 2.f) : 1.f - s->out_pad; + const float ew = width / 3.f; const float eh = height / 2.f; @@ -1078,7 +1205,7 @@ static void cube3x2_to_xyz(const V360Context *s, const float uf = 2.f * (i - u_shift + 0.5f) / ewi - 1.f; const float vf = 2.f * (j - v_shift + 0.5f) / ehi - 1.f; - cube_to_xyz(s, uf, vf, face, vec); + cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh); } /** @@ -1095,8 +1222,10 @@ static void cube3x2_to_xyz(const V360Context *s, */ static void xyz_to_cube3x2(const V360Context *s, const float *vec, int width, int height, - uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv) + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) { + const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_width / 3.f) : 1.f - s->in_pad; + const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_height / 2.f) : 1.f - s->in_pad; const float ew = width / 3.f; const float eh = height / 2.f; float uf, vf; @@ -1107,8 +1236,8 @@ static void xyz_to_cube3x2(const V360Context *s, xyz_to_cube(s, vec, &uf, &vf, &direction); - uf *= (1.f - s->in_pad); - vf *= (1.f - s->in_pad); + uf *= scalew; + vf *= scaleh; face = s->in_cubemap_face_order[direction]; u_face = face % 3; @@ -1143,13 +1272,13 @@ static void xyz_to_cube3x2(const V360Context *s, uf = 2.f * new_ui / ewi - 1.f; vf = 2.f * new_vi / ehi - 1.f; - uf /= (1.f - s->in_pad); - vf /= (1.f - s->in_pad); + uf /= scalew; + vf /= scaleh; process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face); - uf *= (1.f - s->in_pad); - vf *= (1.f - s->in_pad); + uf *= scalew; + vf *= scaleh; u_face = face % 3; v_face = face / 3; @@ -1158,8 +1287,8 @@ static void xyz_to_cube3x2(const V360Context *s, new_ewi = ceilf(ew * (u_face + 1)) - u_shift; new_ehi = ceilf(eh * (v_face + 1)) - v_shift; - new_ui = av_clip(roundf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1); - new_vi = av_clip(roundf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1); + new_ui = av_clip(lrintf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1); + new_vi = av_clip(lrintf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1); } us[i + 1][j + 1] = u_shift + new_ui; @@ -1182,6 +1311,9 @@ static void cube1x6_to_xyz(const V360Context *s, int i, int j, int width, int height, float *vec) { + const float scalew = s->fout_pad > 0 ? 1.f - (float)(s->fout_pad) / s->out_width : 1.f - s->out_pad; + const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_height / 6.f) : 1.f - s->out_pad; + const float ew = width; const float eh = height / 6.f; @@ -1193,7 +1325,7 @@ static void cube1x6_to_xyz(const V360Context *s, const float uf = 2.f * (i + 0.5f) / ew - 1.f; const float vf = 2.f * (j - v_shift + 0.5f) / ehi - 1.f; - cube_to_xyz(s, uf, vf, face, vec); + cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh); } /** @@ -1210,6 +1342,9 @@ static void cube6x1_to_xyz(const V360Context *s, int i, int j, int width, int height, float *vec) { + const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_width / 6.f) : 1.f - s->out_pad; + const float scaleh = s->fout_pad > 0 ? 1.f - (float)(s->fout_pad) / s->out_height : 1.f - s->out_pad; + const float ew = width / 6.f; const float eh = height; @@ -1221,7 +1356,7 @@ static void cube6x1_to_xyz(const V360Context *s, const float uf = 2.f * (i - u_shift + 0.5f) / ewi - 1.f; const float vf = 2.f * (j + 0.5f) / eh - 1.f; - cube_to_xyz(s, uf, vf, face, vec); + cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh); } /** @@ -1238,8 +1373,10 @@ static void cube6x1_to_xyz(const V360Context *s, */ static void xyz_to_cube1x6(const V360Context *s, const float *vec, int width, int height, - uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv) + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) { + const float scalew = s->fin_pad > 0 ? 1.f - (float)(s->fin_pad) / s->in_width : 1.f - s->in_pad; + const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_height / 6.f) : 1.f - s->in_pad; const float eh = height / 6.f; const int ewi = width; float uf, vf; @@ -1249,8 +1386,8 @@ static void xyz_to_cube1x6(const V360Context *s, xyz_to_cube(s, vec, &uf, &vf, &direction); - uf *= (1.f - s->in_pad); - vf *= (1.f - s->in_pad); + uf *= scalew; + vf *= scaleh; face = s->in_cubemap_face_order[direction]; ehi = ceilf(eh * (face + 1)) - ceilf(eh * face); @@ -1279,19 +1416,19 @@ static void xyz_to_cube1x6(const V360Context *s, uf = 2.f * new_ui / ewi - 1.f; vf = 2.f * new_vi / ehi - 1.f; - uf /= (1.f - s->in_pad); - vf /= (1.f - s->in_pad); + uf /= scalew; + vf /= scaleh; process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face); - uf *= (1.f - s->in_pad); - vf *= (1.f - s->in_pad); + uf *= scalew; + vf *= scaleh; v_shift = ceilf(eh * face); new_ehi = ceilf(eh * (face + 1)) - v_shift; - new_ui = av_clip(roundf(0.5f * ewi * (uf + 1.f)), 0, ewi - 1); - new_vi = av_clip(roundf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1); + new_ui = av_clip(lrintf(0.5f * ewi * (uf + 1.f)), 0, ewi - 1); + new_vi = av_clip(lrintf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1); } us[i + 1][j + 1] = new_ui; @@ -1314,8 +1451,10 @@ static void xyz_to_cube1x6(const V360Context *s, */ static void xyz_to_cube6x1(const V360Context *s, const float *vec, int width, int height, - uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv) + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) { + const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_width / 6.f) : 1.f - s->in_pad; + const float scaleh = s->fin_pad > 0 ? 1.f - (float)(s->fin_pad) / s->in_height : 1.f - s->in_pad; const float ew = width / 6.f; const int ehi = height; float uf, vf; @@ -1325,8 +1464,8 @@ static void xyz_to_cube6x1(const V360Context *s, xyz_to_cube(s, vec, &uf, &vf, &direction); - uf *= (1.f - s->in_pad); - vf *= (1.f - s->in_pad); + uf *= scalew; + vf *= scaleh; face = s->in_cubemap_face_order[direction]; ewi = ceilf(ew * (face + 1)) - ceilf(ew * face); @@ -1355,19 +1494,19 @@ static void xyz_to_cube6x1(const V360Context *s, uf = 2.f * new_ui / ewi - 1.f; vf = 2.f * new_vi / ehi - 1.f; - uf /= (1.f - s->in_pad); - vf /= (1.f - s->in_pad); + uf /= scalew; + vf /= scaleh; process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face); - uf *= (1.f - s->in_pad); - vf *= (1.f - s->in_pad); + uf *= scalew; + vf *= scaleh; u_shift = ceilf(ew * face); new_ewi = ceilf(ew * (face + 1)) - u_shift; - new_ui = av_clip(roundf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1); - new_vi = av_clip(roundf(0.5f * ehi * (vf + 1.f)), 0, ehi - 1); + new_ui = av_clip(lrintf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1); + new_vi = av_clip(lrintf(0.5f * ehi * (vf + 1.f)), 0, ehi - 1); } us[i + 1][j + 1] = u_shift + new_ui; @@ -1414,11 +1553,8 @@ static int prepare_stereographic_out(AVFilterContext *ctx) { V360Context *s = ctx->priv; - const float h_angle = tanf(FFMIN(s->h_fov, 359.f) * M_PI / 720.f); - const float v_angle = tanf(FFMIN(s->v_fov, 359.f) * M_PI / 720.f); - - s->flat_range[0] = h_angle; - s->flat_range[1] = v_angle; + s->flat_range[0] = tanf(FFMIN(s->h_fov, 359.f) * M_PI / 720.f); + s->flat_range[1] = tanf(FFMIN(s->v_fov, 359.f) * M_PI / 720.f); return 0; } @@ -1448,6 +1584,23 @@ static void stereographic_to_xyz(const V360Context *s, normalize_vector(vec); } +/** + * Prepare data for processing stereographic input format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_stereographic_in(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + s->iflat_range[0] = tanf(FFMIN(s->ih_fov, 359.f) * M_PI / 720.f); + s->iflat_range[1] = tanf(FFMIN(s->iv_fov, 359.f) * M_PI / 720.f); + + return 0; +} + /** * Calculate frame position in stereographic format for corresponding 3D coordinates on sphere. * @@ -1462,25 +1615,27 @@ static void stereographic_to_xyz(const V360Context *s, */ static void xyz_to_stereographic(const V360Context *s, const float *vec, int width, int height, - uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv) + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) { - const float x = av_clipf(vec[0] / (1.f - vec[1]), -1.f, 1.f) * s->input_mirror_modifier[0]; - const float y = av_clipf(vec[2] / (1.f - vec[1]), -1.f, 1.f) * s->input_mirror_modifier[1]; + const float x = vec[0] / (1.f - vec[1]) / s->iflat_range[0] * s->input_mirror_modifier[0]; + const float y = vec[2] / (1.f - vec[1]) / s->iflat_range[1] * s->input_mirror_modifier[1]; float uf, vf; - int ui, vi; + int visible, ui, vi; uf = (x + 1.f) * width / 2.f; vf = (y + 1.f) * height / 2.f; ui = floorf(uf); vi = floorf(vf); - *du = uf - ui; - *dv = vf - vi; + visible = isfinite(x) && isfinite(y) && vi >= 0 && vi < height && ui >= 0 && ui < width; + + *du = visible ? uf - ui : 0.f; + *dv = visible ? vf - vi : 0.f; for (int i = -1; i < 3; i++) { for (int j = -1; j < 3; j++) { - us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1); - vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1); + us[i + 1][j + 1] = visible ? av_clip(ui + j, 0, width - 1) : 0; + vs[i + 1][j + 1] = visible ? av_clip(vi + i, 0, height - 1) : 0; } } } @@ -1499,7 +1654,7 @@ static void xyz_to_stereographic(const V360Context *s, */ static void xyz_to_equirect(const V360Context *s, const float *vec, int width, int height, - uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv) + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) { const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0]; const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1]; @@ -1522,6 +1677,68 @@ static void xyz_to_equirect(const V360Context *s, } } +/** + * Prepare data for processing flat input format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_flat_in(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + s->iflat_range[0] = tanf(0.5f * s->ih_fov * M_PI / 180.f); + s->iflat_range[1] = tanf(0.5f * s->iv_fov * M_PI / 180.f); + + return 0; +} + +/** + * Calculate frame position in flat format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static void xyz_to_flat(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float theta = acosf(vec[2]); + const float r = tanf(theta); + const float rr = fabsf(r) < 1e+6f ? r : hypotf(width, height); + const float zf = -vec[2]; + const float h = hypotf(vec[0], vec[1]); + const float c = h <= 1e-6f ? 1.f : rr / h; + float uf = -vec[0] * c / s->iflat_range[0] * s->input_mirror_modifier[0]; + float vf = vec[1] * c / s->iflat_range[1] * s->input_mirror_modifier[1]; + int visible, ui, vi; + + uf = zf >= 0.f ? (uf + 1.f) * width / 2.f : 0.f; + vf = zf >= 0.f ? (vf + 1.f) * height / 2.f : 0.f; + + ui = floorf(uf); + vi = floorf(vf); + + visible = vi >= 0 && vi < height && ui >= 0 && ui < width; + + *du = uf - ui; + *dv = vf - vi; + + for (int i = -1; i < 3; i++) { + for (int j = -1; j < 3; j++) { + us[i + 1][j + 1] = visible ? av_clip(ui + j, 0, width - 1) : 0; + vs[i + 1][j + 1] = visible ? av_clip(vi + i, 0, height - 1) : 0; + } + } +} + /** * Calculate frame position in mercator format for corresponding 3D coordinates on sphere. * @@ -1536,15 +1753,15 @@ static void xyz_to_equirect(const V360Context *s, */ static void xyz_to_mercator(const V360Context *s, const float *vec, int width, int height, - uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv) + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) { const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0]; - const float theta = 0.5f * asinhf(vec[1] / sqrtf(1.f - vec[1] * vec[1])) * s->input_mirror_modifier[1]; + const float theta = -vec[1] * s->input_mirror_modifier[1]; float uf, vf; int ui, vi; - uf = (phi / M_PI + 1.f) * width / 2.f; - vf = (theta / M_PI + 1.f) * height / 2.f; + uf = (phi / M_PI + 1.f) * width / 2.f; + vf = (av_clipf(logf((1.f + theta) / (1.f - theta)) / (2.f * M_PI), -1.f, 1.f) + 1.f) * height / 2.f; ui = floorf(uf); vi = floorf(vf); @@ -1553,7 +1770,7 @@ static void xyz_to_mercator(const V360Context *s, for (int i = -1; i < 3; i++) { for (int j = -1; j < 3; j++) { - us[i + 1][j + 1] = mod(ui + j, width); + us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1); vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1); } } @@ -1573,17 +1790,18 @@ static void mercator_to_xyz(const V360Context *s, int i, int j, int width, int height, float *vec) { - const float phi = ((2.f * i) / width - 1.f) * M_PI; - const float theta = atanf(sinhf(((2.f * j) / height - 1.f) * 2.f * M_PI)); + const float phi = ((2.f * i) / width - 1.f) * M_PI + M_PI_2; + const float y = ((2.f * j) / height - 1.f) * M_PI; + const float div = expf(2.f * y) + 1.f; const float sin_phi = sinf(phi); const float cos_phi = cosf(phi); - const float sin_theta = sinf(theta); - const float cos_theta = cosf(theta); + const float sin_theta = -2.f * expf(y) / div; + const float cos_theta = -(expf(2.f * y) - 1.f) / div; - vec[0] = cos_theta * sin_phi; - vec[1] = -sin_theta; - vec[2] = -cos_theta * cos_phi; + vec[0] = sin_theta * cos_phi; + vec[1] = cos_theta; + vec[2] = sin_theta * sin_phi; } /** @@ -1600,7 +1818,7 @@ static void mercator_to_xyz(const V360Context *s, */ static void xyz_to_ball(const V360Context *s, const float *vec, int width, int height, - uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv) + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) { const float l = hypotf(vec[0], vec[1]); const float r = sqrtf(1.f + vec[2]) / M_SQRT2; @@ -1618,7 +1836,7 @@ static void xyz_to_ball(const V360Context *s, for (int i = -1; i < 3; i++) { for (int j = -1; j < 3; j++) { - us[i + 1][j + 1] = mod(ui + j, width); + us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1); vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1); } } @@ -1706,7 +1924,7 @@ static void hammer_to_xyz(const V360Context *s, */ static void xyz_to_hammer(const V360Context *s, const float *vec, int width, int height, - uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv) + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) { const float theta = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0]; @@ -1726,7 +1944,73 @@ static void xyz_to_hammer(const V360Context *s, for (int i = -1; i < 3; i++) { for (int j = -1; j < 3; j++) { - us[i + 1][j + 1] = mod(ui + j, width); + us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1); + vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1); + } + } +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in sinusoidal format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static void sinusoidal_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float theta = ((2.f * j) / height - 1.f) * M_PI_2; + const float phi = ((2.f * i) / width - 1.f) * M_PI / cosf(theta); + + const float sin_phi = sinf(phi); + const float cos_phi = cosf(phi); + const float sin_theta = sinf(theta); + const float cos_theta = cosf(theta); + + vec[0] = cos_theta * sin_phi; + vec[1] = -sin_theta; + vec[2] = -cos_theta * cos_phi; + + normalize_vector(vec); +} + +/** + * Calculate frame position in sinusoidal format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static void xyz_to_sinusoidal(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1]; + const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0] * cosf(theta); + float uf, vf; + int ui, vi; + + uf = (phi / M_PI + 1.f) * width / 2.f; + vf = (theta / M_PI_2 + 1.f) * height / 2.f; + ui = floorf(uf); + vi = floorf(vf); + + *du = uf - ui; + *dv = vf - vi; + + for (int i = -1; i < 3; i++) { + for (int j = -1; j < 3; j++) { + us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1); vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1); } } @@ -1935,7 +2219,7 @@ static void eac_to_xyz(const V360Context *s, */ static void xyz_to_eac(const V360Context *s, const float *vec, int width, int height, - uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv) + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) { const float pixel_pad = 2; const float u_pad = pixel_pad / width; @@ -1990,11 +2274,8 @@ static int prepare_flat_out(AVFilterContext *ctx) { V360Context *s = ctx->priv; - const float h_angle = 0.5f * s->h_fov * M_PI / 180.f; - const float v_angle = 0.5f * s->v_fov * M_PI / 180.f; - - s->flat_range[0] = tanf(h_angle); - s->flat_range[1] = tanf(v_angle); + s->flat_range[0] = tanf(0.5f * s->h_fov * M_PI / 180.f); + s->flat_range[1] = tanf(0.5f * s->v_fov * M_PI / 180.f); return 0; } @@ -2023,6 +2304,375 @@ static void flat_to_xyz(const V360Context *s, normalize_vector(vec); } +/** + * Prepare data for processing fisheye output format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_fisheye_out(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + s->flat_range[0] = s->h_fov / 180.f; + s->flat_range[1] = s->v_fov / 180.f; + + return 0; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in fisheye format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static void fisheye_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float uf = s->flat_range[0] * ((2.f * i) / width - 1.f); + const float vf = s->flat_range[1] * ((2.f * j) / height - 1.f); + + const float phi = -atan2f(vf, uf); + const float theta = -M_PI_2 * (1.f - hypotf(uf, vf)); + + vec[0] = cosf(theta) * cosf(phi); + vec[1] = cosf(theta) * sinf(phi); + vec[2] = sinf(theta); + + normalize_vector(vec); +} + +/** + * Prepare data for processing fisheye input format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_fisheye_in(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + s->iflat_range[0] = s->ih_fov / 180.f; + s->iflat_range[1] = s->iv_fov / 180.f; + + return 0; +} + +/** + * Calculate frame position in fisheye format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static void xyz_to_fisheye(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float phi = -atan2f(hypotf(vec[0], vec[1]), -vec[2]) / M_PI; + const float theta = -atan2f(vec[0], vec[1]); + + float uf = sinf(theta) * phi * s->input_mirror_modifier[0] / s->iflat_range[0]; + float vf = cosf(theta) * phi * s->input_mirror_modifier[1] / s->iflat_range[1]; + + const int visible = hypotf(uf, vf) <= 0.5f; + int ui, vi; + + uf = (uf + 0.5f) * width; + vf = (vf + 0.5f) * height; + + ui = floorf(uf); + vi = floorf(vf); + + *du = visible ? uf - ui : 0.f; + *dv = visible ? vf - vi : 0.f; + + for (int i = -1; i < 3; i++) { + for (int j = -1; j < 3; j++) { + us[i + 1][j + 1] = visible ? av_clip(ui + j, 0, width - 1) : 0; + vs[i + 1][j + 1] = visible ? av_clip(vi + i, 0, height - 1) : 0; + } + } +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in pannini format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static void pannini_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float uf = ((2.f * i) / width - 1.f); + const float vf = ((2.f * j) / height - 1.f); + + const float d = s->h_fov; + const float k = uf * uf / ((d + 1.f) * (d + 1.f)); + const float dscr = k * k * d * d - (k + 1.f) * (k * d * d - 1.f); + const float clon = (-k * d + sqrtf(dscr)) / (k + 1.f); + const float S = (d + 1.f) / (d + clon); + const float lon = -(M_PI + atan2f(uf, S * clon)); + const float lat = -atan2f(vf, S); + + vec[0] = sinf(lon) * cosf(lat); + vec[1] = sinf(lat); + vec[2] = cosf(lon) * cosf(lat); + + normalize_vector(vec); +} + +/** + * Prepare data for processing cylindrical output format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_cylindrical_out(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + s->flat_range[0] = M_PI * s->h_fov / 360.f; + s->flat_range[1] = tanf(0.5f * s->v_fov * M_PI / 180.f); + + return 0; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in cylindrical format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static void cylindrical_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float uf = s->flat_range[0] * ((2.f * i) / width - 1.f); + const float vf = s->flat_range[1] * ((2.f * j) / height - 1.f); + + const float phi = uf; + const float theta = atanf(vf); + + const float sin_phi = sinf(phi); + const float cos_phi = cosf(phi); + const float sin_theta = sinf(theta); + const float cos_theta = cosf(theta); + + vec[0] = cos_theta * sin_phi; + vec[1] = -sin_theta; + vec[2] = -cos_theta * cos_phi; + + normalize_vector(vec); +} + +/** + * Prepare data for processing cylindrical input format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_cylindrical_in(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + s->iflat_range[0] = M_PI * s->ih_fov / 360.f; + s->iflat_range[1] = tanf(0.5f * s->iv_fov * M_PI / 180.f); + + return 0; +} + +/** + * Calculate frame position in cylindrical format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static void xyz_to_cylindrical(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0] / s->iflat_range[0]; + const float theta = atan2f(-vec[1], hypotf(vec[0], vec[2])) * s->input_mirror_modifier[1] / s->iflat_range[1]; + int visible, ui, vi; + float uf, vf; + + uf = (phi + 1.f) * (width - 1) / 2.f; + vf = (tanf(theta) + 1.f) * height / 2.f; + ui = floorf(uf); + vi = floorf(vf); + + visible = vi >= 0 && vi < height && ui >= 0 && ui < width && + theta <= M_PI * s->iv_fov / 180.f && + theta >= -M_PI * s->iv_fov / 180.f; + + *du = uf - ui; + *dv = vf - vi; + + for (int i = -1; i < 3; i++) { + for (int j = -1; j < 3; j++) { + us[i + 1][j + 1] = visible ? av_clip(ui + j, 0, width - 1) : 0; + vs[i + 1][j + 1] = visible ? av_clip(vi + i, 0, height - 1) : 0; + } + } +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in perspective format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static void perspective_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float uf = ((2.f * i) / width - 1.f); + const float vf = ((2.f * j) / height - 1.f); + const float rh = hypotf(uf, vf); + const float sinzz = 1.f - rh * rh; + const float h = 1.f + s->v_fov; + const float sinz = (h - sqrtf(sinzz)) / (h / rh + rh / h); + const float sinz2 = sinz * sinz; + + if (sinz2 <= 1.f) { + const float cosz = sqrtf(1.f - sinz2); + + const float theta = asinf(cosz); + const float phi = atan2f(uf, vf); + + const float sin_phi = sinf(phi); + const float cos_phi = cosf(phi); + const float sin_theta = sinf(theta); + const float cos_theta = cosf(theta); + + vec[0] = cos_theta * sin_phi; + vec[1] = sin_theta; + vec[2] = -cos_theta * cos_phi; + } else { + vec[0] = 0.f; + vec[1] = -1.f; + vec[2] = 0.f; + } + + normalize_vector(vec); +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in tetrahedron format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static void tetrahedron_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float uf = (float)i / width; + const float vf = (float)j / height; + + vec[0] = uf < 0.5f ? uf * 4.f - 1.f : 3.f - uf * 4.f; + vec[1] = 1.f - vf * 2.f; + vec[2] = 2.f * fabsf(1.f - fabsf(1.f - uf * 2.f + vf)) - 1.f; + + normalize_vector(vec); +} + +/** + * Calculate frame position in tetrahedron format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static void xyz_to_tetrahedron(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + float d = 0.5f * (vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]); + + const float d0 = (vec[0] * 0.5f + vec[1] * 0.5f + vec[2] *-0.5f) / d; + const float d1 = (vec[0] *-0.5f + vec[1] *-0.5f + vec[2] *-0.5f) / d; + const float d2 = (vec[0] * 0.5f + vec[1] *-0.5f + vec[2] * 0.5f) / d; + const float d3 = (vec[0] *-0.5f + vec[1] * 0.5f + vec[2] * 0.5f) / d; + + float uf, vf, x, y, z; + int ui, vi; + + d = FFMAX(d0, FFMAX3(d1, d2, d3)); + + x = vec[0] / d; + y = vec[1] / d; + z = -vec[2] / d; + + vf = 0.5f - y * 0.5f; + + if ((x + y >= 0.f && y + z >= 0.f && -z - x <= 0.f) || + (x + y <= 0.f && -y + z >= 0.f && z - x >= 0.f)) { + uf = 0.25f * x + 0.25f; + } else { + uf = 0.75f - 0.25f * x; + } + + uf *= width; + vf *= height; + + ui = floorf(uf); + vi = floorf(vf); + + *du = uf - ui; + *dv = vf - vi; + + for (int i = -1; i < 3; i++) { + for (int j = -1; j < 3; j++) { + us[i + 1][j + 1] = mod(ui + j, width); + vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1); + } + } +} + /** * Calculate 3D coordinates on sphere for corresponding frame position in dual fisheye format. * @@ -2055,8 +2705,8 @@ static void dfisheye_to_xyz(const V360Context *s, const float sin_theta = sinf(theta); const float cos_theta = cosf(theta); - vec[0] = cos_theta * uf / lh; - vec[1] = cos_theta * -vf / lh; + vec[0] = cos_theta * m * -uf / lh; + vec[1] = cos_theta * -vf / lh; vec[2] = sin_theta; normalize_vector(vec); @@ -2076,23 +2726,24 @@ static void dfisheye_to_xyz(const V360Context *s, */ static void xyz_to_dfisheye(const V360Context *s, const float *vec, int width, int height, - uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv) + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) { const float scale = 1.f - s->in_pad; const float ew = width / 2.f; const float eh = height; - const float phi = atan2f(-vec[1], -vec[0]) * s->input_mirror_modifier[0]; - const float theta = acosf(fabsf(vec[2])) / M_PI * s->input_mirror_modifier[1]; + const float h = hypotf(vec[0], vec[1]); + const float lh = h > 0.f ? h : 1.f; + const float theta = acosf(fabsf(vec[2])) / M_PI; - float uf = (theta * cosf(phi) * scale + 0.5f) * ew; - float vf = (theta * sinf(phi) * scale + 0.5f) * eh; + float uf = (theta * (-vec[0] / lh) * s->input_mirror_modifier[0] * scale + 0.5f) * ew; + float vf = (theta * (-vec[1] / lh) * s->input_mirror_modifier[1] * scale + 0.5f) * eh; int ui, vi; int u_shift; - if (vec[2] >= 0) { + if (vec[2] >= 0.f) { u_shift = 0; } else { u_shift = ceilf(ew); @@ -2197,7 +2848,7 @@ static void barrel_to_xyz(const V360Context *s, */ static void xyz_to_barrel(const V360Context *s, const float *vec, int width, int height, - uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv) + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) { const float scale = 0.99f; @@ -2260,7 +2911,7 @@ static void multiply_matrix(float c[3][3], const float a[3][3], const float b[3] { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - float sum = 0; + float sum = 0.f; for (int k = 0; k < 3; k++) sum += a[i][k] * b[k][j]; @@ -2355,18 +3006,18 @@ static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int p) return 0; } -static void fov_from_dfov(V360Context *s, float w, float h) +static void fov_from_dfov(float d_fov, float w, float h, float *h_fov, float *v_fov) { - const float da = tanf(0.5 * FFMIN(s->d_fov, 359.f) * M_PI / 180.f); + const float da = tanf(0.5 * FFMIN(d_fov, 359.f) * M_PI / 180.f); const float d = hypotf(w, h); - s->h_fov = atan2f(da * w, d) * 360.f / M_PI; - s->v_fov = atan2f(da * h, d) * 360.f / M_PI; + *h_fov = atan2f(da * w, d) * 360.f / M_PI; + *v_fov = atan2f(da * h, d) * 360.f / M_PI; - if (s->h_fov < 0.f) - s->h_fov += 360.f; - if (s->v_fov < 0.f) - s->v_fov += 360.f; + if (*h_fov < 0.f) + *h_fov += 360.f; + if (*v_fov < 0.f) + *v_fov += 360.f; } static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc) @@ -2396,8 +3047,8 @@ static av_always_inline int v360_slice(AVFilterContext *ctx, void *arg, int jobn for (int j = slice_start; j < slice_end; j++) { for (int i = 0; i < width; i++) { - uint16_t *u = s->u[p] + (j * uv_linesize + i) * s->elements; - uint16_t *v = s->v[p] + (j * uv_linesize + i) * s->elements; + int16_t *u = s->u[p] + (j * uv_linesize + i) * s->elements; + int16_t *v = s->v[p] + (j * uv_linesize + i) * s->elements; int16_t *ker = s->ker[p] + (j * uv_linesize + i) * s->elements; if (s->out_transpose) @@ -2446,29 +3097,43 @@ static int config_output(AVFilterLink *outlink) s->calculate_kernel = nearest_kernel; s->remap_slice = depth <= 8 ? remap1_8bit_slice : remap1_16bit_slice; s->elements = 1; - sizeof_uv = sizeof(uint16_t) * s->elements; + sizeof_uv = sizeof(int16_t) * s->elements; sizeof_ker = 0; break; case BILINEAR: s->calculate_kernel = bilinear_kernel; s->remap_slice = depth <= 8 ? remap2_8bit_slice : remap2_16bit_slice; s->elements = 2 * 2; - sizeof_uv = sizeof(uint16_t) * s->elements; - sizeof_ker = sizeof(uint16_t) * s->elements; + sizeof_uv = sizeof(int16_t) * s->elements; + sizeof_ker = sizeof(int16_t) * s->elements; break; case BICUBIC: s->calculate_kernel = bicubic_kernel; s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice; s->elements = 4 * 4; - sizeof_uv = sizeof(uint16_t) * s->elements; - sizeof_ker = sizeof(uint16_t) * s->elements; + sizeof_uv = sizeof(int16_t) * s->elements; + sizeof_ker = sizeof(int16_t) * s->elements; break; case LANCZOS: s->calculate_kernel = lanczos_kernel; s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice; s->elements = 4 * 4; - sizeof_uv = sizeof(uint16_t) * s->elements; - sizeof_ker = sizeof(uint16_t) * s->elements; + sizeof_uv = sizeof(int16_t) * s->elements; + sizeof_ker = sizeof(int16_t) * s->elements; + break; + case SPLINE16: + s->calculate_kernel = spline16_kernel; + s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice; + s->elements = 4 * 4; + sizeof_uv = sizeof(int16_t) * s->elements; + sizeof_ker = sizeof(int16_t) * s->elements; + break; + case GAUSSIAN: + s->calculate_kernel = gaussian_kernel; + s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice; + s->elements = 4 * 4; + sizeof_uv = sizeof(int16_t) * s->elements; + sizeof_ker = sizeof(int16_t) * s->elements; break; default: av_assert0(0); @@ -2521,6 +3186,15 @@ static int config_output(AVFilterLink *outlink) set_dimensions(s->inplanewidth, s->inplaneheight, w, h, desc); set_dimensions(s->in_offset_w, s->in_offset_h, in_offset_w, in_offset_h, desc); + s->in_width = s->inplanewidth[0]; + s->in_height = s->inplaneheight[0]; + + if (s->id_fov > 0.f) + fov_from_dfov(s->id_fov, w, h, &s->ih_fov, &s->iv_fov); + + if (s->in_transpose) + FFSWAP(int, s->in_width, s->in_height); + switch (s->in) { case EQUIRECTANGULAR: s->in_transform = xyz_to_equirect; @@ -2553,7 +3227,14 @@ static int config_output(AVFilterLink *outlink) hf = h / 9.f * 8.f; break; case FLAT: - av_log(ctx, AV_LOG_ERROR, "Flat format is not accepted as input.\n"); + s->in_transform = xyz_to_flat; + err = prepare_flat_in(ctx); + wf = w; + hf = h; + break; + case PERSPECTIVE: + case PANNINI: + av_log(ctx, AV_LOG_ERROR, "Supplied format is not accepted as input.\n"); return AVERROR(EINVAL); case DUAL_FISHEYE: s->in_transform = xyz_to_dfisheye; @@ -2569,7 +3250,7 @@ static int config_output(AVFilterLink *outlink) break; case STEREOGRAPHIC: s->in_transform = xyz_to_stereographic; - err = 0; + err = prepare_stereographic_in(ctx); wf = w; hf = h / 2.f; break; @@ -2577,7 +3258,7 @@ static int config_output(AVFilterLink *outlink) s->in_transform = xyz_to_mercator; err = 0; wf = w; - hf = h; + hf = h / 2.f; break; case BALL: s->in_transform = xyz_to_ball; @@ -2591,6 +3272,30 @@ static int config_output(AVFilterLink *outlink) wf = w; hf = h; break; + case SINUSOIDAL: + s->in_transform = xyz_to_sinusoidal; + err = 0; + wf = w; + hf = h; + break; + case FISHEYE: + s->in_transform = xyz_to_fisheye; + err = prepare_fisheye_in(ctx); + wf = w * 2; + hf = h; + break; + case CYLINDRICAL: + s->in_transform = xyz_to_cylindrical; + err = prepare_cylindrical_in(ctx); + wf = w; + hf = h * 2.f; + break; + case TETRAHEDRON: + s->in_transform = xyz_to_tetrahedron; + err = 0; + wf = w; + hf = h; + break; default: av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n"); return AVERROR_BUG; @@ -2604,74 +3309,110 @@ static int config_output(AVFilterLink *outlink) case EQUIRECTANGULAR: s->out_transform = equirect_to_xyz; prepare_out = NULL; - w = roundf(wf); - h = roundf(hf); + w = lrintf(wf); + h = lrintf(hf); break; case CUBEMAP_3_2: s->out_transform = cube3x2_to_xyz; prepare_out = prepare_cube_out; - w = roundf(wf / 4.f * 3.f); - h = roundf(hf); + w = lrintf(wf / 4.f * 3.f); + h = lrintf(hf); break; case CUBEMAP_1_6: s->out_transform = cube1x6_to_xyz; prepare_out = prepare_cube_out; - w = roundf(wf / 4.f); - h = roundf(hf * 3.f); + w = lrintf(wf / 4.f); + h = lrintf(hf * 3.f); break; case CUBEMAP_6_1: s->out_transform = cube6x1_to_xyz; prepare_out = prepare_cube_out; - w = roundf(wf / 2.f * 3.f); - h = roundf(hf / 2.f); + w = lrintf(wf / 2.f * 3.f); + h = lrintf(hf / 2.f); break; case EQUIANGULAR: s->out_transform = eac_to_xyz; prepare_out = prepare_eac_out; - w = roundf(wf); - h = roundf(hf / 8.f * 9.f); + w = lrintf(wf); + h = lrintf(hf / 8.f * 9.f); break; case FLAT: s->out_transform = flat_to_xyz; prepare_out = prepare_flat_out; - w = roundf(wf); - h = roundf(hf); + w = lrintf(wf); + h = lrintf(hf); break; case DUAL_FISHEYE: s->out_transform = dfisheye_to_xyz; prepare_out = NULL; - w = roundf(wf); - h = roundf(hf); + w = lrintf(wf); + h = lrintf(hf); break; case BARREL: s->out_transform = barrel_to_xyz; prepare_out = NULL; - w = roundf(wf / 4.f * 5.f); - h = roundf(hf); + w = lrintf(wf / 4.f * 5.f); + h = lrintf(hf); break; case STEREOGRAPHIC: s->out_transform = stereographic_to_xyz; prepare_out = prepare_stereographic_out; - w = roundf(wf); - h = roundf(hf * 2.f); + w = lrintf(wf); + h = lrintf(hf * 2.f); break; case MERCATOR: s->out_transform = mercator_to_xyz; prepare_out = NULL; - w = roundf(wf); - h = roundf(hf); + w = lrintf(wf); + h = lrintf(hf * 2.f); break; case BALL: s->out_transform = ball_to_xyz; prepare_out = NULL; - w = roundf(wf); - h = roundf(hf * 2.f); + w = lrintf(wf); + h = lrintf(hf * 2.f); break; case HAMMER: s->out_transform = hammer_to_xyz; prepare_out = NULL; - w = roundf(wf); - h = roundf(hf); + w = lrintf(wf); + h = lrintf(hf); + break; + case SINUSOIDAL: + s->out_transform = sinusoidal_to_xyz; + prepare_out = NULL; + w = lrintf(wf); + h = lrintf(hf); + break; + case FISHEYE: + s->out_transform = fisheye_to_xyz; + prepare_out = prepare_fisheye_out; + w = lrintf(wf * 0.5f); + h = lrintf(hf); + break; + case PANNINI: + s->out_transform = pannini_to_xyz; + prepare_out = NULL; + w = lrintf(wf); + h = lrintf(hf); + break; + case CYLINDRICAL: + s->out_transform = cylindrical_to_xyz; + prepare_out = prepare_cylindrical_out; + w = lrintf(wf); + h = lrintf(hf * 0.5f); + break; + case PERSPECTIVE: + s->out_transform = perspective_to_xyz; + prepare_out = NULL; + w = lrintf(wf / 2.f); + h = lrintf(hf); + break; + case TETRAHEDRON: + s->out_transform = tetrahedron_to_xyz; + prepare_out = NULL; + w = lrintf(wf); + h = lrintf(hf); break; default: av_log(ctx, AV_LOG_ERROR, "Specified output format is not handled.\n"); @@ -2694,7 +3435,7 @@ static int config_output(AVFilterLink *outlink) } if (s->d_fov > 0.f) - fov_from_dfov(s, w, h); + fov_from_dfov(s->d_fov, w, h, &s->h_fov, &s->v_fov); if (prepare_out) { err = prepare_out(ctx); @@ -2704,6 +3445,12 @@ static int config_output(AVFilterLink *outlink) set_dimensions(s->pr_width, s->pr_height, w, h, desc); + s->out_width = s->pr_width[0]; + s->out_height = s->pr_height[0]; + + if (s->out_transpose) + FFSWAP(int, s->out_width, s->out_height); + switch (s->out_stereo) { case STEREO_2D: out_offset_w = out_offset_h = 0; @@ -2738,9 +3485,8 @@ static int config_output(AVFilterLink *outlink) s->map[0] = s->map[1] = s->map[2] = s->map[3] = 0; } else { s->nb_allocated = 2; - s->map[0] = 0; + s->map[0] = s->map[3] = 0; s->map[1] = s->map[2] = 1; - s->map[3] = 0; } for (int i = 0; i < s->nb_allocated; i++)