1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.experimentalcar; 18 19 import android.annotation.FloatRange; 20 import android.annotation.NonNull; 21 import android.car.occupantawareness.GazeDetection; 22 import android.util.Log; 23 24 /** {@link GazeAttentionProcessor} estimates driver attention based on a gaze history. */ 25 class GazeAttentionProcessor { 26 private static final String TAG = "Car.OAS.GazeAttentionProcessor"; 27 private static final int NOT_SET = -1; 28 29 private final Configuration mConfig; 30 31 /** Current attention value. */ 32 @FloatRange(from = 0.0f, to = 1.0f) 33 private float mAttention; 34 35 /** Timestamp of last frame received, in milliseconds since boot. */ 36 private long mLastTimestamp = NOT_SET; 37 GazeAttentionProcessor(Configuration configuration)38 GazeAttentionProcessor(Configuration configuration) { 39 mConfig = configuration; 40 mAttention = mConfig.initialValue; 41 } 42 43 /** Gets the current attention awareness value. */ 44 @FloatRange(from = 0.0f, to = 1.0f) getAttention()45 public float getAttention() { 46 return mAttention; 47 } 48 49 /** 50 * Updates the current attention with a new gaze detection. 51 * 52 * @param detection New {@link android.car.occupantawareness.GazeDetection} data. 53 * @param timestamp Timestamp that the detection was detected, in milliseconds since boot. 54 * @return Attention value [0, 1]. 55 */ 56 @FloatRange(from = 0.0f, to = 1.0f) updateAttention(@onNull GazeDetection detection, long timestamp)57 public float updateAttention(@NonNull GazeDetection detection, long timestamp) { 58 // When the first frame of data is received, no duration can be computed. Just capture the 59 // timestamp for the next pass and return the initial buffer value. 60 if (mLastTimestamp == NOT_SET) { 61 logd("First pass, returning 'initialValue=" + mConfig.initialValue); 62 mLastTimestamp = timestamp; 63 return mConfig.initialValue; 64 } 65 66 float startingAttention = mAttention; 67 68 // Compute the time since the last detection, in seconds. 69 float dtSeconds = (float) (timestamp - mLastTimestamp) / 1000.0f; 70 71 if (isOnRoadGaze(detection.gazeTarget)) { 72 logd("Handling on-road gaze"); 73 mAttention += dtSeconds * mConfig.growthRate; 74 mAttention = Math.min(mAttention, 1.0f); // Cap value to max of 1. 75 } else { 76 logd("Handling off-road gaze"); 77 mAttention -= dtSeconds * mConfig.decayRate; 78 mAttention = Math.max(mAttention, 0.0f); // Cap value to min of 0. 79 } 80 81 // Save current timestamp for next pass. 82 mLastTimestamp = timestamp; 83 84 logd(String.format("updateAttention(): Time=%1.2f. Attention [%f]->[%f]. ", 85 dtSeconds, startingAttention, mAttention)); 86 87 return mAttention; 88 } 89 90 /** Gets whether the gaze target is on-road. */ isOnRoadGaze(@azeDetection.VehicleRegion int gazeTarget)91 private boolean isOnRoadGaze(@GazeDetection.VehicleRegion int gazeTarget) { 92 switch (gazeTarget) { 93 case GazeDetection.VEHICLE_REGION_FORWARD_ROADWAY: 94 case GazeDetection.VEHICLE_REGION_LEFT_ROADWAY: 95 case GazeDetection.VEHICLE_REGION_RIGHT_ROADWAY: 96 return true; 97 default: 98 return false; 99 } 100 } 101 logd(String message)102 private static void logd(String message) { 103 if (Log.isLoggable(TAG, Log.DEBUG)) { 104 Log.d(TAG, message); 105 } 106 } 107 108 /** Configuration settings for {@link GazeAttentionProcessor}. */ 109 public static class Configuration { 110 /** Initial value for the attention buffer, [0, 1]. */ 111 @FloatRange(from = 0.0f, to = 1.0f) 112 public final float initialValue; 113 114 /** 115 * Rate at which the attention decays when the driver is looking off-road (per second of 116 * off-road looks) 117 */ 118 public final float decayRate; 119 120 /** 121 * Rate at which the attention grows when the driver is looking on-road (per second of 122 * on-road looks). 123 */ 124 public final float growthRate; 125 126 /** 127 * Creates an instance of {@link Configuration}. 128 * 129 * @param initialValue the initial value that the attention buffer starts at. 130 * @param decayRate the rate at which the attention buffer decreases when the driver is 131 * looking off-road. 132 * @param growthRate the rate at which the attention buffer increases when the driver is 133 * looking on-road. 134 */ Configuration(float initialValue, float decayRate, float growthRate)135 Configuration(float initialValue, float decayRate, float growthRate) { 136 this.initialValue = initialValue; 137 this.decayRate = decayRate; 138 this.growthRate = growthRate; 139 } 140 141 @Override toString()142 public String toString() { 143 return String.format( 144 "Configuration{initialValue=%s, decayRate=%s, growthRate=%s}", 145 initialValue, decayRate, growthRate); 146 } 147 } 148 } 149