xref: /aosp_15_r20/frameworks/av/media/libheadtracking/StillnessDetector.h (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1*ec779b8eSAndroid Build Coastguard Worker /*
2*ec779b8eSAndroid Build Coastguard Worker  * Copyright (C) 2021 The Android Open Source Project
3*ec779b8eSAndroid Build Coastguard Worker  *
4*ec779b8eSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*ec779b8eSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*ec779b8eSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*ec779b8eSAndroid Build Coastguard Worker  *
8*ec779b8eSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*ec779b8eSAndroid Build Coastguard Worker  *
10*ec779b8eSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*ec779b8eSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*ec779b8eSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*ec779b8eSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*ec779b8eSAndroid Build Coastguard Worker  * limitations under the License.
15*ec779b8eSAndroid Build Coastguard Worker  */
16*ec779b8eSAndroid Build Coastguard Worker #pragma once
17*ec779b8eSAndroid Build Coastguard Worker 
18*ec779b8eSAndroid Build Coastguard Worker #include <deque>
19*ec779b8eSAndroid Build Coastguard Worker 
20*ec779b8eSAndroid Build Coastguard Worker #include <media/Pose.h>
21*ec779b8eSAndroid Build Coastguard Worker 
22*ec779b8eSAndroid Build Coastguard Worker namespace android {
23*ec779b8eSAndroid Build Coastguard Worker namespace media {
24*ec779b8eSAndroid Build Coastguard Worker 
25*ec779b8eSAndroid Build Coastguard Worker /**
26*ec779b8eSAndroid Build Coastguard Worker  * Given a stream of poses, determines if the pose is stable ("still").
27*ec779b8eSAndroid Build Coastguard Worker  * Stillness is defined as all poses in the recent history ("window") being near the most recent
28*ec779b8eSAndroid Build Coastguard Worker  * sample.
29*ec779b8eSAndroid Build Coastguard Worker  *
30*ec779b8eSAndroid Build Coastguard Worker  * Typical usage:
31*ec779b8eSAndroid Build Coastguard Worker  *
32*ec779b8eSAndroid Build Coastguard Worker  * StillnessDetector detector(StilnessDetector::Options{...});
33*ec779b8eSAndroid Build Coastguard Worker  *
34*ec779b8eSAndroid Build Coastguard Worker  * while (...) {
35*ec779b8eSAndroid Build Coastguard Worker  *    detector.setInput(timestamp, pose);
36*ec779b8eSAndroid Build Coastguard Worker  *    bool still = detector.calculate(timestamp);
37*ec779b8eSAndroid Build Coastguard Worker  * }
38*ec779b8eSAndroid Build Coastguard Worker  *
39*ec779b8eSAndroid Build Coastguard Worker  * The detection is not considered reliable until a sufficient number of samples has been provided
40*ec779b8eSAndroid Build Coastguard Worker  * for an initial fill-up of the window. During that time, the detector will return whatever default
41*ec779b8eSAndroid Build Coastguard Worker  * value has been configured.
42*ec779b8eSAndroid Build Coastguard Worker  * The reset() method can be used to empty the window again and get back to this initial state.
43*ec779b8eSAndroid Build Coastguard Worker  * In the special case of the window size being 0, the state will always be considered "still".
44*ec779b8eSAndroid Build Coastguard Worker  */
45*ec779b8eSAndroid Build Coastguard Worker class StillnessDetector {
46*ec779b8eSAndroid Build Coastguard Worker   public:
47*ec779b8eSAndroid Build Coastguard Worker     /**
48*ec779b8eSAndroid Build Coastguard Worker      * Configuration options for the detector.
49*ec779b8eSAndroid Build Coastguard Worker      */
50*ec779b8eSAndroid Build Coastguard Worker     struct Options {
51*ec779b8eSAndroid Build Coastguard Worker         /**
52*ec779b8eSAndroid Build Coastguard Worker          * During the initial fill of the window, should we consider the state still?
53*ec779b8eSAndroid Build Coastguard Worker          */
54*ec779b8eSAndroid Build Coastguard Worker          bool defaultValue;
55*ec779b8eSAndroid Build Coastguard Worker         /**
56*ec779b8eSAndroid Build Coastguard Worker          * How long is the window, in ticks. The special value of 0 indicates that the stream is
57*ec779b8eSAndroid Build Coastguard Worker          * always considered still.
58*ec779b8eSAndroid Build Coastguard Worker          */
59*ec779b8eSAndroid Build Coastguard Worker         int64_t windowDuration;
60*ec779b8eSAndroid Build Coastguard Worker         /**
61*ec779b8eSAndroid Build Coastguard Worker          * How much of a translational deviation from the target (in meters) is considered motion.
62*ec779b8eSAndroid Build Coastguard Worker          * This is an approximate quantity - the actual threshold might be a little different as we
63*ec779b8eSAndroid Build Coastguard Worker          * trade-off accuracy with computational efficiency.
64*ec779b8eSAndroid Build Coastguard Worker          */
65*ec779b8eSAndroid Build Coastguard Worker         float translationalThreshold;
66*ec779b8eSAndroid Build Coastguard Worker         /**
67*ec779b8eSAndroid Build Coastguard Worker          * How much of a rotational deviation from the target (in radians) is considered motion.
68*ec779b8eSAndroid Build Coastguard Worker          * This is an approximate quantity - the actual threshold might be a little different as we
69*ec779b8eSAndroid Build Coastguard Worker          * trade-off accuracy with computational efficiency.
70*ec779b8eSAndroid Build Coastguard Worker          */
71*ec779b8eSAndroid Build Coastguard Worker         float rotationalThreshold;
72*ec779b8eSAndroid Build Coastguard Worker     };
73*ec779b8eSAndroid Build Coastguard Worker 
74*ec779b8eSAndroid Build Coastguard Worker     /** Ctor. */
75*ec779b8eSAndroid Build Coastguard Worker     explicit StillnessDetector(const Options& options);
76*ec779b8eSAndroid Build Coastguard Worker 
77*ec779b8eSAndroid Build Coastguard Worker     /** Clear the window. */
78*ec779b8eSAndroid Build Coastguard Worker     void reset();
79*ec779b8eSAndroid Build Coastguard Worker     /** Push a new sample. */
80*ec779b8eSAndroid Build Coastguard Worker     void setInput(int64_t timestamp, const Pose3f& input);
81*ec779b8eSAndroid Build Coastguard Worker     /** Calculate whether the stream is still at the given timestamp. */
82*ec779b8eSAndroid Build Coastguard Worker     bool calculate(int64_t timestamp);
83*ec779b8eSAndroid Build Coastguard Worker     /** Return the stillness state from the previous call to calculate() */
84*ec779b8eSAndroid Build Coastguard Worker     bool getPreviousState() const;
85*ec779b8eSAndroid Build Coastguard Worker   private:
86*ec779b8eSAndroid Build Coastguard Worker     struct TimestampedPose {
87*ec779b8eSAndroid Build Coastguard Worker         int64_t timestamp;
88*ec779b8eSAndroid Build Coastguard Worker         Pose3f pose;
89*ec779b8eSAndroid Build Coastguard Worker     };
90*ec779b8eSAndroid Build Coastguard Worker 
91*ec779b8eSAndroid Build Coastguard Worker     const Options mOptions;
92*ec779b8eSAndroid Build Coastguard Worker     // Precalculated cos(mOptions.rotationalThreshold / 2)
93*ec779b8eSAndroid Build Coastguard Worker     const float mCosHalfRotationalThreshold;
94*ec779b8eSAndroid Build Coastguard Worker     std::deque<TimestampedPose> mFifo;
95*ec779b8eSAndroid Build Coastguard Worker     bool mWindowFull = false;
96*ec779b8eSAndroid Build Coastguard Worker     bool mCurrentState = true;
97*ec779b8eSAndroid Build Coastguard Worker     bool mPreviousState = true;
98*ec779b8eSAndroid Build Coastguard Worker     // As soon as motion is detected, this will be set for the time of detection + window duration,
99*ec779b8eSAndroid Build Coastguard Worker     // and during this time we will always consider outselves in motion without checking. This is
100*ec779b8eSAndroid Build Coastguard Worker     // used for hyteresis purposes, since because of the approximate method we use for determining
101*ec779b8eSAndroid Build Coastguard Worker     // stillness, we may toggle back and forth at a rate faster than the window side.
102*ec779b8eSAndroid Build Coastguard Worker     std::optional<int64_t> mSuppressionDeadline;
103*ec779b8eSAndroid Build Coastguard Worker 
104*ec779b8eSAndroid Build Coastguard Worker     bool areNear(const Pose3f& pose1, const Pose3f& pose2) const;
105*ec779b8eSAndroid Build Coastguard Worker     void discardOld(int64_t timestamp);
106*ec779b8eSAndroid Build Coastguard Worker };
107*ec779b8eSAndroid Build Coastguard Worker 
108*ec779b8eSAndroid Build Coastguard Worker }  // namespace media
109*ec779b8eSAndroid Build Coastguard Worker }  // namespace android
110