Table of Contents
Signal Functions
This page contains a list of all functions that result in new signals.
Data_Exists
Data_Exists(a) returns 1 if the signal exists.
Example, compute “something” only if the event label RON exists.
Conditional_Statement /ITERATION_PARAMETER_NAME=TEST_RON /EXPRESSION=Data_Exists(EVENT_LABEL::ORIGINAL::RON) ! /AS_INTEGER=TRUE ; ! commands to execute Conditional_Statement_End /ITERATION_PARAMETER_NAME=TEST_RON ;
Frame Count
Frame_Count(a) – return the number of frames in the signal
Add
add(a,b) – add two expressions
Visual3D's pipeline parameters assume that a + is a delimiter between entries.
If the user wants to use an expression containing a + in a command that allows multiple signals, Visual3D will interpret this plus sign incorrectly.
The workaround is to use a mathematical function add() to express the addition.
Example. Adding two signals could be accomplished equally well by the following two commands.
Evaluate_Expression /Expression= LANDMARK::ORIGINAL::RIGHT_HIP + TARGET::ORIGINAL::LHIP /Result_Name=TEST1 ! /Result_Type=DERIVED ! /Result_Folder=PROCESSED ; Evaluate_Expression /Expression= add(LANDMARK::ORIGINAL::RIGHT_HIP , TARGET::ORIGINAL::LHIP) /Result_Name=TEST2 ! /Result_Type=DERIVED ! /Result_Folder=PROCESSED ;
Sort
sort(signal, direction (default = 1), component (default = 1))
sort(target::original::rft1) sort descending by all components independently sort(target::original::rft1, 1.0) sort ascending by all components independently sort(target::original::rft1, -1.0) sort descending by all components independently sort(target::original::rft1, 1.0, Component) sort ascending, by component, other frames sorted by component sort sort(target::original::rft1, -1.0, Component) sort descending, by component, other frames sorted by component sort
Transpose
Transpose(signal) // Swaps frames and components of a signal
This transposes a signal. Useful when you have an array of signals, and you create a metric for each and append as components. You can turn the components into a list of metrics by frames (like we are used to using in Visual3D). Example of it's use, creates a list of metrics of the mean of the z component of the target rft1 from RTO to RHS. (note, the spline didn't have to be done here, but it is here just to show it's use).
Evaluate_Expression /EXPRESSION=transpose( mean( append_as_components ( spline ( snip (target::original::rft1::z, event_label::original::rto, event_label::original::rhs), 101) ))) ! /SIGNAL_TYPES= ! /SIGNAL_FOLDER=ORIGINAL ! /SIGNAL_NAMES= ! /RESULT_TYPES=DERIVED ! /RESULT_FOLDERS=PROCESSED /RESULT_NAME=test ! /APPLY_AS_SUFFIX_TO_SIGNAL_NAME=FALSE ;
First_Derivative
Second_Derivative
Interpolate
interpolate(signal, gap, fit, order)
For example, interpolate a signal across a maximum gap of 10 frames, fitting 3 frames before and after the gap using a third order polynomial
INTERPOLATE(TARGET::PROCESSED::RSK3,10,3,3)
Resolve Discontinuity
Resolve_Discontinuity(signal, expected magnitude of the discontinuity, event_label, event_instance)
Joint angles often exhibit discontinuities because of limitations of resolving a joint angle as an Euler Sequence. This command resolves the discontinuity by add/subtracting an offset at the point the discontinuity occurs.
For example,
Resolve_Discontinuity(link_model_based::original::rshoulder_angle, range, event_label::original::rhs, instance) Resolve_Discontinuity(link_model_based::original::rshoulder_angle, range, event_label::original::rhs) %%//%% starts from first instance Resolve_Discontinuity(link_model_based::original::rshoulder_angle, range) %%//%% starts from first valid frame,
Note that the event label requires the full signal path. i.e. event_label::original::rhs not just rhs
One of the interesting consequences of this decision is that the command is actually looking for an array of “frame times” so you could actually use a number or a metric signal or an expression. Example, a figure skater spinning has a rotation angle that continues to increase; a double jump may cover 720 degrees.
Euler angles, however, can only be resolved into a range of 360 degrees because anything beyond that is actually the same physical angle as an angle within this range.
In the case of the figure skater, the axial angle may begin at 90 degrees relative to the laboratory and increase to 180 degrees at which it suddenly becomes -180 degrees and then continues up to 180 degrees at which it suddenly becomes -180 degrees.
This is a natural consequence of the way Euler angles are parsed from the Rotation Matrix.
Visual3D includes a “convenience function” for removing this discontinuity, by adding/subtracting an offset at the point the discontinuity occurs.
The typical calling syntax is:
Resolve_Discontinuity(signal, range, event_label, instance)
The range represents the range of motion of the signal. In the figure skating example above, the range is 360 degrees. The value of the signal at the specified instance of the event_label is assumed to be “in the range” Visual3D then looks for a large discontinuity in the signal and adds the range. if the instance is omitted:
Resolve_Discontinuity(signal, range, event_label)
The first instance of the event is used. if the instance and the event_label are omitted:
Resolve_Discontinuity(signal, range)
the first valid frame is used
Example: the angle of a golf club to the anterior direction of the golfer (LINK_MODEL_BASED::ORIGINAL::CLUB_ANGLE).
The has a discontinuity as the signal goes beyond the range possible for computing the Cardan Sequence.
The following expression adds 360 at the point of discontinuity
At the Event_Label TAKEAWAY the club angle is assumed to be “within the range”, which means that this value will persist in the output signal.
The command is looking for the time of the event, so it is important to use the full signal name EVENT_LABEL::ORIGINAL::TAKEAWAY
At a discontinuity 360 degrees is either added or subtracted from the value depending on the direction of the discontinuity.
Evaluate_Expression /EXPRESSION=RESOLVE_DISCONTINUITY(LINK_MODEL_BASED::ORIGINAL::CLUB_ANGLE,360,EVENT_LABEL::ORIGINAL::TAKEAWAY) /RESULT_NAME=CLUB_ANGLE /RESULT_TYPE=LINK_MODEL_BASED /RESULT_FOLDER=PROCESSED ;
Indefinite Integral
Indefinite_Integral(signal, initial_value, start_event, end_event) - where initial_value, start_event and end_event are optional.
See Pipeline Command Indefinite_Integral
Example; Indefinite_Integral(target::processed::rft1) Indefinite_Integral(target::processed::rft1, vector(0.0, 0.0, 1.0)) Indefinite_Integral(target::processed::rft1, vector(0.0, 0.0, 1.0), event_label::original::rhs) Indefinite_Integral(target::processed::rft1, vector(0.0, 0.0, 1.0), event_label::original::rhs, event_label::original::rto)
Note that the event label requires the full signal path. i.e. event_label::original::rhs not just rhs
Cumulative_Sum
Cumulative_Sum(signal, initial_value, start_event, end_event) - where initial_value, start_event and end_event are optional.
Example; Cumulative_Sum(target::processed::rft1) Cumulative_Sum(target::processed::rft1, vector(0.0, 0.0, 1.0)) Cumulative_Sum(target::processed::rft1, vector(0.0, 0.0, 1.0), event_label::original::rhs) Cumulative_Sum(target::processed::rft1, vector(0.0, 0.0, 1.0), event_label::original::rhs, event_label::original::rto) Cumulative_Sum(target::processed::rft1, vector(NAN, NAN, NAN), event_label::original::rhs)
Note that the event label requires the full signal path. i.e. event_label::original::rhs not just rhs
Point Relative to 3 Points
Point_Relative_To_3Points(Point1, Vertex1, Vertex2, Vertex3)
Compute the location of Point1 relative to a coordinate system defined by Vertex1, Vertex2, and Vertex3
Consider the hypothetical situation.
There are two sets of motion trials, tagged condition1 and condition2 there are two static files (e.g. two models) in one model (assigned to condition1) the anatomical landmarks are attached. in the other model, the same anatomical landmarks did not exist. Selecting condition1 to be active, makes model1 active **Select_Active_File** /FILE_NAME=CONDITION1 ! /QUERY= ; ! determine the offsets of marker RLKNEE relative to the three markers RTH1, RTH2, and RTH3 ! and save as a pipeline parameter Set_Pipeline_Parameter_From_Expression /PARAMETER_NAME=TEST /EXPRESSION=Median(Point_Relative_To_3Points(MODEL::TARGET::RLKNEE, MODEL::TARGET::RTH2, MODEL::TARGET::RTH3, MODEL::TARGET::RTH4)) /AS_INTEGER=FALSE **;** ! making condition2 active will make model2 active. Select_Active_File /FILE_NAME=CONDITION2 ! /QUERY= ; ! now create a landmark for RLKNEE in the second model Add_Landmark /LANDMARK_NAME=RLKNEE ! /CALIBRATION_FILE= ! /USER_GENERATED=TRUE ! /USE_PERCENTAGE=FALSE ! /CALIBRATION_ONLY=FALSE /USE_TARGETS=TRUE ! /SEGMENT_NAME= /TARGET_TYPES=TARGET+TARGET+TARGET /TARGET_NAMES=RTH2+RTH3+RTH4 ! /MCS_ML=0.0 ! /MCS_AP=0.0 ! /MCS_AXIAL=0.0 /LANDMARK_LOCATION=::TEST ! /REFERENCE_LOCATION_TYPE= ! /REFERENCE_LOCATION_NAME= ! /USE_REFERENCE_LOCATION=FALSE ;
Point_Tracked_By_3Points
Point_Tracked_By_3Points(Point1, Vertex1, Vertex2, Vertex3)
This function was implemented to used DERIVED signals as tracking markers for a point relative to these tracking markers.
In other words, this is mimics a LANDMARK in which a point is tracked by three TARGETS or LANDMARKS in a model.
The difference is that a model isn't required.
Why would this be useful?
The c3d format specifies that the lowest sampling rate is the POINT rate (i.e. the marker rate)
Marker based tracking, however, often has possible sampling rates that are much higher than video rates used by markerless tracking.
In a situation like baseball batting or golf many users record marker and markerless synchronously and merge the files.
The markers are used to track the club or bat at a higher sampling rate than the video
Then the c3d format, and Visual3D's implementation, collide with this objective.
When merging the two files, Visual3D automatically downsamples the POINT rate to the ROTATION rate.
To compensate (at least somewhat), Visual3D stores a copy of the POINT data as a DERIVED signal at the original POINT rate
The data is preserved, but the model and model based items cannot refer to the DERIVED signals
The workaround is to perform some calculations only on the DERIVED signals independent of any model.
As as aside, for some applications, using only the marker data will result in faster processing and smaller cmz files.
Example:
locate a point in the model relative to 3 markers
then track this point in the movement trial
Evaluate_Expression /EXPRESSION=Point_Tracked_By_3Points(Point_Relative_To_3Points(MODEL::TARGET::FaceBot, MODEL::TARGET::Shaft4, MODEL::TARGET::Shaft5, MODEL::TARGET::Shaft6), TARGET::ORIGINAL::Shaft4, TARGET::ORIGINAL::Shaft5, TARGET::ORIGINAL::Shaft6) /RESULT_TYPES=DERIVED /RESULT_FOLDERS=CLUBFACE /RESULT_NAME=FaceBot ;
Snip
Functions for segmenting a signal into a collection of signals within frame numbers, events, or event_sequences. The result is a list (array) of signal segments (snips or traces) within each event or frame sequence.
There are three separate syntaxes that are valid for this command:
- Snip(signal, first_frame_number, last_frame_number)
Example: Snip(DERIVED::PROCESSED::SIGNAL_NAME, Frame_Number, Frame_Number)
- Snip(signal, start_event, end_event)
Example: Snip(DERIVED::PROCESSED::SIGNAL_NAME, EVENT_LABEL::ORIGINAL::START, EVENT_LABEL::ORIGINAL::END)
- Snip(signal, event_sequence)
Example: Snip(DERIVED::PROCESSED::SIGNAL_NAME, EVENT_LABEL::SEQUENCE::SEQ_NAME)
Example
Example 1 demonstrates how the Snip expression can be used to extract the last 3 frames of data from a metric signal.
!Create a test metric signal Metric_Explicit ! /RESULT_METRIC_FOLDER=PROCESSED /RESULT_METRIC_NAME=AA /METRIC_VALUE=LIST(1,2,3,4) ; !Create a new signal from the last 3 frames of this metric sigal Evaluate_Expression /EXPRESSION=SNIP(METRIC::PROCESSED::AA,FRAME_COUNT(METRIC::PROCESSED::AA)-2,FRAME_COUNT(METRIC::PROCESSED::AA)) ! /SIGNAL_TYPES= ! /SIGNAL_FOLDER=ORIGINAL ! /SIGNAL_NAMES= /RESULT_TYPES=METRIC /RESULT_FOLDERS=PROCESSED /RESULT_NAME=AB ! /APPLY_AS_SUFFIX_TO_SIGNAL_NAME=FALSE ; ! Note that in this case this is equivalent to: Evaluate_Expression /EXPRESSION=SNIP(METRIC::PROCESSED::AA,2,4) ! /SIGNAL_TYPES= ! /SIGNAL_FOLDER=ORIGINAL ! /SIGNAL_NAMES= /RESULT_TYPES=METRIC /RESULT_FOLDERS=PROCESSED /RESULT_NAME=AB_EQUIVALENT ! /APPLY_AS_SUFFIX_TO_SIGNAL_NAME=FALSE ;
Example 2 demonstrates how the Snip expression in conjunction with spline, and several other functions
The following pipeline resulted from a support email.\\ The request was to compare several different methods for defining the hip and knee angles\\ Signals were created by pairwise subtracting one signal from another signal\\ Each of the difference signals in several files were parsed into regions defined by an event sequence\\ Each region was then time normalized to 101 points\\ The RMS for each region was then computed.\\ Each file, therefore, contained a number of RMS values\\ The RMS signals from each file were then concatenated into a signal GLOBAL signal containing values from all files Select_Active_File /FILE_NAME=ALL_FILES ! /QUERY= ! /SUBJECT_TAGS=NO_SUBJECT ; ! difference signals were stored as DERIVED::PROCESSED ! Create a pipeline parameter containing a list of the signal names in this folder Set_Pipeline_Parameter_To_List_Of_Signal_Names /PARAMETER_NAME=SIGNALS /SIGNAL_TYPES=DERIVED /SIGNAL_FOLDER=PROCESSED ! /SIGNAL_MASK=* ; ! Use a For_Each command to iteratively act on each signal in the list For_Each /ITERATION_PARAMETER_NAME=INDEX ! /ITERATION_PARAMETER_COUNT_NAME= /ITEMS=::SIGNALS ; ! While it might be possible to provide a solution with a single expression, it would ! take some time to modify Visual3D ! the following the commands (one for each of x, y, z) do 3 things ! isolates the data with the event sequence selected ! resamples the data to 101 points using a cubic spline ! create a resulting signal that has a column for each event sequence in the file time normalized to 101 frames Evaluate_Expression /EXPRESSION=Append_As_Components( SPLINE( SNIP(CURRENT_SIGNAL, EVENT_LABEL::ORIGINAL::Left Foot Strike, EVENT_LABEL::ORIGINAL::Left Foot Strike),101) ) /SIGNAL_TYPES=DERIVED /SIGNAL_FOLDER=PROCESSED /SIGNAL_NAMES=::INDEX /SIGNAL_COMPONENTS=X ! /RESULT_TYPES=DERIVED /RESULT_FOLDERS=DIFF /RESULT_NAME=_DIFFX /APPLY_AS_SUFFIX_TO_SIGNAL_NAME=TRUE ; Evaluate_Expression /EXPRESSION=Append_As_Components( SPLINE( SNIP(CURRENT_SIGNAL, EVENT_LABEL::ORIGINAL::Left Foot Strike, EVENT_LABEL::ORIGINAL::Left Foot Strike),101) ) /SIGNAL_TYPES=DERIVED /SIGNAL_FOLDER=PROCESSED /SIGNAL_NAMES=::INDEX /SIGNAL_COMPONENTS=Y ! /RESULT_TYPES=DERIVED /RESULT_FOLDERS=DIFF /RESULT_NAME=_DIFFY /APPLY_AS_SUFFIX_TO_SIGNAL_NAME=TRUE ; Evaluate_Expression /EXPRESSION=Append_As_Components( SPLINE( SNIP(CURRENT_SIGNAL, EVENT_LABEL::ORIGINAL::Left Foot Strike, EVENT_LABEL::ORIGINAL::Left Foot Strike),101) ) /SIGNAL_TYPES=DERIVED /SIGNAL_FOLDER=PROCESSED /SIGNAL_NAMES=::INDEX /SIGNAL_COMPONENTS=Z ! /RESULT_TYPES=DERIVED /RESULT_FOLDERS=DIFF /RESULT_NAME=_DIFFZ /APPLY_AS_SUFFIX_TO_SIGNAL_NAME=TRUE ; ! the following command consolidates the above information ! the RMS value of each column is computed ! the signal is transposed so that each RMS value for each event sequence is on a different line ! x,y, and z values for the RMS are then stored in one signal Evaluate_Expression /EXPRESSION=VECTOR(Transpose(Metric_RMS(DERIVED::DIFF:&:&::INDEX&_DIFFX)),Transpose(Metric_RMS(DERIVED::DIFF:&:&::INDEX&_DIFFY)),Transpose(Metric_RMS(DERIVED::DIFF:&:&::INDEX&_DIFFZ))) /SIGNAL_TYPES= /SIGNAL_FOLDER= /SIGNAL_NAMES= ! /SIGNAL_COMPONENTS= /RESULT_TYPES=METRIC /RESULT_FOLDERS=RMS /RESULT_NAME=::INDEX&_RMS ! /APPLY_AS_SUFFIX_TO_SIGNAL_NAME=FALSE ; End_For_Each /ITERATION_PARAMETER_NAME=INDEX ; ! Create a pipeline containing the names of the metric signals just created Set_Pipeline_Parameter_To_List_Of_Signal_Names /PARAMETER_NAME=SIGNALS /SIGNAL_TYPES=METRIC /SIGNAL_FOLDER=RMS ! /SIGNAL_MASK=* ; ! Concatenate the RMS metric signals into one GLOBAL signal containing all RSM values ! across all files ! Make the GLOBAL workspace active and you should see the results. For_Each /ITERATION_PARAMETER_NAME=INDEX2 ! /ITERATION_PARAMETER_COUNT_NAME= /ITEMS=::SIGNALS ; Evaluate_Expression /EXPRESSION=Append_As_Frames(GLOBAL::METRIC::RMS:&:&::INDEX2&,CURRENT_SIGNAL) /SIGNAL_TYPES=METRIC /SIGNAL_FOLDER=RMS /SIGNAL_NAMES=::INDEX2 ! /SIGNAL_COMPONENTS= /RESULT_TYPES=METRIC /RESULT_FOLDERS=RMS /RESULT_NAME=GLOBAL:&:&::INDEX2 ! /APPLY_AS_SUFFIX_TO_SIGNAL_NAME=FALSE ; End_For_Each /ITERATION_PARAMETER_NAME=INDEX2 ;
Spline
Spline(signal_list, number_of_points)
This does a spline of a signal (array of snips) to normalize each signal to the same number of points.
Append As...
Append_As_Components(signal_list)
This takes the signal list, and appends each signal into one signal as new components.
Append_As_Frames(signal_list)
This takes a signal list, and appends each signal to the end of the previous signal to create the result with each signal appended to each other as new frames
Append_As_Frames Example:
Open any cmz file with more than 1 file Select_Active_File /FILE_NAME=ALL_FILES ! /QUERY= **;** ! Create a metric signal with 3 components and 1 frame Metric_Explicit /RESULT_METRIC_FOLDER=TEST /RESULT_METRIC_NAME=SCOTT /METRIC_VALUE=1+2+3 ; !The following command will concatenate the metric signal from each file !into a GLOBAL metric signal. Evaluate_Expression /EXPRESSION=Append_As_Frames(GLOBAL::METRIC::TEST::SCOTTA,CURRENT_SIGNAL) /SIGNAL_TYPES=METRIC /SIGNAL_FOLDER=TEST /SIGNAL_NAMES=SCOTT /RESULT_TYPES=METRIC /RESULT_FOLDERS=TEST /RESULT_NAME=GLOBAL::SCOTTA ! /APPLY_AS_SUFFIX_TO_SIGNAL_NAME=FALSE ;