From a188c8075c229c6544348e4941c957c51a42e558 Mon Sep 17 00:00:00 2001 From: Corentin Vigourt Date: Wed, 12 Apr 2023 11:51:29 +0200 Subject: [PATCH 1/4] Update rgb sensors --- python/evolutek/lib/indicators/ws2812b.py | 16 +-- python/evolutek/lib/sensors/rgb_sensors.py | 52 ++++----- python/evolutek/lib/utils/color.py | 103 ++++++++++++++++-- .../tests/sensors/test_rgb_sensors.py | 2 +- 4 files changed, 124 insertions(+), 49 deletions(-) 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..9fea87b2 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,28 @@ 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 = 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__) - 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 color == Color.Unknown: + print(f"[{self.name}] Sensor {self.id} didn't see any color") - if rgb[index] < self.calibration[index] * SENSITIVITY: - return Color.Unknown + dist = rgb.compute_dist(color.value) + if dist >= DELTA_FOR_COLOR: + print(f"[{self.name}] Sensor {self.id} detect a bad color (dist={dist})") + - 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 with {color.value} with dist {dist}") + 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..fb503fa2 100644 --- a/python/evolutek/lib/utils/color.py +++ b/python/evolutek/lib/utils/color.py @@ -1,16 +1,81 @@ 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(r, g, b) + + 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) + + @staticmethod + def mean(colors): + result = RGBColor(0, 0, 0) + for color in colors: + result += color + return result / len(colors) + + def compute_dist(self, color): + tmp = self.__sub__(color) + return sqrt( + tmp.r ** 2 + tmp.g **2 + tmp.b **2 + ) 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 +83,21 @@ 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__: + if rgb_color == color.value: + return color + return Color.Unknown + + @staticmethod + def get_closest_color(rgb_color, colors): + closest = Color.Unknow + min_dist = rgb_color.compute_dist(Color.Unknow.value) + for color in colors: + dist = rgb_color.compute_dist(color.value) + if 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..cd6c1ef2 100644 --- a/python/evolutek/tests/sensors/test_rgb_sensors.py +++ b/python/evolutek/tests/sensors/test_rgb_sensors.py @@ -2,7 +2,7 @@ from evolutek.lib.sensors.rgb_sensors import RGBSensors -rgb_sensors = RGBSensors([1, 2, 3, 4]) +rgb_sensors = RGBSensors([1]) print(rgb_sensors.is_initialized()) print(rgb_sensors) From b2dc86bfe571f1f51900cfec57e5cff57ceae1c3 Mon Sep 17 00:00:00 2001 From: PAL Date: Wed, 25 May 2022 18:13:03 +0200 Subject: [PATCH 2/4] Fixes --- python/evolutek/lib/sensors/rgb_sensors.py | 2 +- python/evolutek/lib/utils/color.py | 10 +++++----- python/evolutek/tests/sensors/test_rgb_sensors.py | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/python/evolutek/lib/sensors/rgb_sensors.py b/python/evolutek/lib/sensors/rgb_sensors.py index 9fea87b2..0d059a89 100644 --- a/python/evolutek/lib/sensors/rgb_sensors.py +++ b/python/evolutek/lib/sensors/rgb_sensors.py @@ -44,7 +44,7 @@ def calibrate(self): def read(self): 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__) + color = Color.get_closest_color(rgb, self.color_to_detect if self.color_to_detect is not None else Color.__members__.values()) if color == Color.Unknown: print(f"[{self.name}] Sensor {self.id} didn't see any color") diff --git a/python/evolutek/lib/utils/color.py b/python/evolutek/lib/utils/color.py index fb503fa2..a4285eba 100644 --- a/python/evolutek/lib/utils/color.py +++ b/python/evolutek/lib/utils/color.py @@ -10,7 +10,7 @@ def __init__(self, r, g, b): @staticmethod def from_tupple(t): - return RGBColor(r, g, b) + return RGBColor(t[0], t[1], t[2]) def to_tupple(self): return (self.r, self.g, self.b) @@ -57,7 +57,7 @@ def mean(colors): result = RGBColor(0, 0, 0) for color in colors: result += color - return result / len(colors) + return result.__div__(len(colors)) def compute_dist(self, color): tmp = self.__sub__(color) @@ -86,15 +86,15 @@ def get_by_name(name): @staticmethod def get_by_rgb(rgb_color): - for color in Color.__members__: + 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 = Color.Unknow - min_dist = rgb_color.compute_dist(Color.Unknow.value) + closest = Color.Unknown + min_dist = rgb_color.compute_dist(Color.Unknown.value) for color in colors: dist = rgb_color.compute_dist(color.value) if dist < min_dist: diff --git a/python/evolutek/tests/sensors/test_rgb_sensors.py b/python/evolutek/tests/sensors/test_rgb_sensors.py index cd6c1ef2..6d9c0506 100644 --- a/python/evolutek/tests/sensors/test_rgb_sensors.py +++ b/python/evolutek/tests/sensors/test_rgb_sensors.py @@ -3,7 +3,6 @@ from evolutek.lib.sensors.rgb_sensors import RGBSensors rgb_sensors = RGBSensors([1]) -print(rgb_sensors.is_initialized()) print(rgb_sensors) for sensor in rgb_sensors: From ca1c37e7974162d564d80b0fad29dda9606f6a48 Mon Sep 17 00:00:00 2001 From: Corentin Date: Wed, 12 Apr 2023 18:44:01 +0200 Subject: [PATCH 3/4] Try another method to detect color --- python/evolutek/lib/sensors/rgb_sensors.py | 8 ----- python/evolutek/lib/utils/color.py | 34 +++++++++++++++------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/python/evolutek/lib/sensors/rgb_sensors.py b/python/evolutek/lib/sensors/rgb_sensors.py index 0d059a89..1a76d13b 100644 --- a/python/evolutek/lib/sensors/rgb_sensors.py +++ b/python/evolutek/lib/sensors/rgb_sensors.py @@ -46,14 +46,6 @@ def read(self): 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()) - if color == Color.Unknown: - print(f"[{self.name}] Sensor {self.id} didn't see any color") - - dist = rgb.compute_dist(color.value) - if dist >= DELTA_FOR_COLOR: - print(f"[{self.name}] Sensor {self.id} detect a bad color (dist={dist})") - - print(f"[{self.name}] Sensor {self.id} detect color with {color.value} with dist {dist}") return color diff --git a/python/evolutek/lib/utils/color.py b/python/evolutek/lib/utils/color.py index a4285eba..68fcf1ad 100644 --- a/python/evolutek/lib/utils/color.py +++ b/python/evolutek/lib/utils/color.py @@ -52,18 +52,27 @@ def __mul__(self, coef: float): return RGBColor(self.r * coef, self.g * coef, self.b * coef) + def get_rgb_percentages(self): + sum = self.r + self.g + self.b + 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)) - - def compute_dist(self, color): - tmp = self.__sub__(color) - return sqrt( - tmp.r ** 2 + tmp.g **2 + tmp.b **2 - ) + + @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 = RGBColor(0, 0, 0) @@ -93,11 +102,14 @@ def get_by_rgb(rgb_color): @staticmethod def get_closest_color(rgb_color, colors): - closest = Color.Unknown - min_dist = rgb_color.compute_dist(Color.Unknown.value) + closest = None + min_dist = None + for color in colors: - dist = rgb_color.compute_dist(color.value) - if dist < min_dist: + dist = RGBColor.compute_dist(rgb_color, colors) + if min_dist is None or dist < min_dist: min_dist = dist closest = color - return closest + + return color + From ec591bf9d4c8d66c85b42d6d59cb4048e7e3002d Mon Sep 17 00:00:00 2001 From: PAL Date: Wed, 25 May 2022 17:40:23 +0200 Subject: [PATCH 4/4] Fix new method --- python/evolutek/lib/sensors/rgb_sensors.py | 2 +- python/evolutek/lib/utils/color.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/python/evolutek/lib/sensors/rgb_sensors.py b/python/evolutek/lib/sensors/rgb_sensors.py index 1a76d13b..08ab02ba 100644 --- a/python/evolutek/lib/sensors/rgb_sensors.py +++ b/python/evolutek/lib/sensors/rgb_sensors.py @@ -46,7 +46,7 @@ def read(self): 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()) - print(f"[{self.name}] Sensor {self.id} detect color with {color.value} with dist {dist}") + print(f"[{self.name}] Sensor {self.id} detect color {color.name} with {color.value}") return color def __str__(self): diff --git a/python/evolutek/lib/utils/color.py b/python/evolutek/lib/utils/color.py index 68fcf1ad..154bc19f 100644 --- a/python/evolutek/lib/utils/color.py +++ b/python/evolutek/lib/utils/color.py @@ -54,6 +54,8 @@ def __mul__(self, coef: float): 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 @@ -104,12 +106,10 @@ def get_by_rgb(rgb_color): def get_closest_color(rgb_color, colors): closest = None min_dist = None - for color in colors: - dist = RGBColor.compute_dist(rgb_color, colors) + dist = RGBColor.compute_dist(rgb_color, color.value) if min_dist is None or dist < min_dist: min_dist = dist closest = color - - return color + return closest