Adding Stabilization Algorithms
Introduction
The stabilization (smoothing) algorithms dictate how the interpolated orientations for each frame will be smoothed out and readjusted. Generally, each algorithm can be understood as a filter that takes a vector of quaternion-timestamp value pairs and delivers a vector of corrected orientations whose correction can be tuned by certain given parameters. The steps to implement a new smoothing algorithm are:
- Define the new smoothing algorithm.
- Define the smoothing parameters for the algorithm.
Next, we will showcase how to implement any algorithm following the previous steps.
Define the Stabilization Algorithm
The RidgeRun Video Stabilization Library is extensible and can adopt different smoothing algorithms. Apart from the usual constructor method, the IStabilizer interface describes how to implement new algorithms with the following methods:
- The Apply method that implements the core of the algorithm.
- The Reset method that updates the smoothing algorithm parameters.
Additionally, the algorithm can be included in the static builder method of the interface.
Add the Constructor Method
This method should take a shared pointer to the corresponding smoothing parameters instance and return it. Since this parameter is also derived, casting it to validate the type dynamically is necessary.
ExampleSmoothing::ExampleSmoothing(const std::shared_ptr<ExampleParams> params) {
std::shared_ptr<ExampleParams> casted =
std::dynamic_pointer_cast<ExampleParams>(params);
if (casted == nullptr) {
throw RuntimeError{RuntimeError::IncompatibleParameters,
"The runtime settings are incompatible. Use "
"ExampleParams"};
} else {
params_ = casted;
}
}
Extend Static Builder
To enable the ISmoothing interface to instantiate custom algorithms, it is required to:
- Extend the SmoothingAlgorithms enumerator class.
- Add the corresponding case to the switch in the builder method.
Extend StabilizerAlgorithms enum
enum class SmoothingAlgorithms {
kSphericalExponential = 0,
kFixedHorizon,
/* add new algorithm */
ExampleSmoothing,
};
Modify Builder Method
std::shared_ptr<IStabilizer> IStabilizer::Build(
const SmoothingAlgorithms algorithm,
const std::shared_ptr<SmoothingParams> params) {
switch (algorithm) {
case StabilizerAlgorithms::kSphericalExponential:
return std::make_shared<SphericalExponential>(params);
case StabilizerAlgorithms::kFixedHorizon:
return std::make_shared<FixedHorizon>(params);
break;
case StabilizerAlgorithms::ExampleSmoothing:
return std::make_shared<ExampleSmoothing>(params);
break;
default:
return nullptr;
}
}
Define the Apply Method
This method must receive an input vector of quaternion-timestamp value pairs as the first parameter. The quaternions must be in double format, and the timestamps need to be 64-bit unsigned integers in microsecond units. The second parameter corresponds to the frame rate of the video stream to be corrected, and it is generally required by smoothing algorithms. This second parameter must be at least a length of 3, each corresponding to the previous sample (index 0), current sample (index 1), and future samples (index 2).
RuntimeError ExampleSmoothing::Apply(
std::vector<Quaternion<double>>& correction,
const std::vector<std::pair<Quaternion<double>, uint64_t>>& interpolated,
const double rate) {
RuntimeError ret{};
/* Algorithm here */
return ret;
}
Define the Reset Method
This method only needs to specify how to dynamically cast the parameters shared pointer into the smoothing algorithm class.
RuntimeError ExampleSmoothing::Reset(
const std::shared_ptr<SmoothingParams> params) {
RuntimeError ret{};
std::shared_ptr<ExampleParams> casted =
std::dynamic_pointer_cast<ExampleParams>(params);
if (casted == nullptr) {
throw RuntimeError{RuntimeError::IncompatibleParameters,
"The runtime settings are incompatible. Use "
"ExampleParams"};
} else {
params_ = casted;
return ret;
}
}
Define the Stabilization Parameters
The smoothing parameters describe what will be the initial orientation that the algorithm should stabilize towards and the smoothing degree desired. These parameters are extensible given the case an algorithm requires additional parameters.
struct ExampleParams : public SmoothingParams {
/**
* additional parameter.
*/
double additional_ = 0;
ExampleParams(double smoothing, Quaternion<double> initial_orientation,
double additional)
: SmoothingParams{smoothing, initial_orientation},
additional_{additional} {}
/**
* @brief Destroy the ExampleParams instance.
*/
virtual ~ExampleParams() = default;
};