OpenShot Library | libopenshot 0.5.0
Loading...
Searching...
No Matches
SphericalProjection.cpp
Go to the documentation of this file.
1
9// Copyright (c) 2008-2025 OpenShot Studios, LLC
10//
11// SPDX-License-Identifier: LGPL-3.0-or-later
12
13#include "SphericalProjection.h"
14#include "Exceptions.h"
15
16#include <algorithm>
17#include <cmath>
18#include <omp.h>
19#include <vector>
20
21using namespace openshot;
22
24 : yaw(0.0), pitch(0.0), roll(0.0), fov(90.0), in_fov(180.0),
25 projection_mode(0), invert(0), input_model(INPUT_EQUIRECT), interpolation(3)
26{
27 init_effect_details();
28}
29
31 Keyframe new_roll, Keyframe new_fov)
32 : yaw(new_yaw), pitch(new_pitch), roll(new_roll), fov(new_fov),
33 in_fov(180.0), projection_mode(0), invert(0),
34 input_model(INPUT_EQUIRECT), interpolation(3)
35{
36 init_effect_details();
37}
38
39void SphericalProjection::init_effect_details()
40{
42 info.class_name = "SphericalProjection";
43 info.name = "Spherical Projection";
45 "Flatten and reproject 360° or fisheye inputs into a rectilinear view with yaw, pitch, roll, and FOV. Supports Equirect and multiple fisheye lens models.";
46 info.has_audio = false;
47 info.has_video = true;
48}
49
50namespace {
51 inline double cubic_interp(double p0, double p1, double p2, double p3,
52 double t)
53 {
54 double a0 = -0.5 * p0 + 1.5 * p1 - 1.5 * p2 + 0.5 * p3;
55 double a1 = p0 - 2.5 * p1 + 2.0 * p2 - 0.5 * p3;
56 double a2 = -0.5 * p0 + 0.5 * p2;
57 double a3 = p1;
58 return ((a0 * t + a1) * t + a2) * t + a3;
59 }
60} // namespace
61
62std::shared_ptr<openshot::Frame>
63SphericalProjection::GetFrame(std::shared_ptr<openshot::Frame> frame,
64 int64_t frame_number) {
65 auto img = frame->GetImage();
66 if (img->format() != QImage::Format_ARGB32)
67 *img = img->convertToFormat(QImage::Format_ARGB32);
68
69 int W = img->width(), H = img->height();
70 int bpl = img->bytesPerLine();
71 uchar *src = img->bits();
72
73 QImage output(W, H, QImage::Format_ARGB32);
74 output.fill(Qt::black);
75 uchar *dst = output.bits();
76 int dst_bpl = output.bytesPerLine();
77
78 // Keyframes / angles
79 const double DEG = M_PI / 180.0;
80 double yaw_r = -yaw.GetValue(frame_number) * DEG; // drag right -> look right
81 double pitch_r = pitch.GetValue(frame_number) * DEG; // drag up -> look up
82 double roll_r = -roll.GetValue(frame_number) * DEG; // positive slider -> clockwise on screen
83 double in_fov_r = in_fov.GetValue(frame_number) * DEG;
84 double out_fov_r= fov.GetValue(frame_number) * DEG;
85
86 // Apply invert as a 180° yaw for equirect inputs (camera-centric; no mirroring)
88 yaw_r += M_PI;
89 }
90
91 // Rotation R = Ry(yaw) * Rx(pitch). (Roll applied in screen space.)
92 double sy = sin(yaw_r), cy = cos(yaw_r);
93 double sp = sin(pitch_r),cp = cos(pitch_r);
94
95 double r00 = cy;
96 double r01 = sy * sp;
97 double r02 = sy * cp;
98
99 double r10 = 0.0;
100 double r11 = cp;
101 double r12 = -sp;
102
103 double r20 = -sy;
104 double r21 = cy * sp;
105 double r22 = cy * cp;
106
107 // Keep roll clockwise on screen regardless of facing direction
108 double roll_sign = (r22 >= 0.0) ? 1.0 : -1.0;
109
110 // Perspective scalars (rectilinear)
111 double hx = tan(out_fov_r * 0.5);
112 double vy = hx * double(H) / W;
113
114 auto q = [](double a) { return std::llround(a * 1e6); };
115 bool recompute = uv_map.empty() || W != cached_width || H != cached_height ||
116 q(yaw_r) != q(cached_yaw) ||
117 q(pitch_r) != q(cached_pitch) ||
118 q(roll_r) != q(cached_roll) ||
119 q(in_fov_r) != q(cached_in_fov) ||
120 q(out_fov_r) != q(cached_out_fov) ||
121 input_model != cached_input_model ||
122 projection_mode != cached_projection_mode ||
123 invert != cached_invert;
124
125 if (recompute) {
126 uv_map.resize(W * H * 2);
127
128#pragma omp parallel for schedule(static)
129 for (int yy = 0; yy < H; yy++) {
130 double ndc_y = (2.0 * (yy + 0.5) / H - 1.0) * vy;
131
132 for (int xx = 0; xx < W; xx++) {
133 double uf = -1.0, vf = -1.0;
134
135 const bool out_is_rect =
137
138 if (!out_is_rect) {
139 // ---------------- FISHEYE OUTPUT ----------------
140 double cx = (xx + 0.5) - W * 0.5;
141 double cy_dn = (yy + 0.5) - H * 0.5;
142 double R = 0.5 * std::min(W, H);
143
144 // screen plane, Y-up; apply roll by -roll (clockwise), adjusted by roll_sign
145 double rx = cx / R;
146 double ry_up = -cy_dn / R;
147 double cR = cos(roll_r), sR = sin(roll_r) * roll_sign;
148 double rxr = cR * rx + sR * ry_up;
149 double ryr = -sR * rx + cR * ry_up;
150
151 double r_norm = std::sqrt(rxr * rxr + ryr * ryr);
152 if (r_norm <= 1.0) {
153 double theta_max = out_fov_r * 0.5;
154 double theta = 0.0;
155 switch (projection_mode) {
157 // r ∝ θ
158 theta = r_norm * theta_max;
159 break;
161 // r ∝ 2 sin(θ/2)
162 theta = 2.0 * std::asin(std::clamp(r_norm * std::sin(theta_max * 0.5), -1.0, 1.0));
163 break;
165 // r ∝ 2 tan(θ/2)
166 theta = 2.0 * std::atan(r_norm * std::tan(theta_max * 0.5));
167 break;
169 // r ∝ sin(θ)
170 theta = std::asin(std::clamp(r_norm * std::sin(theta_max), -1.0, 1.0));
171 break;
172 default:
173 theta = r_norm * theta_max;
174 break;
175 }
176
177 // NOTE: Y was upside-down; fix by using +ryr (not -ryr)
178 double phi = std::atan2(ryr, rxr);
179
180 // Camera ray from fisheye output
181 double vx = std::sin(theta) * std::cos(phi);
182 double vy2= std::sin(theta) * std::sin(phi);
183 double vz = -std::cos(theta);
184
185 // Rotate into world
186 double dx = r00 * vx + r01 * vy2 + r02 * vz;
187 double dy = r10 * vx + r11 * vy2 + r12 * vz;
188 double dz = r20 * vx + r21 * vy2 + r22 * vz;
189
190 project_input(dx, dy, dz, in_fov_r, W, H, uf, vf);
191 } else {
192 uf = vf = -1.0; // outside disk
193 }
194
195 } else {
196 // ---------------- RECTILINEAR OUTPUT ----------------
197 double ndc_x = (2.0 * (xx + 0.5) / W - 1.0) * hx;
198
199 // screen plane Y-up; roll by -roll (clockwise), adjusted by roll_sign
200 double sx = ndc_x;
201 double sy_up = -ndc_y;
202 double cR = cos(roll_r), sR = sin(roll_r) * roll_sign;
203 double rx = cR * sx + sR * sy_up;
204 double ry = -sR * sx + cR * sy_up;
205
206 // Camera ray (camera looks down -Z)
207 double vx = rx, vy2 = ry, vz = -1.0;
208 double inv_len = 1.0 / std::sqrt(vx*vx + vy2*vy2 + vz*vz);
209 vx *= inv_len; vy2 *= inv_len; vz *= inv_len;
210
211 // Rotate into world
212 double dx = r00 * vx + r01 * vy2 + r02 * vz;
213 double dy = r10 * vx + r11 * vy2 + r12 * vz;
214 double dz = r20 * vx + r21 * vy2 + r22 * vz;
215
216 project_input(dx, dy, dz, in_fov_r, W, H, uf, vf);
217 }
218
219 int idx = 2 * (yy * W + xx);
220 uv_map[idx] = (float)uf;
221 uv_map[idx + 1] = (float)vf;
222 }
223 }
224
225 cached_width = W;
226 cached_height = H;
227 cached_yaw = yaw_r;
228 cached_pitch = pitch_r;
229 cached_roll = roll_r;
230 cached_in_fov = in_fov_r;
231 cached_out_fov= out_fov_r;
232 cached_input_model = input_model;
233 cached_projection_mode = projection_mode;
234 cached_invert = invert;
235 }
236
237 // Auto sampler selection (uses enums)
238 int sampler = interpolation;
239 if (interpolation == INTERP_AUTO) {
240 double coverage_r =
241 (projection_mode == MODE_RECT_SPHERE) ? 2.0 * M_PI :
243 in_fov_r; // rough heuristic otherwise
244 double ppd_src = W / coverage_r;
245 double ppd_out = W / out_fov_r;
246 double ratio = ppd_out / ppd_src;
247 if (ratio < 0.8) sampler = INTERP_AUTO; // mipmaps path below
248 else if (ratio <= 1.2) sampler = INTERP_BILINEAR;
249 else sampler = INTERP_BICUBIC;
250 }
251
252 // Build mipmaps only if needed (box)
253 std::vector<QImage> mipmaps;
254 if (sampler == INTERP_AUTO) {
255 mipmaps.push_back(*img);
256 for (int level = 1; level < 4; ++level) {
257 const QImage &prev = mipmaps[level - 1];
258 if (prev.width() <= 1 || prev.height() <= 1) break;
259 int w = prev.width() / 2, h = prev.height() / 2;
260 QImage next(w, h, QImage::Format_ARGB32);
261 uchar *nb = next.bits(); int nbpl = next.bytesPerLine();
262 const uchar *pb = prev.bits(); int pbpl = prev.bytesPerLine();
263 for (int y = 0; y < h; y++) {
264 for (int x = 0; x < w; x++) {
265 for (int c = 0; c < 4; c++) {
266 int p00 = pb[(2*y) * pbpl + (2*x) * 4 + c];
267 int p10 = pb[(2*y) * pbpl + (2*x+1) * 4 + c];
268 int p01 = pb[(2*y+1) * pbpl + (2*x) * 4 + c];
269 int p11 = pb[(2*y+1) * pbpl + (2*x+1) * 4 + c];
270 nb[y * nbpl + x * 4 + c] = (p00 + p10 + p01 + p11) / 4;
271 }
272 }
273 }
274 mipmaps.push_back(next);
275 }
276 }
277
278#pragma omp parallel for schedule(static)
279 for (int yy = 0; yy < H; yy++) {
280 uchar *dst_row = dst + yy * dst_bpl;
281 for (int xx = 0; xx < W; xx++) {
282 int idx = 2 * (yy * W + xx);
283 double uf = uv_map[idx];
284 double vf = uv_map[idx + 1];
285 uchar *d = dst_row + xx * 4;
286
288 uf = std::fmod(std::fmod(uf, W) + W, W);
289 vf = std::clamp(vf, 0.0, (double)H - 1);
291 uf = std::clamp(uf, 0.0, (double)W - 1);
292 vf = std::clamp(vf, 0.0, (double)H - 1);
293 } else if (uf < 0 || uf >= W || vf < 0 || vf >= H) {
294 d[0] = d[1] = d[2] = 0; d[3] = 0;
295 continue;
296 }
297
298 if (sampler == INTERP_NEAREST) {
299 int x0 = std::clamp(int(std::floor(uf)), 0, W - 1);
300 int y0 = std::clamp(int(std::floor(vf)), 0, H - 1);
301 uchar *s = src + y0 * bpl + x0 * 4;
302 d[0]=s[0]; d[1]=s[1]; d[2]=s[2]; d[3]=s[3];
303 } else if (sampler == INTERP_BILINEAR) {
304 int x0 = std::clamp(int(std::floor(uf)), 0, W - 1);
305 int y0 = std::clamp(int(std::floor(vf)), 0, H - 1);
306 int x1 = std::clamp(x0 + 1, 0, W - 1);
307 int y1 = std::clamp(y0 + 1, 0, H - 1);
308 double dxr = uf - x0, dyr = vf - y0;
309 uchar *p00 = src + y0 * bpl + x0 * 4;
310 uchar *p10 = src + y0 * bpl + x1 * 4;
311 uchar *p01 = src + y1 * bpl + x0 * 4;
312 uchar *p11 = src + y1 * bpl + x1 * 4;
313 for (int c = 0; c < 4; c++) {
314 double v0 = p00[c] * (1 - dxr) + p10[c] * dxr;
315 double v1 = p01[c] * (1 - dxr) + p11[c] * dxr;
316 d[c] = uchar(v0 * (1 - dyr) + v1 * dyr + 0.5);
317 }
318 } else if (sampler == INTERP_BICUBIC) {
319 int x1 = std::clamp(int(std::floor(uf)), 0, W - 1);
320 int y1 = std::clamp(int(std::floor(vf)), 0, H - 1);
321 double tx = uf - x1, ty = vf - y1;
322 for (int c = 0; c < 4; c++) {
323 double col[4];
324 for (int j = -1; j <= 2; j++) {
325 int y = std::clamp(y1 + j, 0, H - 1);
326 double row[4];
327 for (int i = -1; i <= 2; i++) {
328 int x = std::clamp(x1 + i, 0, W - 1);
329 row[i + 1] = src[y * bpl + x * 4 + c];
330 }
331 col[j + 1] = cubic_interp(row[0], row[1], row[2], row[3], tx);
332 }
333 double val = cubic_interp(col[0], col[1], col[2], col[3], ty);
334 d[c] = uchar(std::clamp(val, 0.0, 255.0) + 0.5);
335 }
336 } else { // INTERP_AUTO -> mipmaps + bilinear
337 double uf_dx = 0.0, vf_dx = 0.0, uf_dy = 0.0, vf_dy = 0.0;
338 if (xx + 1 < W) { uf_dx = uv_map[idx + 2] - uf; vf_dx = uv_map[idx + 3] - vf; }
339 if (yy + 1 < H) { uf_dy = uv_map[idx + 2 * W] - uf; vf_dy = uv_map[idx + 2 * W + 1] - vf; }
340 double scale_x = std::sqrt(uf_dx*uf_dx + vf_dx*vf_dx);
341 double scale_y = std::sqrt(uf_dy*uf_dy + vf_dy*vf_dy);
342 double scale = std::max(scale_x, scale_y);
343 int level = 0;
344 if (scale > 1.0)
345 level = std::min<int>(std::floor(std::log2(scale)), (int)mipmaps.size() - 1);
346 const QImage &lvl = mipmaps[level];
347 int Wl = lvl.width(), Hl = lvl.height();
348 int bpl_l = lvl.bytesPerLine();
349 const uchar *srcl = lvl.bits();
350 double uf_l = uf / (1 << level);
351 double vf_l = vf / (1 << level);
352 int x0 = std::clamp(int(std::floor(uf_l)), 0, Wl - 1);
353 int y0 = std::clamp(int(std::floor(vf_l)), 0, Hl - 1);
354 int x1 = std::clamp(x0 + 1, 0, Wl - 1);
355 int y1 = std::clamp(y0 + 1, 0, Hl - 1);
356 double dxr = uf_l - x0, dyr = vf_l - y0;
357 const uchar *p00 = srcl + y0 * bpl_l + x0 * 4;
358 const uchar *p10 = srcl + y0 * bpl_l + x1 * 4;
359 const uchar *p01 = srcl + y1 * bpl_l + x0 * 4;
360 const uchar *p11 = srcl + y1 * bpl_l + x1 * 4;
361 for (int c = 0; c < 4; c++) {
362 double v0 = p00[c] * (1 - dxr) + p10[c] * dxr;
363 double v1 = p01[c] * (1 - dxr) + p11[c] * dxr;
364 d[c] = uchar(v0 * (1 - dyr) + v1 * dyr + 0.5);
365 }
366 }
367 }
368 }
369
370 *img = output;
371 return frame;
372}
373
374void SphericalProjection::project_input(double dx, double dy, double dz,
375 double in_fov_r, int W, int H,
376 double &uf, double &vf) const {
378 // Center (-Z) -> lon=0; +X (screen right) -> +lon
379 double lon = std::atan2(dx, -dz);
380 double lat = std::asin(std::clamp(dy, -1.0, 1.0));
381
383 lon = std::clamp(lon, -M_PI / 2.0, M_PI / 2.0);
384
385 double horiz_span = (projection_mode == MODE_RECT_HEMISPHERE) ? M_PI : 2.0 * M_PI;
386 double lon_offset = (projection_mode == MODE_RECT_HEMISPHERE) ? M_PI / 2.0 : M_PI;
387 uf = ((lon + lon_offset) / horiz_span) * W;
388
389 // Image Y grows downward: north (lat = +π/2) at top
390 vf = (M_PI / 2.0 - lat) / M_PI * H;
391 return;
392 }
393
394 // -------- Fisheye inputs --------
395 // Optical axis default is -Z; "Invert" flips hemisphere.
396 const double ax = 0.0, ay = 0.0;
397 double az = -1.0;
398 if (invert == INVERT_BACK) az = 1.0;
399
400 double cos_t = std::clamp(dx * ax + dy * ay + dz * az, -1.0, 1.0);
401 double theta = std::acos(cos_t);
402 double tmax = std::max(1e-6, in_fov_r * 0.5);
403
404 double r_norm = 0.0;
405 switch (input_model) {
406 case INPUT_FEQ_EQUIDISTANT: r_norm = theta / tmax; break;
407 case INPUT_FEQ_EQUISOLID: r_norm = std::sin(theta*0.5) / std::max(1e-12, std::sin(tmax*0.5)); break;
408 case INPUT_FEQ_STEREOGRAPHIC: r_norm = std::tan(theta*0.5) / std::max(1e-12, std::tan(tmax*0.5)); break;
409 case INPUT_FEQ_ORTHOGRAPHIC: r_norm = std::sin(theta) / std::max(1e-12, std::sin(tmax)); break;
410 default: r_norm = theta / tmax; break;
411 }
412
413 // Azimuth in camera XY; final Y is downward -> subtract sine in vf
414 double phi = std::atan2(dy, dx);
415
416 double R = 0.5 * std::min(W, H);
417 double rpx = r_norm * R;
418 uf = W * 0.5 + rpx * std::cos(phi);
419 vf = H * 0.5 - rpx * std::sin(phi);
420}
421
422std::string SphericalProjection::Json() const
423{
424 return JsonValue().toStyledString();
425}
426
428{
429 Json::Value root = EffectBase::JsonValue();
430 root["type"] = info.class_name;
431 root["yaw"] = yaw.JsonValue();
432 root["pitch"] = pitch.JsonValue();
433 root["roll"] = roll.JsonValue();
434 root["fov"] = fov.JsonValue();
435 root["in_fov"] = in_fov.JsonValue();
436 root["projection_mode"] = projection_mode;
437 root["invert"] = invert;
438 root["input_model"] = input_model;
439 root["interpolation"] = interpolation;
440 return root;
441}
442
443void SphericalProjection::SetJson(const std::string value)
444{
445 try
446 {
447 Json::Value root = openshot::stringToJson(value);
448 SetJsonValue(root);
449 }
450 catch (...)
451 {
452 throw InvalidJSON("Invalid JSON for SphericalProjection");
453 }
454}
455
456void SphericalProjection::SetJsonValue(const Json::Value root)
457{
459
460 if (!root["yaw"].isNull()) yaw.SetJsonValue(root["yaw"]);
461 if (!root["pitch"].isNull()) pitch.SetJsonValue(root["pitch"]);
462 if (!root["roll"].isNull()) roll.SetJsonValue(root["roll"]);
463 if (!root["fov"].isNull()) fov.SetJsonValue(root["fov"]);
464 if (!root["in_fov"].isNull()) in_fov.SetJsonValue(root["in_fov"]);
465
466 if (!root["projection_mode"].isNull())
467 projection_mode = root["projection_mode"].asInt();
468
469 if (!root["invert"].isNull())
470 invert = root["invert"].asInt();
471
472 if (!root["input_model"].isNull())
473 input_model = root["input_model"].asInt();
474
475 if (!root["interpolation"].isNull())
476 interpolation = root["interpolation"].asInt();
477
478 // Clamp to enum options
479 projection_mode = std::clamp(projection_mode,
480 (int)MODE_RECT_SPHERE,
482 invert = std::clamp(invert, (int)INVERT_NORMAL, (int)INVERT_BACK);
484 interpolation = std::clamp(interpolation, (int)INTERP_NEAREST, (int)INTERP_AUTO);
485
486 // any property change should invalidate cached UV map
487 uv_map.clear();
488
489
490 // any property change should invalidate cached UV map
491 uv_map.clear();
492}
493std::string SphericalProjection::PropertiesJSON(int64_t requested_frame) const
494{
495 Json::Value root = BasePropertiesJSON(requested_frame);
496
497 root["yaw"] = add_property_json("Yaw", yaw.GetValue(requested_frame), "float", "degrees", &yaw, -180, 180, false, requested_frame);
498 root["pitch"] = add_property_json("Pitch", pitch.GetValue(requested_frame), "float", "degrees", &pitch,-180, 180, false, requested_frame);
499 root["roll"] = add_property_json("Roll", roll.GetValue(requested_frame), "float", "degrees", &roll, -180, 180, false, requested_frame);
500
501 root["fov"] = add_property_json("Out FOV", fov.GetValue(requested_frame), "float", "degrees", &fov, 0, 179, false, requested_frame);
502 root["in_fov"] = add_property_json("In FOV", in_fov.GetValue(requested_frame), "float", "degrees", &in_fov, 1, 360, false, requested_frame);
503
504 root["projection_mode"] = add_property_json("Projection Mode", projection_mode, "int", "", nullptr,
505 (int)MODE_RECT_SPHERE, (int)MODE_FISHEYE_ORTHOGRAPHIC, false, requested_frame);
506 root["projection_mode"]["choices"].append(add_property_choice_json("Sphere", (int)MODE_RECT_SPHERE, projection_mode));
507 root["projection_mode"]["choices"].append(add_property_choice_json("Hemisphere", (int)MODE_RECT_HEMISPHERE, projection_mode));
508 root["projection_mode"]["choices"].append(add_property_choice_json("Fisheye: Equidistant", (int)MODE_FISHEYE_EQUIDISTANT, projection_mode));
509 root["projection_mode"]["choices"].append(add_property_choice_json("Fisheye: Equisolid", (int)MODE_FISHEYE_EQUISOLID, projection_mode));
510 root["projection_mode"]["choices"].append(add_property_choice_json("Fisheye: Stereographic", (int)MODE_FISHEYE_STEREOGRAPHIC,projection_mode));
511 root["projection_mode"]["choices"].append(add_property_choice_json("Fisheye: Orthographic", (int)MODE_FISHEYE_ORTHOGRAPHIC, projection_mode));
512
513 root["invert"] = add_property_json("Invert View", invert, "int", "", nullptr, 0, 1, false, requested_frame);
514 root["invert"]["choices"].append(add_property_choice_json("Normal", 0, invert));
515 root["invert"]["choices"].append(add_property_choice_json("Invert", 1, invert));
516
517 root["input_model"] = add_property_json("Input Model", input_model, "int", "", nullptr, INPUT_EQUIRECT, INPUT_FEQ_ORTHOGRAPHIC, false, requested_frame);
518 root["input_model"]["choices"].append(add_property_choice_json("Equirectangular (Panorama)", INPUT_EQUIRECT, input_model));
519 root["input_model"]["choices"].append(add_property_choice_json("Fisheye: Equidistant", INPUT_FEQ_EQUIDISTANT, input_model));
520 root["input_model"]["choices"].append(add_property_choice_json("Fisheye: Equisolid", INPUT_FEQ_EQUISOLID, input_model));
521 root["input_model"]["choices"].append(add_property_choice_json("Fisheye: Stereographic", INPUT_FEQ_STEREOGRAPHIC, input_model));
522 root["input_model"]["choices"].append(add_property_choice_json("Fisheye: Orthographic", INPUT_FEQ_ORTHOGRAPHIC, input_model));
523
524 root["interpolation"] = add_property_json("Interpolation", interpolation, "int", "", nullptr, 0, 3, false, requested_frame);
525 root["interpolation"]["choices"].append(add_property_choice_json("Nearest", 0, interpolation));
526 root["interpolation"]["choices"].append(add_property_choice_json("Bilinear", 1, interpolation));
527 root["interpolation"]["choices"].append(add_property_choice_json("Bicubic", 2, interpolation));
528 root["interpolation"]["choices"].append(add_property_choice_json("Auto", 3, interpolation));
529
530 return root.toStyledString();
531}
Header file for all Exception classes.
Header file for SphericalProjection effect class.
Json::Value add_property_choice_json(std::string name, int value, int selected_value) const
Generate JSON choice for a property (dropdown properties)
Definition ClipBase.cpp:132
Json::Value add_property_json(std::string name, float value, std::string type, std::string memo, const Keyframe *keyframe, float min_value, float max_value, bool readonly, int64_t requested_frame) const
Generate JSON for a property.
Definition ClipBase.cpp:96
virtual Json::Value JsonValue() const
Generate Json::Value for this object.
Json::Value BasePropertiesJSON(int64_t requested_frame) const
Generate JSON object of base properties (recommended to be used by all effects)
virtual void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
EffectInfoStruct info
Information about the current effect.
Definition EffectBase.h:69
Exception for invalid JSON.
Definition Exceptions.h:218
A Keyframe is a collection of Point instances, which is used to vary a number or property over time.
Definition KeyFrame.h:53
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition KeyFrame.cpp:372
double GetValue(int64_t index) const
Get the value at a specific index.
Definition KeyFrame.cpp:258
Json::Value JsonValue() const
Generate Json::Value for this object.
Definition KeyFrame.cpp:339
int projection_mode
0=Sphere, 1=Hemisphere, 2=Fisheye
Keyframe in_fov
Source lens coverage / FOV (degrees)
void SetJson(std::string value) override
Load JSON string into this object.
void SetJsonValue(Json::Value root) override
Load Json::Value into this object.
Keyframe yaw
Yaw around up-axis (degrees)
Keyframe fov
Output field-of-view (degrees)
Json::Value JsonValue() const override
Generate Json::Value for this object.
int invert
0=Normal, 1=Invert (back lens / +180°)
std::shared_ptr< Frame > GetFrame(int64_t frame_number) override
ClipBase override: create a fresh Frame then call the main GetFrame.
Keyframe roll
Roll around forward-axis (degrees)
SphericalProjection()
Blank ctor (for JSON deserialization)
Keyframe pitch
Pitch around right-axis (degrees)
int input_model
0=Equirect, 1=Fisheye-Equidistant
std::string Json() const override
Generate JSON string of this object.
std::string PropertiesJSON(int64_t requested_frame) const override
int interpolation
0=Nearest, 1=Bilinear, 2=Bicubic, 3=Auto
This namespace is the default namespace for all code in the openshot library.
Definition Compressor.h:29
const Json::Value stringToJson(const std::string value)
Definition Json.cpp:16
bool has_video
Determines if this effect manipulates the image of a frame.
Definition EffectBase.h:40
bool has_audio
Determines if this effect manipulates the audio of a frame.
Definition EffectBase.h:41
std::string class_name
The class name of the effect.
Definition EffectBase.h:36
std::string name
The name of the effect.
Definition EffectBase.h:37
std::string description
The description of this effect and what it does.
Definition EffectBase.h:38