cbbair01 Posted November 21 Posted November 21 Hello, I am using Unity 2019.4 VR to track eye positions, blinks, etc. to be written to a csv file The issue I am having is that i cannot access the pupil diameter data with the code I have. I checked the validity of the Pupil value and got this error. Left Pupil Valid: False, Right Pupil Valid: False UnityEngine.Debug:Log (object) GazeRecorder:Update () (at Assets/Everything DigitalTwin/All_digitslTwin/script/GazeRecorder.cs:80) I saw many people say this wasn't possible with SRanipal so I wanted to check on here.I am using SRanipal version 1.3.6.12, with a Vive Focus 3 to calibrate it. I will attach my code to see what I am calling GazeRecorder.cs using System.Collections.Generic; using UnityEngine; using System.IO; using ViveSR.anipal.Eye; public class GazeRecorder : MonoBehaviour { public string fileName; private string currentObjectName = ""; private float gazeStartTime = 0f; private Dictionary<string, float> gazeDurations = new Dictionary<string, float>(); public LayerMask gazeLayerMask; private VerboseData eyeVerboseData; // Holds detailed eye-tracking data private Vector3 previousGazeDirection = Vector3.zero; // For gaze velocity calculation private float lastBlinkTime = 0f; // For blink rate calculation private int blinkCount = 0; void Start() { // Ensure SRanipal is initialized if (!SRanipal_Eye_Framework.Instance.EnableEye) { Debug.LogError("SRanipal Eye Module is not enabled!"); return; } Debug.Log("SRanipal Eye Framework initialized successfully."); // Set the fileName to the Downloads folder string downloadsPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile), "Downloads"); fileName = Path.Combine(downloadsPath, "GazeData.csv"); Debug.Log($"Gaze data will be stored in: {fileName}"); // Create the file and write the header using (StreamWriter sw = new StreamWriter(fileName, false)) { sw.WriteLine("ObjectName,StartTime,EndTime,Duration,PupilSize,BlinkRate,GazeVelocity,SaccadeVelocity"); } // Retrieve layer indices int handsLayer = LayerMask.NameToLayer("Hands"); int teleportableLayer = LayerMask.NameToLayer("Teleportable"); int noEyeLayer = LayerMask.NameToLayer("NoEye"); int excludedLayers = (1 << handsLayer) | (1 << teleportableLayer) | (1 << noEyeLayer); gazeLayerMask = ~excludedLayers; // Ensure SRanipal is initialized if (!SRanipal_Eye_Framework.Instance.EnableEye) { Debug.LogError("SRanipal Eye Module is not enabled!"); } } void Update() { RaycastHit hit; Ray ray = new Ray(Camera.main.transform.position, Camera.main.transform.forward); // Get eye tracking data if (SRanipal_Eye.GetVerboseData(out eyeVerboseData)) { // Pupil size (validate and calculate) float leftPupilDiameter = eyeVerboseData.left.GetValidity(SingleEyeDataValidity.SINGLE_EYE_DATA_PUPIL_DIAMETER_VALIDITY) ? eyeVerboseData.left.pupil_diameter_mm : 0f; float rightPupilDiameter = eyeVerboseData.right.GetValidity(SingleEyeDataValidity.SINGLE_EYE_DATA_PUPIL_DIAMETER_VALIDITY) ? eyeVerboseData.right.pupil_diameter_mm : 0f; float pupilSize = (leftPupilDiameter > 0 && rightPupilDiameter > 0) ? (leftPupilDiameter + rightPupilDiameter) / 2.0f : -1f; bool isLeftPupilValid = eyeVerboseData.left.GetValidity(SingleEyeDataValidity.SINGLE_EYE_DATA_PUPIL_DIAMETER_VALIDITY); bool isRightPupilValid = eyeVerboseData.right.GetValidity(SingleEyeDataValidity.SINGLE_EYE_DATA_PUPIL_DIAMETER_VALIDITY); Debug.Log($"Left Pupil Valid: {eyeVerboseData.left.GetValidity(SingleEyeDataValidity.SINGLE_EYE_DATA_PUPIL_DIAMETER_VALIDITY)}, " + $"Right Pupil Valid: {eyeVerboseData.right.GetValidity(SingleEyeDataValidity.SINGLE_EYE_DATA_PUPIL_DIAMETER_VALIDITY)}"); // Blink detection bool isBlinking = eyeVerboseData.left.eye_openness < 0.1f && eyeVerboseData.right.eye_openness < 0.1f; if (isBlinking && Time.time - lastBlinkTime > 0.2f) { blinkCount++; lastBlinkTime = Time.time; } float blinkRate = blinkCount / Mathf.Max(Time.time, 1f); // Avoid division by zero // Gaze velocity Vector3 currentGazeDirection = eyeVerboseData.combined.eye_data.gaze_direction_normalized; float gazeVelocity = (currentGazeDirection - previousGazeDirection).magnitude / Mathf.Max(Time.deltaTime, 0.0001f); previousGazeDirection = currentGazeDirection; // Saccade velocity float saccadeVelocity = gazeVelocity > 10f ? gazeVelocity : 0f; // Threshold for saccades // Cast the ray and ignore objects in excluded layers if (Physics.Raycast(ray, out hit, Mathf.Infinity, gazeLayerMask)) { string hitObjectName = hit.collider.gameObject.name; if (hitObjectName != currentObjectName) { if (currentObjectName != "") { float gazeEndTime = Time.time; float gazeDuration = gazeEndTime - gazeStartTime; // Log gaze data LogGazeData(currentObjectName, gazeStartTime, gazeEndTime, gazeDuration, pupilSize, blinkRate, gazeVelocity, saccadeVelocity); } currentObjectName = hitObjectName; gazeStartTime = Time.time; Debug.Log($"Started tracking: {currentObjectName}"); } } else { if (currentObjectName != "") { float gazeEndTime = Time.time; float gazeDuration = gazeEndTime - gazeStartTime; LogGazeData(currentObjectName, gazeStartTime, gazeEndTime, gazeDuration, pupilSize, blinkRate, gazeVelocity, saccadeVelocity); Debug.Log($"Stopped tracking: {currentObjectName}"); currentObjectName = ""; } } } else { Debug.LogWarning("Failed to retrieve eye data."); } } void LogGazeData(string objectName, float startTime, float endTime, float duration, float pupilSize, float blinkRate, float gazeVelocity, float saccadeVelocity) { try { using (StreamWriter sw = new StreamWriter(fileName, true)) { sw.WriteLine($"{objectName},{startTime},{endTime},{duration},{pupilSize},{blinkRate},{gazeVelocity},{saccadeVelocity}"); } } catch (IOException ex) { Debug.LogError($"Failed to write to file: {ex.Message}"); } } }
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now