@@ -35,19 +35,31 @@ pub fn export_full_metadata(gyro_url: &str, _stab: &Arc<crate::StabilizationMana
3535 Ok ( serde_json:: to_string_pretty ( & output) ?)
3636}
3737
38- pub fn export_gyro_data ( is_csv : bool , fields_json : & str , stab : & Arc < crate :: StabilizationManager > ) -> String {
38+ pub fn export_gyro_data ( filename : & str , fields_json : & str , stab : & Arc < crate :: StabilizationManager > ) -> String {
3939 use std:: fmt:: Write ;
4040 use crate :: util:: MapClosest ;
4141 const RAD2DEG : f64 = 180.0 / std:: f64:: consts:: PI ;
4242 enum TimestampType {
4343 Milliseconds ( f64 ) ,
4444 Microseconds ( i64 ) ,
4545 }
46+ #[ derive( PartialEq , Eq ) ]
47+ enum Format {
48+ Csv , Json , Usd , Jsx
49+ }
4650 fn get ( f : Option < [ f64 ; 3 ] > , i : usize ) -> f64 { f. map ( |x| x[ i] ) . unwrap_or_default ( ) }
4751
52+ let format = match filename. split ( '.' ) . last ( ) . unwrap_or_default ( ) {
53+ "csv" => Format :: Csv ,
54+ "json" => Format :: Json ,
55+ "usd" => Format :: Usd ,
56+ "jsx" => Format :: Jsx ,
57+ _ => Format :: Csv ,
58+ } ;
59+
4860 let fields: serde_json:: Value = serde_json:: from_str ( fields_json) . unwrap ( ) ;
4961
50- let all_samples = fields. get ( "export_all_samples" ) . and_then ( |x| x. as_bool ( ) ) . unwrap_or_default ( ) ;
62+ let all_samples = fields. get ( "export_all_samples" ) . and_then ( |x| x. as_bool ( ) ) . unwrap_or_default ( ) && format != Format :: Usd && format != Format :: Jsx ;
5163
5264 let original = fields. get ( "original" ) . and_then ( |x| x. as_object ( ) ) . unwrap ( ) ;
5365 let stabilized = fields. get ( "stabilized" ) . and_then ( |x| x. as_object ( ) ) . unwrap ( ) ;
@@ -65,19 +77,20 @@ pub fn export_gyro_data(is_csv: bool, fields_json: &str, stab: &Arc<crate::Stabi
6577 let fovs = zooming. get ( "fovs" ) . and_then ( |x| x. as_bool ( ) ) . unwrap_or_default ( ) ;
6678 let minimal_fovs = zooming. get ( "minimal_fovs" ) . and_then ( |x| x. as_bool ( ) ) . unwrap_or_default ( ) ;
6779
68- let mut csv = String :: from ( "frame,timestamp_ms" ) ;
80+ let mut output = String :: new ( ) ;
6981 let mut json = Vec :: < std:: collections:: HashMap < & str , serde_json:: Value > > :: new ( ) ;
70- if is_csv {
71- if oaccl { let _ = write ! ( csv, ",org_acc_x,org_acc_y,org_acc_z" ) ; }
72- if oeulr { let _ = write ! ( csv, ",org_pitch,org_yaw,org_roll" ) ; }
73- if ogyro { let _ = write ! ( csv, ",org_gyro_x,org_gyro_y,org_gyro_z" ) ; }
74- if oquat { let _ = write ! ( csv, ",org_quat_w,org_quat_x,org_quat_y,org_quat_z" ) ; }
75- if seulr { let _ = write ! ( csv, ",stab_pitch,stab_yaw,stab_roll" ) ; }
76- if squat { let _ = write ! ( csv, ",stab_quat_w,stab_quat_x,stab_quat_y,stab_quat_z" ) ; }
77- if focal_length { let _ = write ! ( csv, ",focal_length" ) ; }
78- if fovs { let _ = write ! ( csv, ",fov_scale" ) ; }
79- if minimal_fovs { let _ = write ! ( csv, ",minimal_fov_scale" ) ; }
80- let _ = write ! ( csv, "\n " ) ;
82+ if format == Format :: Csv {
83+ let _ = write ! ( output, "frame,timestamp_ms" ) ;
84+ if oaccl { let _ = write ! ( output, ",org_acc_x,org_acc_y,org_acc_z" ) ; }
85+ if oeulr { let _ = write ! ( output, ",org_pitch,org_yaw,org_roll" ) ; }
86+ if ogyro { let _ = write ! ( output, ",org_gyro_x,org_gyro_y,org_gyro_z" ) ; }
87+ if oquat { let _ = write ! ( output, ",org_quat_w,org_quat_x,org_quat_y,org_quat_z" ) ; }
88+ if seulr { let _ = write ! ( output, ",stab_pitch,stab_yaw,stab_roll" ) ; }
89+ if squat { let _ = write ! ( output, ",stab_quat_w,stab_quat_x,stab_quat_y,stab_quat_z" ) ; }
90+ if focal_length { let _ = write ! ( output, ",focal_length" ) ; }
91+ if fovs { let _ = write ! ( output, ",fov_scale" ) ; }
92+ if minimal_fovs { let _ = write ! ( output, ",minimal_fov_scale" ) ; }
93+ let _ = write ! ( output, "\n " ) ;
8194 }
8295
8396 let params = stab. params . read ( ) ;
@@ -110,6 +123,44 @@ pub fn export_gyro_data(is_csv: bool, fields_json: &str, stab: &Arc<crate::Stabi
110123 ( None , frame, TimestampType :: Milliseconds ( middle_timestamp) , timestamp_ms)
111124 } ) . collect ( )
112125 } ;
126+ let num_frames = params. frame_count ;
127+ let fps = params. get_scaled_fps ( ) ;
128+ let frame_times = serde_json:: to_string ( & ( 0 ..num_frames) . map ( |i| i as f64 / fps) . collect :: < Vec < _ > > ( ) ) . unwrap ( ) ;
129+
130+ if format == Format :: Usd {
131+ output. push_str ( & format ! ( r#"#usda 1.0
132+ (
133+ defaultPrim = "root"
134+ doc = "Gyroflow"
135+ endTimeCode = {num_frames}
136+ metersPerUnit = 1
137+ startTimeCode = 1
138+ timeCodesPerSecond = {fps:.6}
139+ upAxis = "Z"
140+ )
141+ def Xform "root" (
142+ customData = {{
143+ dictionary Blender = {{
144+ bool generated = 1
145+ }}
146+ }}
147+ )
148+ {{
149+ def Xform "GyroflowCamera"
150+ {{
151+ matrix4d xformOp:transform.timeSamples = {{
152+ "#
153+ ) ) ;
154+ }
155+
156+ if format == Format :: Jsx {
157+ let duration_s = params. duration_ms / 1000.0 ;
158+ output. push_str ( & format ! ( r#"var comp = app.project.activeItem;
159+ var GyroflowCamera = comp.layers.addCamera("GyroflowCamera",[0,0]);
160+ GyroflowCamera.inPoint = 0.0;
161+ GyroflowCamera.outPoint = {duration_s};
162+ GyroflowCamera.property("orientation").setValuesAtTimes({frame_times}, ["# ) ) ;
163+ }
113164
114165 let raw_imu = gyro. raw_imu ( & file_metadata) ;
115166
@@ -123,12 +174,38 @@ pub fn export_gyro_data(is_csv: bool, fields_json: &str, stab: &Arc<crate::Stabi
123174 let val_ogyro = [ get ( raw_imu. gyro , 0 ) , get ( raw_imu. gyro , 1 ) , get ( raw_imu. gyro , 2 ) ] ;
124175 let val_oquat = [ quatv[ 3 ] , quatv[ 0 ] , quatv[ 1 ] , quatv[ 2 ] ] ;
125176
177+ if format == Format :: Jsx && !( seulr && !oeulr) {
178+ output. push_str ( & format ! ( "[{},{},{}],\n " , val_oeulr[ 0 ] , -val_oeulr[ 2 ] , val_oeulr[ 1 ] ) ) ;
179+ }
180+ if format == Format :: Usd && !( seulr && !oeulr) {
181+ let matrix = nalgebra:: Matrix4 :: from ( quat) ;
182+ output. push_str ( & format ! ( " {}: ( ({},{},{}, 0), ({}, {}, {}, 0), ({}, {}, {}, 0), (7.0, -7.0, 1.5, 1) ),\n " ,
183+ frame + 1 ,
184+ matrix[ ( 0 , 0 ) ] , matrix[ ( 1 , 0 ) ] , matrix[ ( 2 , 0 ) ] ,
185+ matrix[ ( 0 , 1 ) ] , matrix[ ( 1 , 1 ) ] , matrix[ ( 2 , 1 ) ] ,
186+ matrix[ ( 0 , 2 ) ] , matrix[ ( 1 , 2 ) ] , matrix[ ( 2 , 2 ) ]
187+ ) ) ;
188+ }
189+
126190 let quat = match ts { TimestampType :: Microseconds ( ts) => * gyro. smoothed_quaternions . get ( & ts) . unwrap ( ) , TimestampType :: Milliseconds ( ts) => gyro. smoothed_quat_at_timestamp ( ts) } ;
127191 let quate = quat. euler_angles ( ) ;
128192 let quatv = quat. as_vector ( ) ;
129193 let val_seulr = [ quate. 0 * RAD2DEG , quate. 1 * RAD2DEG , quate. 2 * RAD2DEG ] ;
130194 let val_squat = [ quatv[ 3 ] , quatv[ 0 ] , quatv[ 1 ] , quatv[ 2 ] ] ;
131195
196+ if format == Format :: Jsx && ( seulr && !oeulr) {
197+ output. push_str ( & format ! ( "[{},{},{}],\n " , val_seulr[ 0 ] , -val_seulr[ 2 ] , val_seulr[ 1 ] ) ) ;
198+ }
199+ if format == Format :: Usd && ( seulr && !oeulr) {
200+ let matrix = nalgebra:: Matrix4 :: from ( quat) ;
201+ output. push_str ( & format ! ( " {}: ( ({},{},{}, 0), ({}, {}, {}, 0), ({}, {}, {}, 0), (7.0, -7.0, 1.5, 1) ),\n " ,
202+ frame + 1 ,
203+ matrix[ ( 0 , 0 ) ] , matrix[ ( 1 , 0 ) ] , matrix[ ( 2 , 0 ) ] ,
204+ matrix[ ( 0 , 1 ) ] , matrix[ ( 1 , 1 ) ] , matrix[ ( 2 , 1 ) ] ,
205+ matrix[ ( 0 , 2 ) ] , matrix[ ( 1 , 2 ) ] , matrix[ ( 2 , 2 ) ]
206+ ) ) ;
207+ }
208+
132209 if let Some ( val) = file_metadata. lens_params . get_closest ( & ( ( timestamp_ms * 1000.0 ) . round ( ) as i64 ) , 100000 ) { // closest within 100ms
133210 if let Some ( fl) = val. focal_length {
134211 focal_length_value = Some ( fl as f64 ) ;
@@ -138,19 +215,19 @@ pub fn export_gyro_data(is_csv: bool, fields_json: &str, stab: &Arc<crate::Stabi
138215 let val_fov = * params. fovs . get ( frame) . unwrap_or ( & 0.0 ) ;
139216 let val_minimal_fov = * params. minimal_fovs . get ( frame) . unwrap_or ( & 0.0 ) ;
140217
141- if is_csv {
142- let _ = write ! ( csv , "{frame},{timestamp_ms:.3}" ) ;
143- if oaccl { let _ = write ! ( csv , ",{:.6},{:.6},{:.6}" , val_oaccl[ 0 ] , val_oaccl[ 1 ] , val_oaccl[ 2 ] ) ; }
144- if oeulr { let _ = write ! ( csv , ",{:.3},{:.3},{:.3}" , val_oeulr[ 0 ] , val_oeulr[ 1 ] , val_oeulr[ 2 ] ) ; }
145- if ogyro { let _ = write ! ( csv , ",{:.6},{:.6},{:.6}" , val_ogyro[ 0 ] , val_ogyro[ 1 ] , val_ogyro[ 2 ] ) ; }
146- if oquat { let _ = write ! ( csv , ",{:.6},{:.6},{:.6},{:.6}" , val_oquat[ 0 ] , val_oquat[ 1 ] , val_oquat[ 2 ] , val_oquat[ 3 ] ) ; }
147- if seulr { let _ = write ! ( csv , ",{:.3},{:.3},{:.3}" , val_seulr[ 0 ] , val_seulr[ 1 ] , val_seulr[ 2 ] ) ; }
148- if squat { let _ = write ! ( csv , ",{:.6},{:.6},{:.6},{:.6}" , val_squat[ 0 ] , val_squat[ 1 ] , val_squat[ 2 ] , val_squat[ 3 ] ) ; }
149- if focal_length { let _ = write ! ( csv , ",{val_fl:.3}" ) ; }
150- if fovs { let _ = write ! ( csv , ",{val_fov:.3}" ) ; }
151- if minimal_fovs { let _ = write ! ( csv , ",{val_minimal_fov:.3}" ) ; }
152- let _ = write ! ( csv , "\n " ) ;
153- } else {
218+ if format == Format :: Csv {
219+ let _ = write ! ( output , "{frame},{timestamp_ms:.3}" ) ;
220+ if oaccl { let _ = write ! ( output , ",{:.6},{:.6},{:.6}" , val_oaccl[ 0 ] , val_oaccl[ 1 ] , val_oaccl[ 2 ] ) ; }
221+ if oeulr { let _ = write ! ( output , ",{:.3},{:.3},{:.3}" , val_oeulr[ 0 ] , val_oeulr[ 1 ] , val_oeulr[ 2 ] ) ; }
222+ if ogyro { let _ = write ! ( output , ",{:.6},{:.6},{:.6}" , val_ogyro[ 0 ] , val_ogyro[ 1 ] , val_ogyro[ 2 ] ) ; }
223+ if oquat { let _ = write ! ( output , ",{:.6},{:.6},{:.6},{:.6}" , val_oquat[ 0 ] , val_oquat[ 1 ] , val_oquat[ 2 ] , val_oquat[ 3 ] ) ; }
224+ if seulr { let _ = write ! ( output , ",{:.3},{:.3},{:.3}" , val_seulr[ 0 ] , val_seulr[ 1 ] , val_seulr[ 2 ] ) ; }
225+ if squat { let _ = write ! ( output , ",{:.6},{:.6},{:.6},{:.6}" , val_squat[ 0 ] , val_squat[ 1 ] , val_squat[ 2 ] , val_squat[ 3 ] ) ; }
226+ if focal_length { let _ = write ! ( output , ",{val_fl:.3}" ) ; }
227+ if fovs { let _ = write ! ( output , ",{val_fov:.3}" ) ; }
228+ if minimal_fovs { let _ = write ! ( output , ",{val_minimal_fov:.3}" ) ; }
229+ let _ = write ! ( output , "\n " ) ;
230+ } else if format == Format :: Json {
154231 let mut obj = std:: collections:: HashMap :: new ( ) ;
155232 obj. insert ( "frame" , frame. into ( ) ) ;
156233 obj. insert ( "timestamp_ms" , timestamp_ms. into ( ) ) ;
@@ -166,9 +243,71 @@ pub fn export_gyro_data(is_csv: bool, fields_json: &str, stab: &Arc<crate::Stabi
166243 json. push ( obj) ;
167244 }
168245 }
246+ let mut comp_params = crate :: stabilization:: ComputeParams :: from_manager ( stab) ;
247+
248+ if format == Format :: Jsx {
249+ output = output. trim_end_matches ( ",\n " ) . to_string ( ) ;
250+ output. push_str ( "]);\n " ) ;
251+
252+ let ( camera_matrix, _, _, _, _, _) = crate :: stabilization:: FrameTransform :: get_lens_data_at_timestamp ( & comp_params, 0.0 ) ;
253+ output. push_str ( & format ! ( "GyroflowCamera.property(\" zoom\" ).setValue({});\n " , camera_matrix[ ( 0 , 0 ) ] ) ) ;
254+ if comp_params. gyro . read ( ) . file_metadata . read ( ) . lens_params . len ( ) > 1 {
255+ output. push_str ( & format ! ( "GyroflowCamera.property(\" zoom\" ).setValuesAtTimes({frame_times}, [" ) ) ;
256+ for f in 0 ..num_frames as i32 {
257+ let timestamp = crate :: timestamp_at_frame ( f, fps) ;
258+ let ( camera_matrix, _, _, _, _, _) = crate :: stabilization:: FrameTransform :: get_lens_data_at_timestamp ( & comp_params, timestamp) ;
259+ output. push_str ( & format ! ( "{},\n " , camera_matrix[ ( 0 , 0 ) ] ) ) ;
260+ }
261+ output = output. trim_end_matches ( ",\n " ) . to_string ( ) ;
262+ output. push_str ( "]);\n " ) ;
263+ }
264+ }
265+
266+ if format == Format :: Csv || format == Format :: Jsx {
267+ output
268+ } else if format == Format :: Usd {
269+ let aspect = params. video_size . 0 as f64 / params. video_size . 1 as f64 ;
270+ let width_mm = 35.0 ;
271+ let height_mm = width_mm / aspect;
272+
273+ comp_params. calculate_camera_fovs ( ) ;
274+
275+ output. push_str ( "\n }" ) ;
276+ let fov = comp_params. camera_diagonal_fovs . first ( ) . unwrap ( ) ;
277+ let diagonal_mm = ( width_mm. powi ( 2 ) + height_mm. powi ( 2 ) ) . sqrt ( ) ;
278+ let focal_length_mm = diagonal_mm / ( 2.0 * ( fov. to_radians ( ) / 2.0 ) . tan ( ) ) / 100.0 ;
279+
280+ let focal_lengths = {
281+ let mut fls = String :: new ( ) ;
282+ if comp_params. camera_diagonal_fovs . len ( ) > 1 {
283+ fls. push_str ( "float focalLength.timeSamples = {\n " ) ;
284+ for ( frame, fov) in comp_params. camera_diagonal_fovs . iter ( ) . enumerate ( ) {
285+ let focal_length_mm = diagonal_mm / ( 2.0 * ( fov. to_radians ( ) / 2.0 ) . tan ( ) ) / 100.0 ;
286+ fls. push_str ( & format ! ( " {}: {focal_length_mm},\n " , frame + 1 ) ) ;
287+ }
288+ fls. push_str ( "}" ) ;
289+ }
290+ fls
291+ } ;
292+
293+ output. push_str ( & format ! ( r#"
294+ uniform token[] xformOpOrder = ["xformOp:transform"]
295+
296+ def Camera "GyroflowCamera"
297+ {{
298+ float2 clippingRange = (0.1, 100)
299+ float focalLength = {focal_length_mm}
300+ {focal_lengths}
301+ float horizontalAperture = {}
302+ float horizontalApertureOffset = 0
303+ token projection = "perspective"
304+ float verticalAperture = {}
305+ float verticalApertureOffset = 0
306+ }}
307+ }}
308+ }}"# , width_mm / 100.0 , height_mm / 100.0 ) ) ;
169309
170- if is_csv {
171- csv
310+ output
172311 } else {
173312 serde_json:: to_string ( & json) . unwrap ( )
174313 }
0 commit comments