-
Notifications
You must be signed in to change notification settings - Fork 1
/
model.py
138 lines (127 loc) · 4.51 KB
/
model.py
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import pickle
import os
from copy import copy
from pathlib import Path
class BrightnessModel():
def __init__(self, minB, maxB):
"""
:param minB: float between 0 and 100, minimum brightness
:param maxB: float between 0 and 100, maximum brightness
"""
self.minB = minB
self.maxB = maxB
def load(self, file):
return os.path.exists(file)
def save(self, file):
Path(file).parent.mkdir(parents=True, exist_ok=True)
def saveIfNecessary(self, file):
raise NotImplementedError
def addObservation(self, screen, backlight):
"""
:param screen: float between 0 and 1, screen brightness
:param backlight: float between 0 and 100, backlight brightness
"""
raise NotImplementedError
def predict(self, screen):
"""
:param screen: float between 0 and 1, screen brightness
:return: float between 0 and 100, backlight brightness
"""
raise NotImplementedError
def clip(self, brightness):
return round(max(self.minB, min(self.maxB, brightness)))
class SimpleModel(BrightnessModel):
def __init__(self, minB, maxB, maxObs=10):
super().__init__(minB, maxB)
self._observations = []
self._lastSave = None
self._i = 0
assert(maxObs > 0)
self.maxObs = maxObs
def addObservation(self, screen, backlight):
self._observations = self.filterInconsistent(screen, backlight, self._observations)
if len(self._observations) == self.maxObs:
# remove item with smallest _i
iSmallest = min(
self._observations,
key=lambda x: x[-1]
)[-1]
self._observations = [
obs for obs in self._observations
if obs[-1] > iSmallest
]
self._observations.append((screen, backlight, self._i))
self._i += 1
@staticmethod
def filterInconsistent(screen, backlight, observations):
return [
(s, b, i) for s, b, i in observations
if (s > screen and b < backlight) or (s < screen and b > backlight)
]
def predict(self, screen):
assert(len(self._observations) > 0)
if len(self._observations) == 1:
return self._observations[0][1]
# find screenL, screenR, backL, backR
# such that screenL <= screen <= screenR, and screenL is maximized, and screenR is minimized
# and backL and backR are the corresponding backlights
screenL = screenR = backL = backR = None
try:
screenL, backL, _ = max(
filter(
lambda x: x[0] <= screen,
self._observations,
),
key=lambda x: x[0])
except ValueError:
pass
try:
screenR, backR, _ = min(
filter(
lambda x: x[0] >= screen,
self._observations,
),
key=lambda x: x[0])
except ValueError:
pass
if screenL is None:
screenL, backL, _ = min(
filter(
lambda x: x[0] > screenR,
self._observations,
),
key=lambda x: x[0])
elif screenR is None:
screenR, backR, _ = max(
filter(
lambda x: x[0] < screenL,
self._observations,
),
key=lambda x: x[0])
# now we have screenL, screenR, backL, backR
# we want to find the linear interpolation between backL and backR
# that corresponds to screen
if screenL == screenR:
assert(backL == backR)
return backL
return self.clip(
backL + (backR - backL) * (screen - screenL) / (screenR - screenL)
)
def load(self, file):
if not super().load(file):
return False
with open(file, "rb") as f:
self._observations = pickle.load(f)
self._i = 1 + max(
obs[-1] for obs in self._observations
)
self._lastSave = copy(self._observations)
return True
def save(self, file):
super().save(file)
with open(file, "wb") as f:
pickle.dump(self._observations, f)
self._lastSave = copy(self._observations)
def saveIfNecessary(self, file):
if self._lastSave != self._observations:
self.save(file)