diff --git a/python/evolutek/lib/indicators/ws2812b.py b/python/evolutek/lib/indicators/ws2812b.py index d52c89d9..12122c23 100644 --- a/python/evolutek/lib/indicators/ws2812b.py +++ b/python/evolutek/lib/indicators/ws2812b.py @@ -57,12 +57,12 @@ def set_mode(self, mode=LightningMode.Loading): self.mode = mode - self.leds.fill(Color.Black.value) + self.leds.fill(Color.Black.value.to_tupple()) if self.mode == LightningMode.Loading: self.current_led = self.nb_leds - 1 for i in range(NB_LOADING_LED): - self.leds[i] = self.loading_color.value + self.leds[i] = self.loading_color.value.to_tupple() elif self.mode == LightningMode.Disabled or self.mode == LightningMode.Error: self.state = False @@ -79,25 +79,25 @@ def run(self): with self.lock: if self.mode == LightningMode.Disabled: for i in range(self.nb_leds): - self.leds[i] = Color.Orange.value if self.state ^ i % 2 == 0 else Color.Black.value + self.leds[i] = Color.Orange.value.to_tupple() if self.state ^ i % 2 == 0 else Color.Black.value.to_tupple() self.state = not self.state elif self.mode == LightningMode.Error: - self.leds.fill(Color.Red.value if self.state else Color.Black.value) + self.leds.fill(Color.Red.value.to_tupple() if self.state else Color.Black.value.to_tupple()) self.state = not self.state elif self.mode == LightningMode.Loading: - self.leds[self.current_led] = Color.Black.value - self.leds[(self.current_led + NB_LOADING_LED) % self.nb_leds] = self.loading_color.value + self.leds[self.current_led] = Color.Black.value.to_tupple() + self.leds[(self.current_led + NB_LOADING_LED) % self.nb_leds] = self.loading_color.value.to_tupple() self.current_led = (self.current_led + 1) % self.nb_leds elif self.mode == LightningMode.Running: - self.leds.fill(Color.Green.value) + self.leds.fill(Color.Green.value.to_tupple()) self.leds.show() sleep(refresh[self.mode]) - self.leds.fill(Color.Black.value) + self.leds.fill(Color.Black.value.to_tupple()) self.leds.show() print(f"[{self.name}] Stopped") diff --git a/python/evolutek/lib/sensors/rgb_sensors.py b/python/evolutek/lib/sensors/rgb_sensors.py index a415edd7..08ab02ba 100644 --- a/python/evolutek/lib/sensors/rgb_sensors.py +++ b/python/evolutek/lib/sensors/rgb_sensors.py @@ -2,27 +2,23 @@ import adafruit_tcs34725 import board import busio -import time - from enum import Enum from time import sleep from evolutek.lib.component import Component, ComponentsHolder -from evolutek.lib.utils.color import Color +from evolutek.lib.utils.color import RGBColor, Color TCA = None -CALIBRATE = 10 - -# Up -> More perturbations (more false positives) -# Down -> Better detection (more false negatives) -SENSITIVITY = 1.25 +CALIBRATION_NB_VALUES = 10 +DELTA_FOR_COLOR = 50 class TCS34725(Component): - def __init__(self, id, channel): - self.calibration = [0, 0, 0] + def __init__(self, id, channel, color_to_detect=None): + self.calibration = RGBColor(0, 0, 0) self.sensor = None self.channel = channel + self.color_to_detect = color_to_detect super().__init__('TCS34725', id) def _initialize(self): @@ -38,32 +34,20 @@ def _initialize(self): return True def calibrate(self): - for i in range(CALIBRATE): - rgb = self.sensor.color_rgb_bytes - self.calibration[0] += rgb[0] - self.calibration[1] += rgb[1] - self.calibration[2] += rgb[2] + calibration = [] + for i in range(CALIBRATION_NB_VALUES): + calibration.append(RGBColor.from_tupple(self.sensor.color_rgb_bytes)) sleep(0.1) - self.calibration[0] /= CALIBRATE - self.calibration[1] /= CALIBRATE - self.calibration[2] /= CALIBRATE - # print('Setup: R = %i - G = %i - B = %i' % (self.calibration[0],self.calibration[1],self.calibration[2])) + self.calibration = RGBColor.mean(calibration) + print(f"[{self.name}] Sensor {self.id} calibrated with {self.calibration}") def read(self): - if not self.is_initialized: - print('[%s] %s %d not initialized' % (self.name, self.name, self.id)) - return None - - rgb = self.sensor.color_rgb_bytes - values = [rgb[0] - self.calibration[0], rgb[1] - self.calibration[1], rgb[2] - self.calibration[2]] - index = values.index(max(values)) - - if rgb[index] < self.calibration[index] * SENSITIVITY: - return Color.Unknown + rgb = RGBColor.from_tupple(self.sensor.color_rgb_bytes) + rgb -= self.calibration + color = Color.get_closest_color(rgb, self.color_to_detect if self.color_to_detect is not None else Color.__members__.values()) - res = [Color.Red, Color.Green, Color.Blue][index] - if res == Color.Blue: return Color.Green - return res + print(f"[{self.name}] Sensor {self.id} detect color {color.name} with {color.value}") + return color def __str__(self): s = "----------\n" diff --git a/python/evolutek/lib/utils/color.py b/python/evolutek/lib/utils/color.py index 73ad3efb..154bc19f 100644 --- a/python/evolutek/lib/utils/color.py +++ b/python/evolutek/lib/utils/color.py @@ -1,16 +1,92 @@ from enum import Enum +from math import sqrt + +class RGBColor: + + def __init__(self, r, g, b): + self.r = r + self.g = g + self.b = b + + @staticmethod + def from_tupple(t): + return RGBColor(t[0], t[1], t[2]) + + def to_tupple(self): + return (self.r, self.g, self.b) + + def __str__(self) -> str: + return f"(R, G, B): ({self.r}, {self.g}, {self.b})" + + def __eq__(self, color) -> bool: + return ( + self.r == color.r and + self.g == color.g and + self.b == color.b + ) + + def __add__(self, color): + return RGBColor( + self.r + color.r, + self.g + color.g, + self.b + color.b + ) + + def __sub__(self, color): + return RGBColor( + self.r - color.r, + self.g - color.g, + self.b - color.b + ) + + def __div__(self, div: int): + return RGBColor( + self.r / div, + self.g / div, + self.b / div + ) + + def __mul__(self, coef: float): + if coef < 0.0 or coef > 1.0: + return Color.Black.value + + return RGBColor(self.r * coef, self.g * coef, self.b * coef) + + def get_rgb_percentages(self): + sum = self.r + self.g + self.b + if sum == 0: + return (0, 0, 0) + return (self.r / sum, self.g / sum, self.b / sum) + + @staticmethod + def mean(colors): + result = RGBColor(0, 0, 0) + for color in colors: + result += color + return result.__div__(len(colors)) + + @staticmethod + def compute_dist(a, b): + percentages_a = a.get_rgb_percentages() + percentages_b = b.get_rgb_percentages() + + return (( + abs(percentages_a[0] - percentages_b[0]) + + abs(percentages_a[1] - percentages_b[1]) + + abs(percentages_a[2] - percentages_b[2]) + ) / 3) class Color(Enum): - Black = (0, 0, 0) - Blue = (0, 0, 255) - Brown = (76, 43, 32) - Green = (0, 255, 0) - Orange = (255, 50, 0) - Purple = (115, 25, 115) - Red = (255, 0, 0) - Pink = (188, 64, 119) - Yellow = (247, 181, 0) - Unknown = (-1, -1, -1) + Black = RGBColor(0, 0, 0) + Blue = RGBColor(0, 0, 255) + Brown = RGBColor(76, 43, 32) + Green = RGBColor(0, 255, 0) + Orange = RGBColor(255, 50, 0) + Pink = RGBColor(188, 64, 119) + Purple = RGBColor(115, 25, 115) + Red = RGBColor(255, 0, 0) + Yellow = RGBColor(247, 181, 0) + Unknown = RGBColor(-1, -1, -1) @staticmethod def get_by_name(name): @@ -18,3 +94,22 @@ def get_by_name(name): return Color.__members__[name.capitalize()] except: return Color.Unknown + + @staticmethod + def get_by_rgb(rgb_color): + for color in Color.__members__.values(): + if rgb_color == color.value: + return color + return Color.Unknown + + @staticmethod + def get_closest_color(rgb_color, colors): + closest = None + min_dist = None + for color in colors: + dist = RGBColor.compute_dist(rgb_color, color.value) + if min_dist is None or dist < min_dist: + min_dist = dist + closest = color + return closest + diff --git a/python/evolutek/tests/sensors/test_rgb_sensors.py b/python/evolutek/tests/sensors/test_rgb_sensors.py index 9013c9a8..6d9c0506 100644 --- a/python/evolutek/tests/sensors/test_rgb_sensors.py +++ b/python/evolutek/tests/sensors/test_rgb_sensors.py @@ -2,8 +2,7 @@ from evolutek.lib.sensors.rgb_sensors import RGBSensors -rgb_sensors = RGBSensors([1, 2, 3, 4]) -print(rgb_sensors.is_initialized()) +rgb_sensors = RGBSensors([1]) print(rgb_sensors) for sensor in rgb_sensors: