31 init_effect_details();
38 : yaw(new_yaw), pitch(new_pitch), roll(new_roll)
39 , fov(new_fov), projection_mode(0), invert(0), interpolation(0)
41 init_effect_details();
44 void SphericalProjection::init_effect_details()
49 info.
description =
"Flatten and reproject 360° video with yaw, pitch, roll, and fov (sphere, hemisphere, fisheye modes)";
55 std::shared_ptr<openshot::Frame> frame,
58 auto img = frame->GetImage();
59 if (img->format() != QImage::Format_ARGB32)
60 *img = img->convertToFormat(QImage::Format_ARGB32);
62 int W = img->width(), H = img->height();
63 int bpl = img->bytesPerLine();
64 uchar* src = img->bits();
66 QImage output(W, H, QImage::Format_ARGB32);
67 output.fill(Qt::black);
68 uchar* dst = output.bits();
69 int dst_bpl = output.bytesPerLine();
72 double yaw_r =
yaw.
GetValue(frame_number) * M_PI/180.0;
74 double roll_r = -
roll.
GetValue(frame_number) * M_PI/180.0 + M_PI;
75 double fov_r =
fov.
GetValue(frame_number) * M_PI/180.0;
78 double sy = sin(yaw_r), cy = cos(yaw_r);
79 double sp = sin(pitch_r), cp = cos(pitch_r);
80 double sr = sin(roll_r), cr = cos(roll_r);
82 double r00 = cy*cr + sy*sp*sr, r01 = -cy*sr + sy*sp*cr, r02 = sy*cp;
83 double r10 = cp*sr, r11 = cp*cr, r12 = -sp;
84 double r20 = -sy*cr + cy*sp*sr, r21 = sy*sr + cy*sp*cr, r22 = cy*cp;
87 double hx = tan(fov_r*0.5);
88 double vy = hx * double(H)/W;
90 #pragma omp parallel for schedule(static)
91 for (
int yy = 0; yy < H; yy++) {
92 uchar* dst_row = dst + yy * dst_bpl;
93 double ndc_y = (2.0*(yy + 0.5)/H - 1.0) * vy;
95 for (
int xx = 0; xx < W; xx++) {
97 double ndc_x = (2.0*(xx + 0.5)/W - 1.0) * hx;
98 double vx = ndc_x, vy2 = -ndc_y, vz = -1.0;
99 double inv = 1.0/sqrt(vx*vx + vy2*vy2 + vz*vz);
100 vx *= inv; vy2 *= inv; vz *= inv;
103 double dx = r00*vx + r01*vy2 + r02*vz;
104 double dy = r10*vx + r11*vy2 + r12*vz;
105 double dz = r20*vx + r21*vy2 + r22*vz;
117 double ax = 0.0, ay = 0.0, az =
invert ? -1.0 : 1.0;
118 double cos_t = dx*ax + dy*ay + dz*az;
119 double theta = acos(cos_t);
120 double rpx = (theta / fov_r) * (W/2.0);
121 double phi = atan2(dy, dx);
122 uf = W*0.5 + rpx*cos(phi);
123 vf = H*0.5 + rpx*sin(phi);
127 double lon = atan2(dx, dz);
128 double lat = asin(dy);
130 lon = std::clamp(lon, -M_PI/2.0, M_PI/2.0);
133 vf = (lat + M_PI/2.0)/M_PI * H;
136 uchar* d = dst_row + xx*4;
140 int x0 = std::clamp(
int(std::floor(uf)), 0, W-1);
141 int y0 = std::clamp(
int(std::floor(vf)), 0, H-1);
142 uchar* s = src + y0*bpl + x0*4;
143 d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; d[3] = s[3];
147 int x0 = std::clamp(
int(std::floor(uf)), 0, W-1);
148 int y0 = std::clamp(
int(std::floor(vf)), 0, H-1);
149 int x1 = std::clamp(x0 + 1, 0, W-1);
150 int y1 = std::clamp(y0 + 1, 0, H-1);
151 double dxr = uf - x0, dyr = vf - y0;
152 uchar* p00 = src + y0*bpl + x0*4;
153 uchar* p10 = src + y0*bpl + x1*4;
154 uchar* p01 = src + y1*bpl + x0*4;
155 uchar* p11 = src + y1*bpl + x1*4;
156 for (
int c = 0; c < 4; c++) {
157 double v0 = p00[c]*(1-dxr) + p10[c]*dxr;
158 double v1 = p01[c]*(1-dxr) + p11[c]*dxr;
159 d[c] = uchar(v0*(1-dyr) + v1*dyr + 0.5);
195 throw InvalidJSON(
"Invalid JSON for SphericalProjection");
206 if (!root[
"projection_mode"].isNull())
projection_mode = root[
"projection_mode"].asInt();
207 if (!root[
"invert"].isNull())
invert = root[
"invert"].asInt();
208 if (!root[
"interpolation"].isNull())
interpolation = root[
"interpolation"].asInt();
219 false, requested_frame);
224 false, requested_frame);
229 false, requested_frame);
234 false, requested_frame);
240 false, requested_frame);
249 false, requested_frame);
257 false, requested_frame);
261 return root.toStyledString();