aboutsummaryrefslogtreecommitdiff
path: root/src/argaze/PupilFeatures.py
blob: 3ba95762f7b47c32666a3a65ac7bbc38cd3a3c0d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#!/usr/bin/env python

""" """

__author__ = "Théo de la Hogue"
__credits__ = []
__copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)"
__license__ = "BSD"

from typing import TypeVar
from dataclasses import dataclass, field
import json

from argaze import DataStructures

@dataclass(frozen=True)
class PupilDiameter():
    """Define pupil diameter as ..."""

    value: float = field(default=0.)
    """Pupil diameter value."""

    @property
    def valid(self) -> bool:
        """Is the value not 0"""

        return self.value != 0.

    def __repr__(self):
        """String representation"""

        return json.dumps(self, ensure_ascii = False, default=vars)

class UnvalidPupilDiameter(PupilDiameter):
    """Unvalid pupil diameter."""

    def __init__(self, message=None):

        self.message = message

        super().__init__(0.)

TimeStampedPupilDiametersType = TypeVar('TimeStampedPupilDiameters', bound="TimeStampedPupilDiameters")
# Type definition for type annotation convenience

class TimeStampedPupilDiameters(DataStructures.TimeStampedBuffer):
    """Define timestamped buffer to store pupil diameters."""

    def __setitem__(self, key, value: PupilDiameter|dict):
        """Force PupilDiameter storage."""

        # Convert dict into PupilDiameter
        if type(value) == dict:

            assert(set(['value']).issubset(value.keys()))

            if 'message' in value.keys():

                value = UnvalidPupilDiameter(value['message'])

            else:

                value = PupilDiameter(value['value'])

        assert(type(value) == PupilDiameter or type(value) == UnvalidPupilDiameter)

        super().__setitem__(key, value)

    @classmethod
    def from_json(self, json_filepath: str) -> TimeStampedPupilDiametersType:
        """Create a TimeStampedPupilDiametersType from .json file."""

        with open(json_filepath, encoding='utf-8') as ts_buffer_file:

            json_buffer = json.load(ts_buffer_file)

            return TimeStampedPupilDiameters({ast.literal_eval(ts_str): json_buffer[ts_str] for ts_str in json_buffer})

TimeStampedBufferType = TypeVar('TimeStampedBuffer', bound="TimeStampedBuffer")
# Type definition for type annotation convenience

class PupilDiameterAnalyzer():
    """Abstract class to define what should provide a pupil diameter analyser."""

    def analyze(self, ts, pupil_diameter) -> float:
        """Analyze pupil diameter from successive timestamped pupil diameters."""

        raise NotImplementedError('analyze() method not implemented')

    def browse(self, ts_pupil_diameters: TimeStampedPupilDiameters) -> TimeStampedBufferType:
        """Analyze by browsing timestamped pupil diameters."""

        assert(type(ts_pupil_diameters) == TimeStampedPupilDiameters)

        ts_analyzis = DataStructures.TimeStampedBuffer()

        # Iterate on pupil diameters
        for ts, pupil_diameter in ts_pupil_diameters.items():

            analysis = self.analyze(ts, pupil_diameter)

            if analysis is not None:

                ts_analyzis[ts] = analysis

        return ts_analyzis