* 5) Remap input frame to output frame using precalculated data
*/
+#include <math.h>
+
#include "libavutil/avassert.h"
#include "libavutil/imgutils.h"
#include "libavutil/pixdesc.h"
{ "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" },
+ { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 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" },
{ "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_3_2}, 0, 0, FLAGS, "out" },
{ "c6x1", "cubemap 6x1", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_6_1}, 0, 0, FLAGS, "out" },
{ "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, "out" },
+ { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "out" },
{ "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
{"rectilinear", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
{ "gnomonic", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
{ "lanczos", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 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" },
+ {"out_stereo", "output stereo format", OFFSET(out_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" },
+ { "2d", "2d mono", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_2D}, 0, 0, FLAGS, "stereo" },
+ { "sbs", "side by side", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_SBS}, 0, 0, FLAGS, "stereo" },
+ { "tb", "top bottom", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_TB}, 0, 0, FLAGS, "stereo" },
{ "in_forder", "input cubemap face order", OFFSET(in_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "in_forder"},
{"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"},
{ "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"},
{ "rorder", "rotation order", OFFSET(rorder), AV_OPT_TYPE_STRING, {.str="ypr"}, 0, 0, FLAGS, "rorder"},
- { "h_fov", "horizontal field of view", OFFSET(h_fov), AV_OPT_TYPE_FLOAT, {.dbl=90.f}, 0.00001f, 180.f, FLAGS, "h_fov"},
- { "v_fov", "vertical field of view", OFFSET(v_fov), AV_OPT_TYPE_FLOAT, {.dbl=45.f}, 0.00001f, 90.f, FLAGS, "v_fov"},
+ { "h_fov", "horizontal field of view", OFFSET(h_fov), AV_OPT_TYPE_FLOAT, {.dbl=90.f}, 0.00001f, 360.f, FLAGS, "h_fov"},
+ { "v_fov", "vertical field of view", OFFSET(v_fov), AV_OPT_TYPE_FLOAT, {.dbl=45.f}, 0.00001f, 360.f, FLAGS, "v_fov"},
+ { "d_fov", "diagonal field of view", OFFSET(d_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f, FLAGS, "d_fov"},
{ "h_flip", "flip out video horizontally", OFFSET(h_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "h_flip"},
{ "v_flip", "flip out video vertically", OFFSET(v_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "v_flip"},
{ "d_flip", "flip out video indepth", OFFSET(d_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "d_flip"},
const AVFrame *in = td->in; \
AVFrame *out = td->out; \
\
- for (int plane = 0; plane < s->nb_planes; plane++) { \
- const int in_linesize = in->linesize[plane]; \
- const int out_linesize = out->linesize[plane]; \
- const uint8_t *src = in->data[plane]; \
- uint8_t *dst = out->data[plane]; \
- const int width = s->planewidth[plane]; \
- const int height = s->planeheight[plane]; \
+ for (int stereo = 0; stereo < 1 + s->out_stereo > STEREO_2D; stereo++) { \
+ for (int plane = 0; plane < s->nb_planes; plane++) { \
+ const int in_linesize = in->linesize[plane]; \
+ const int out_linesize = out->linesize[plane]; \
+ const int uv_linesize = s->uv_linesize[plane]; \
+ const int in_offset_w = stereo ? s->in_offset_w[plane] : 0; \
+ 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); \
+ 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]; \
\
- const int slice_start = (height * jobnr ) / nb_jobs; \
- const int slice_end = (height * (jobnr + 1)) / nb_jobs; \
+ const int slice_start = (height * jobnr ) / nb_jobs; \
+ 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 * width * ws * ws; \
- const uint16_t *v = s->v[map] + y * width * ws * ws; \
- const int16_t *ker = s->ker[map] + y * width * ws * ws; \
+ 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; \
\
- s->remap_line(dst + y * out_linesize, width, src, in_linesize, u, v, ker); \
+ s->remap_line(dst + y * out_linesize, width, src, in_linesize, u, v, ker); \
+ } \
} \
} \
\
static void bilinear_kernel(float du, float dv, const XYRemap *r_tmp,
uint16_t *u, uint16_t *v, int16_t *ker)
{
- int i, j;
-
- for (i = 0; i < 2; i++) {
- for (j = 0; j < 2; j++) {
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
u[i * 2 + j] = r_tmp->u[i + 1][j + 1];
v[i * 2 + j] = r_tmp->v[i + 1][j + 1];
}
static void bicubic_kernel(float du, float dv, const XYRemap *r_tmp,
uint16_t *u, uint16_t *v, int16_t *ker)
{
- int i, j;
float du_coeffs[4];
float dv_coeffs[4];
calculate_bicubic_coeffs(du, du_coeffs);
calculate_bicubic_coeffs(dv, dv_coeffs);
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 4; j++) {
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
u[i * 4 + j] = r_tmp->u[i][j];
v[i * 4 + j] = r_tmp->v[i][j];
ker[i * 4 + j] = du_coeffs[j] * dv_coeffs[i] * 16384;
*/
static inline void calculate_lanczos_coeffs(float t, float *coeffs)
{
- int i;
float sum = 0.f;
- for (i = 0; i < 4; i++) {
+ for (int i = 0; i < 4; i++) {
const float x = M_PI * (t - i + 1);
if (x == 0.f) {
coeffs[i] = 1.f;
sum += coeffs[i];
}
- for (i = 0; i < 4; i++) {
+ for (int i = 0; i < 4; i++) {
coeffs[i] /= sum;
}
}
static void lanczos_kernel(float du, float dv, const XYRemap *r_tmp,
uint16_t *u, uint16_t *v, int16_t *ker)
{
- int i, j;
float du_coeffs[4];
float dv_coeffs[4];
calculate_lanczos_coeffs(du, du_coeffs);
calculate_lanczos_coeffs(dv, dv_coeffs);
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 4; j++) {
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
u[i * 4 + j] = r_tmp->u[i][j];
v[i * 4 + j] = r_tmp->v[i][j];
ker[i * 4 + j] = du_coeffs[j] * dv_coeffs[i] * 16384;
}
}
+/**
+ * Normalize vector.
+ *
+ * @param vec vector
+ */
+static void normalize_vector(float *vec)
+{
+ const float norm = sqrtf(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
+
+ vec[0] /= norm;
+ vec[1] /= norm;
+ vec[2] /= norm;
+}
+
/**
* Calculate 3D coordinates on sphere for corresponding cubemap position.
* Common operation for every cubemap.
float *vec)
{
const int direction = s->out_cubemap_direction_order[face];
- float norm;
float l_x, l_y, l_z;
uf /= (1.f - s->out_pad);
break;
}
- norm = sqrtf(l_x * l_x + l_y * l_y + l_z * l_z);
- vec[0] = l_x / norm;
- vec[1] = l_y / norm;
- vec[2] = l_z / norm;
+ vec[0] = l_x;
+ vec[1] = l_y;
+ vec[2] = l_z;
+
+ normalize_vector(vec);
}
/**
* Calculate 3D coordinates on sphere for corresponding frame position in cubemap3x2 format.
*
* @param s filter context
- * @param i horizontal position on frame [0, height)
- * @param j vertical position on frame [0, width)
+ * @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
float uf, vf;
int ui, vi;
int ewi, ehi;
- int i, j;
int direction, face;
int u_face, v_face;
*du = uf - ui;
*dv = vf - vi;
- for (i = -1; i < 3; i++) {
- for (j = -1; j < 3; j++) {
+ for (int i = -1; i < 3; i++) {
+ for (int j = -1; j < 3; j++) {
int new_ui = ui + j;
int new_vi = vi + i;
int u_shift, v_shift;
* Calculate 3D coordinates on sphere for corresponding frame position in cubemap1x6 format.
*
* @param s filter context
- * @param i horizontal position on frame [0, height)
- * @param j vertical position on frame [0, width)
+ * @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
* Calculate 3D coordinates on sphere for corresponding frame position in cubemap6x1 format.
*
* @param s filter context
- * @param i horizontal position on frame [0, height)
- * @param j vertical position on frame [0, width)
+ * @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
float uf, vf;
int ui, vi;
int ehi;
- int i, j;
int direction, face;
xyz_to_cube(s, vec, &uf, &vf, &direction);
*du = uf - ui;
*dv = vf - vi;
- for (i = -1; i < 3; i++) {
- for (j = -1; j < 3; j++) {
+ for (int i = -1; i < 3; i++) {
+ for (int j = -1; j < 3; j++) {
int new_ui = ui + j;
int new_vi = vi + i;
int v_shift;
float uf, vf;
int ui, vi;
int ewi;
- int i, j;
int direction, face;
xyz_to_cube(s, vec, &uf, &vf, &direction);
*du = uf - ui;
*dv = vf - vi;
- for (i = -1; i < 3; i++) {
- for (j = -1; j < 3; j++) {
+ for (int i = -1; i < 3; i++) {
+ for (int j = -1; j < 3; j++) {
int new_ui = ui + j;
int new_vi = vi + i;
int u_shift;
* Calculate 3D coordinates on sphere for corresponding frame position in equirectangular format.
*
* @param s filter context
- * @param i horizontal position on frame [0, height)
- * @param j vertical position on frame [0, width)
+ * @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
vec[2] = -cos_theta * cos_phi;
}
+/**
+ * Prepare data for processing stereographic output format.
+ *
+ * @param ctx filter context
+ *
+ * @return error code
+ */
+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;
+
+ return 0;
+}
+
/**
* Calculate 3D coordinates on sphere for corresponding frame position in stereographic format.
*
* @param s filter context
- * @param i horizontal position on frame [0, height)
- * @param j vertical position on frame [0, width)
+ * @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
int i, int j, int width, int height,
float *vec)
{
- const float z = 0.5f * s->h_fov * M_PI / 180.f;
- const float x = z * (2.f * i / width - 1.f);
- const float y = z * (2.f * j / height - 1.f);
+ const float x = ((2.f * i) / width - 1.f) * s->flat_range[0];
+ const float y = ((2.f * j) / height - 1.f) * s->flat_range[1];
const float xy = x * x + y * y;
- float norm;
vec[0] = 2.f * x / (1.f + xy);
vec[1] = (-1.f + xy) / (1.f + xy);
vec[2] = 2.f * y / (1.f + xy);
- norm = sqrtf(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
- vec[0] /= norm;
- vec[1] /= norm;
- vec[2] /= norm;
+ normalize_vector(vec);
+}
+
+/**
+ * Calculate frame position in stereographic format for corresponding 3D coordinates on sphere.
+ *
+ * @param s filter 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_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)
+{
+ 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];
+ float uf, vf;
+ int 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;
+
+ 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);
+ }
+ }
}
/**
const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1];
float uf, vf;
int ui, vi;
- int i, j;
uf = (phi / M_PI + 1.f) * width / 2.f;
vf = (theta / M_PI_2 + 1.f) * height / 2.f;
*du = uf - ui;
*dv = vf - vi;
- for (i = -1; i < 3; i++) {
- for (j = -1; j < 3; j++) {
+ 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 equi-angular cubemap format.
*
* @param s filter context
- * @param i horizontal position on frame [0, height)
- * @param j vertical position on frame [0, width)
+ * @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
int u_face, v_face, face;
float l_x, l_y, l_z;
- float norm;
float uf = (float)i / width;
float vf = (float)j / height;
av_assert0(0);
}
- norm = sqrtf(l_x * l_x + l_y * l_y + l_z * l_z);
- vec[0] = l_x / norm;
- vec[1] = l_y / norm;
- vec[2] = l_z / norm;
+ vec[0] = l_x;
+ vec[1] = l_y;
+ vec[2] = l_z;
+
+ normalize_vector(vec);
}
/**
float uf, vf;
int ui, vi;
- int i, j;
int direction, face;
int u_face, v_face;
*du = uf - ui;
*dv = vf - vi;
- for (i = -1; i < 3; i++) {
- for (j = -1; j < 3; j++) {
+ 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);
}
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;
- const float sin_phi = sinf(h_angle);
- const float cos_phi = cosf(h_angle);
- const float sin_theta = sinf(v_angle);
- const float cos_theta = cosf(v_angle);
-
- s->flat_range[0] = cos_theta * sin_phi;
- s->flat_range[1] = sin_theta;
- s->flat_range[2] = -cos_theta * cos_phi;
+ s->flat_range[0] = tanf(h_angle);
+ s->flat_range[1] = tanf(v_angle);
+ s->flat_range[2] = -1.f;
return 0;
}
* Calculate 3D coordinates on sphere for corresponding frame position in flat format.
*
* @param s filter context
- * @param i horizontal position on frame [0, height)
- * @param j vertical position on frame [0, width)
+ * @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
const float l_y = -s->flat_range[1] * (2.f * j / height - 1.f);
const float l_z = s->flat_range[2];
- const float norm = sqrtf(l_x * l_x + l_y * l_y + l_z * l_z);
+ vec[0] = l_x;
+ vec[1] = l_y;
+ vec[2] = l_z;
- vec[0] = l_x / norm;
- vec[1] = l_y / norm;
- vec[2] = l_z / norm;
+ normalize_vector(vec);
+}
+
+/**
+ * Calculate 3D coordinates on sphere for corresponding frame position in dual fisheye format.
+ *
+ * @param s filter 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 dfisheye_to_xyz(const V360Context *s,
+ int i, int j, int width, int height,
+ float *vec)
+{
+ const float scale = 1.f + s->out_pad;
+
+ const float ew = width / 2.f;
+ const float eh = height;
+
+ const int ei = i >= ew ? i - ew : i;
+ const float m = i >= ew ? -1.f : 1.f;
+
+ const float uf = ((2.f * ei) / ew - 1.f) * scale;
+ const float vf = ((2.f * j) / eh - 1.f) * scale;
+
+ const float phi = M_PI + atan2f(vf, uf * m);
+ const float theta = m * M_PI_2 * (1.f - hypotf(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 * cos_phi;
+ vec[1] = cos_theta * sin_phi;
+ vec[2] = sin_theta;
+
+ normalize_vector(vec);
}
/**
int ui, vi;
int u_shift;
- int i, j;
if (vec[2] >= 0) {
u_shift = 0;
*du = uf - ui;
*dv = vf - vi;
- for (i = -1; i < 3; i++) {
- for (j = -1; j < 3; j++) {
+ for (int i = -1; i < 3; i++) {
+ for (int j = -1; j < 3; j++) {
us[i + 1][j + 1] = av_clip(u_shift + 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 barrel facebook's format.
*
* @param s filter context
- * @param i horizontal position on frame [0, height)
- * @param j vertical position on frame [0, width)
+ * @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
float l_x, l_y, l_z;
if (i < 4 * width / 5) {
- const float theta_range = M_PI / 4.f;
+ const float theta_range = M_PI_4;
const int ew = 4 * width / 5;
const int eh = height;
const int eh = height / 2;
float uf, vf;
- float norm;
if (j < eh) { // UP
uf = 2.f * (i - 4 * ew) / ew - 1.f;
l_y = -1.f;
l_z = vf;
}
-
- norm = sqrtf(l_x * l_x + l_y * l_y + l_z * l_z);
-
- l_x /= norm;
- l_y /= norm;
- l_z /= norm;
}
vec[0] = l_x;
vec[1] = l_y;
vec[2] = l_z;
+
+ normalize_vector(vec);
}
/**
const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1];
- const float theta_range = M_PI / 4.f;
+ const float theta_range = M_PI_4;
int ew, eh;
int u_shift, v_shift;
float uf, vf;
int ui, vi;
- int i, j;
if (theta > -theta_range && theta < theta_range) {
ew = 4 * width / 5;
*du = uf - ui;
*dv = vf - vi;
- for (i = -1; i < 3; i++) {
- for (j = -1; j < 3; j++) {
+ for (int i = -1; i < 3; i++) {
+ for (int j = -1; j < 3; j++) {
us[i + 1][j + 1] = u_shift + av_clip(ui + j, 0, ew - 1);
vs[i + 1][j + 1] = v_shift + av_clip(vi + i, 0, eh - 1);
}
static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int p)
{
- s->u[p] = av_calloc(s->planewidth[p] * s->planeheight[p], sizeof_uv);
- s->v[p] = av_calloc(s->planewidth[p] * s->planeheight[p], sizeof_uv);
+ s->u[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
+ s->v[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
if (!s->u[p] || !s->v[p])
return AVERROR(ENOMEM);
if (sizeof_ker) {
- s->ker[p] = av_calloc(s->planewidth[p] * s->planeheight[p], sizeof_ker);
+ s->ker[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_ker);
if (!s->ker[p])
return AVERROR(ENOMEM);
}
return 0;
}
+static void fov_from_dfov(V360Context *s, float w, float h)
+{
+ const float da = tanf(0.5 * FFMIN(s->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;
+
+ if (s->h_fov < 0.f)
+ s->h_fov += 360.f;
+ if (s->v_fov < 0.f)
+ s->v_fov += 360.f;
+}
+
+static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc)
+{
+ outw[1] = outw[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
+ outw[0] = outw[3] = w;
+ outh[1] = outh[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
+ outh[0] = outh[3] = h;
+}
+
static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
int sizeof_ker;
int elements;
int err;
- int p, h, w;
+ int h, w;
+ int in_offset_h, in_offset_w;
+ int out_offset_h, out_offset_w;
float hf, wf;
float output_mirror_modifier[3];
void (*in_transform)(const V360Context *s,
float *vec);
void (*calculate_kernel)(float du, float dv, const XYRemap *r_tmp,
uint16_t *u, uint16_t *v, int16_t *ker);
+ int (*prepare_out)(AVFilterContext *ctx);
float rot_mat[3][3];
s->input_mirror_modifier[0] = s->ih_flip ? -1.f : 1.f;
s->rotation_order[order] = rorder;
}
+ switch (s->in_stereo) {
+ case STEREO_2D:
+ w = inlink->w;
+ h = inlink->h;
+ in_offset_w = in_offset_h = 0;
+ break;
+ case STEREO_SBS:
+ w = inlink->w / 2;
+ h = inlink->h;
+ in_offset_w = w;
+ in_offset_h = 0;
+ break;
+ case STEREO_TB:
+ w = inlink->w;
+ h = inlink->h / 2;
+ in_offset_w = 0;
+ in_offset_h = h;
+ break;
+ default:
+ av_assert0(0);
+ }
+
+ 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);
+
switch (s->in) {
case EQUIRECTANGULAR:
in_transform = xyz_to_equirect;
err = 0;
- wf = inlink->w;
- hf = inlink->h;
+ wf = w;
+ hf = h;
break;
case CUBEMAP_3_2:
in_transform = xyz_to_cube3x2;
err = prepare_cube_in(ctx);
- wf = inlink->w / 3.f * 4.f;
- hf = inlink->h;
+ wf = w / 3.f * 4.f;
+ hf = h;
break;
case CUBEMAP_1_6:
in_transform = xyz_to_cube1x6;
err = prepare_cube_in(ctx);
- wf = inlink->w * 4.f;
- hf = inlink->h / 3.f;
+ wf = w * 4.f;
+ hf = h / 3.f;
break;
case CUBEMAP_6_1:
in_transform = xyz_to_cube6x1;
err = prepare_cube_in(ctx);
- wf = inlink->w / 3.f * 2.f;
- hf = inlink->h * 2.f;
+ wf = w / 3.f * 2.f;
+ hf = h * 2.f;
break;
case EQUIANGULAR:
in_transform = xyz_to_eac;
err = prepare_eac_in(ctx);
- wf = inlink->w;
- hf = inlink->h / 9.f * 8.f;
+ wf = w;
+ hf = h / 9.f * 8.f;
break;
case FLAT:
av_log(ctx, AV_LOG_ERROR, "Flat format is not accepted as input.\n");
case DUAL_FISHEYE:
in_transform = xyz_to_dfisheye;
err = 0;
- wf = inlink->w;
- hf = inlink->h;
+ wf = w;
+ hf = h;
break;
case BARREL:
in_transform = xyz_to_barrel;
err = 0;
- wf = inlink->w / 5.f * 4.f;
- hf = inlink->h;
+ wf = w / 5.f * 4.f;
+ hf = h;
+ break;
+ case STEREOGRAPHIC:
+ in_transform = xyz_to_stereographic;
+ err = 0;
+ wf = w;
+ hf = h / 2.f;
break;
default:
av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n");
switch (s->out) {
case EQUIRECTANGULAR:
out_transform = equirect_to_xyz;
- err = 0;
+ prepare_out = NULL;
w = roundf(wf);
h = roundf(hf);
break;
case CUBEMAP_3_2:
out_transform = cube3x2_to_xyz;
- err = prepare_cube_out(ctx);
+ prepare_out = prepare_cube_out;
w = roundf(wf / 4.f * 3.f);
h = roundf(hf);
break;
case CUBEMAP_1_6:
out_transform = cube1x6_to_xyz;
- err = prepare_cube_out(ctx);
+ prepare_out = prepare_cube_out;
w = roundf(wf / 4.f);
h = roundf(hf * 3.f);
break;
case CUBEMAP_6_1:
out_transform = cube6x1_to_xyz;
- err = prepare_cube_out(ctx);
+ prepare_out = prepare_cube_out;
w = roundf(wf / 2.f * 3.f);
h = roundf(hf / 2.f);
break;
case EQUIANGULAR:
out_transform = eac_to_xyz;
- err = prepare_eac_out(ctx);
+ prepare_out = prepare_eac_out;
w = roundf(wf);
h = roundf(hf / 8.f * 9.f);
break;
case FLAT:
out_transform = flat_to_xyz;
- err = prepare_flat_out(ctx);
- w = roundf(wf * s->flat_range[0] / s->flat_range[1] / 2.f);
+ prepare_out = prepare_flat_out;
+ w = roundf(wf);
h = roundf(hf);
break;
case DUAL_FISHEYE:
- av_log(ctx, AV_LOG_ERROR, "Dual fisheye format is not accepted as output.\n");
- return AVERROR(EINVAL);
+ out_transform = dfisheye_to_xyz;
+ prepare_out = NULL;
+ w = roundf(wf);
+ h = roundf(hf);
+ break;
case BARREL:
out_transform = barrel_to_xyz;
- err = 0;
+ prepare_out = NULL;
w = roundf(wf / 4.f * 5.f);
h = roundf(hf);
break;
case STEREOGRAPHIC:
out_transform = stereographic_to_xyz;
- err = 0;
- w = FFMAX(roundf(wf), roundf(hf));
- h = w;
+ prepare_out = prepare_stereographic_out;
+ w = roundf(wf);
+ h = roundf(hf * 2.f);
break;
default:
av_log(ctx, AV_LOG_ERROR, "Specified output format is not handled.\n");
return AVERROR_BUG;
}
- if (err != 0) {
- return err;
- }
-
// Override resolution with user values if specified
if (s->width > 0 && s->height > 0) {
w = s->width;
FFSWAP(int, w, h);
}
- s->planeheight[1] = s->planeheight[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
- s->planeheight[0] = s->planeheight[3] = h;
- s->planewidth[1] = s->planewidth[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
- s->planewidth[0] = s->planewidth[3] = w;
+ if (s->d_fov > 0.f)
+ fov_from_dfov(s, w, h);
+
+ if (prepare_out) {
+ err = prepare_out(ctx);
+ if (err != 0)
+ return err;
+ }
+
+ set_dimensions(s->pr_width, s->pr_height, w, h, desc);
+
+ switch (s->out_stereo) {
+ case STEREO_2D:
+ out_offset_w = out_offset_h = 0;
+ break;
+ case STEREO_SBS:
+ out_offset_w = w;
+ out_offset_h = 0;
+ w *= 2;
+ break;
+ case STEREO_TB:
+ out_offset_w = 0;
+ out_offset_h = h;
+ h *= 2;
+ break;
+ default:
+ av_assert0(0);
+ }
+
+ set_dimensions(s->out_offset_w, s->out_offset_h, out_offset_w, out_offset_h, desc);
+ set_dimensions(s->planewidth, s->planeheight, w, h, desc);
+
+ for (int i = 0; i < 4; i++)
+ s->uv_linesize[i] = FFALIGN(s->pr_width[i], 8);
outlink->h = h;
outlink->w = w;
- s->inplaneheight[1] = s->inplaneheight[2] = FF_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
- s->inplaneheight[0] = s->inplaneheight[3] = inlink->h;
- s->inplanewidth[1] = s->inplanewidth[2] = FF_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
- s->inplanewidth[0] = s->inplanewidth[3] = inlink->w;
s->nb_planes = av_pix_fmt_count_planes(inlink->format);
if (desc->log2_chroma_h == desc->log2_chroma_w && desc->log2_chroma_h == 0) {
s->nb_allocated = 1;
s->map[0] = s->map[1] = s->map[2] = s->map[3] = 0;
allocate_plane(s, sizeof_uv, sizeof_ker, 0);
- } else if (desc->log2_chroma_h == desc->log2_chroma_w) {
+ } else {
s->nb_allocated = 2;
s->map[0] = 0;
s->map[1] = s->map[2] = 1;
s->map[3] = 0;
allocate_plane(s, sizeof_uv, sizeof_ker, 0);
allocate_plane(s, sizeof_uv, sizeof_ker, 1);
- } else {
- s->nb_allocated = 3;
- s->map[0] = 0;
- s->map[1] = 1;
- s->map[2] = 2;
- s->map[3] = 0;
- allocate_plane(s, sizeof_uv, sizeof_ker, 0);
- allocate_plane(s, sizeof_uv, sizeof_ker, 1);
- allocate_plane(s, sizeof_uv, sizeof_ker, 2);
}
calculate_rotation_matrix(s->yaw, s->pitch, s->roll, rot_mat, s->rotation_order);
set_mirror_modifier(s->h_flip, s->v_flip, s->d_flip, output_mirror_modifier);
// Calculate remap data
- for (p = 0; p < s->nb_allocated; p++) {
- const int width = s->planewidth[p];
- const int height = s->planeheight[p];
+ for (int p = 0; p < s->nb_allocated; p++) {
+ const int width = s->pr_width[p];
+ const int uv_linesize = s->uv_linesize[p];
+ const int height = s->pr_height[p];
const int in_width = s->inplanewidth[p];
const int in_height = s->inplaneheight[p];
float du, dv;
float vec[3];
XYRemap r_tmp;
- int i, j;
- for (i = 0; i < width; i++) {
- for (j = 0; j < height; j++) {
- uint16_t *u = s->u[p] + (j * width + i) * elements;
- uint16_t *v = s->v[p] + (j * width + i) * elements;
- int16_t *ker = s->ker[p] + (j * width + i) * elements;
+ for (int i = 0; i < width; i++) {
+ for (int j = 0; j < height; j++) {
+ uint16_t *u = s->u[p] + (j * uv_linesize + i) * elements;
+ uint16_t *v = s->v[p] + (j * uv_linesize + i) * elements;
+ int16_t *ker = s->ker[p] + (j * uv_linesize + i) * elements;
if (s->out_transpose)
out_transform(s, j, i, height, width, vec);
else
out_transform(s, i, j, width, height, vec);
+ av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
rotate(rot_mat, vec);
+ av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
+ normalize_vector(vec);
mirror(output_mirror_modifier, vec);
if (s->in_transpose)
in_transform(s, vec, in_height, in_width, r_tmp.v, r_tmp.u, &du, &dv);
else
in_transform(s, vec, in_width, in_height, r_tmp.u, r_tmp.v, &du, &dv);
+ av_assert1(!isnan(du) && !isnan(dv));
calculate_kernel(du, dv, &r_tmp, u, v, ker);
}
}
static av_cold void uninit(AVFilterContext *ctx)
{
V360Context *s = ctx->priv;
- int p;
- for (p = 0; p < s->nb_allocated; p++) {
+ for (int p = 0; p < s->nb_allocated; p++) {
av_freep(&s->u[p]);
av_freep(&s->v[p]);
av_freep(&s->ker[p]);