diff --git a/app/lang/de.toml b/app/lang/de.toml index de7c127..78b6401 100644 --- a/app/lang/de.toml +++ b/app/lang/de.toml @@ -39,6 +39,7 @@ "sliders.hue_min" = "Hue Min (°)" "sliders.hue_max" = "Hue Max (°)" "sliders.sat_min" = "Sättigung Min (%)" +"sliders.sat_max" = "Sättigung Max (%)" "sliders.val_min" = "Helligkeit Min (%)" "sliders.val_max" = "Helligkeit Max (%)" "sliders.alpha" = "Overlay Alpha" diff --git a/app/lang/en.toml b/app/lang/en.toml index c0183d6..259c5ae 100644 --- a/app/lang/en.toml +++ b/app/lang/en.toml @@ -39,6 +39,7 @@ "sliders.hue_min" = "Hue min (°)" "sliders.hue_max" = "Hue max (°)" "sliders.sat_min" = "Saturation min (%)" +"sliders.sat_max" = "Saturation max (%)" "sliders.val_min" = "Value min (%)" "sliders.val_max" = "Value max (%)" "sliders.alpha" = "Overlay alpha" diff --git a/app/qt/image_processor.py b/app/qt/image_processor.py index 5d1f6f5..39d207f 100644 --- a/app/qt/image_processor.py +++ b/app/qt/image_processor.py @@ -106,6 +106,7 @@ class QtImageProcessor: "hue_min": 0, "hue_max": 360, "sat_min": 25, + "sat_max": 100, "val_min": 15, "val_max": 100, "alpha": 120, @@ -113,6 +114,7 @@ class QtImageProcessor: self.hue_min = self.defaults["hue_min"] self.hue_max = self.defaults["hue_max"] self.sat_min = self.defaults["sat_min"] + self.sat_max = self.defaults["sat_max"] self.val_min = self.defaults["val_min"] self.val_max = self.defaults["val_max"] self.alpha = self.defaults["alpha"] @@ -219,6 +221,7 @@ class QtImageProcessor: match_mask = ( hue_ok & (sat >= float(self.sat_min)) + & (sat <= float(self.sat_max)) & (val >= float(self.val_min)) & (val <= float(self.val_max)) & (alpha_ch > 0) @@ -285,6 +288,7 @@ class QtImageProcessor: match_mask = ( hue_ok & (sat >= float(self.sat_min)) + & (sat <= float(self.sat_max)) & (val >= float(self.val_min)) & (val <= float(self.val_max)) & (alpha_ch > 0) @@ -321,7 +325,7 @@ class QtImageProcessor: hue_ok = self.hue_min <= hue <= self.hue_max else: hue_ok = hue >= self.hue_min or hue <= self.hue_max - sat_ok = s * 100.0 >= self.sat_min + sat_ok = self.sat_min <= s * 100.0 <= self.sat_max val_ok = self.val_min <= v * 100.0 <= self.val_max return hue_ok and sat_ok and val_ok diff --git a/app/qt/main_window.py b/app/qt/main_window.py index bfcfd36..eda792a 100644 --- a/app/qt/main_window.py +++ b/app/qt/main_window.py @@ -41,6 +41,7 @@ SLIDER_SPECS: List[Tuple[str, str, int, int]] = [ ("sliders.hue_min", "hue_min", 0, 360), ("sliders.hue_max", "hue_max", 0, 360), ("sliders.sat_min", "sat_min", 0, 100), + ("sliders.sat_max", "sat_max", 0, 100), ("sliders.val_min", "val_min", 0, 100), ("sliders.val_max", "val_max", 0, 100), ("sliders.alpha", "alpha", 0, 255), @@ -917,6 +918,7 @@ class MainWindow(QtWidgets.QMainWindow, I18nMixin): "hue_min": self.processor.hue_min, "hue_max": self.processor.hue_max, "sat_min": self.processor.sat_min, + "sat_max": self.processor.sat_max, "val_min": self.processor.val_min, "val_max": self.processor.val_max, "alpha": self.processor.alpha, @@ -954,10 +956,12 @@ class MainWindow(QtWidgets.QMainWindow, I18nMixin): self._update_color_display(self._current_color, self._t("palette.current")) # 2. Apply slider values - keys = ["hue_min", "hue_max", "sat_min", "val_min", "val_max", "alpha"] + keys = ["hue_min", "hue_max", "sat_min", "sat_max", "val_min", "val_max", "alpha"] for key in keys: if key in settings: setattr(self.processor, key, settings[key]) + else: + setattr(self.processor, key, self.processor.defaults.get(key, 0)) # 3. Apply shapes and reference size ref_size = None @@ -1177,13 +1181,15 @@ class MainWindow(QtWidgets.QMainWindow, I18nMixin): margin = 15 hue_min = max(0, int(hue) - margin) hue_max = min(360, int(hue) + margin) + # For a picked color, give a window of ±20% for saturation and -30% for value sat_min = max(0, int(sat) - 20) + sat_max = min(100, int(sat) + 20) val_min = max(0, int(val) - 30) val_max = 100 for attr, value in [ ("hue_min", hue_min), ("hue_max", hue_max), - ("sat_min", sat_min), ("val_min", val_min), ("val_max", val_max), + ("sat_min", sat_min), ("sat_max", sat_max), ("val_min", val_min), ("val_max", val_max), ]: ctrl = self._slider_controls.get(attr) if ctrl: diff --git a/config.toml b/config.toml index 57155b1..b91c6f3 100644 --- a/config.toml +++ b/config.toml @@ -15,6 +15,7 @@ overlay_color = "#ff0000" hue_min = 250.0 hue_max = 310.0 sat_min = 15.0 +sat_max = 100.0 val_min = 15.0 val_max = 100.0 alpha = 120 diff --git a/tests/test_image_processor.py b/tests/test_image_processor.py index 9d3495f..41b331a 100644 --- a/tests/test_image_processor.py +++ b/tests/test_image_processor.py @@ -12,18 +12,18 @@ def test_stats_summary(): matches_excl=10, total_excl=20 ) - # Mock translator def mock_t(key, **kwargs): if key == "stats.placeholder": return "Placeholder" - return f"{kwargs['with_pct']:.1f} {kwargs['without_pct']:.1f} {kwargs['excluded_pct']:.1f} {kwargs['excluded_match_pct']:.1f}" + if not kwargs: + return key + return f"{kwargs['with_pct']:.1f} {kwargs['without_pct']:.1f} {kwargs['excluded_pct']:.1f}" res = s.summary(mock_t) # with_pct: 40/80 = 50.0 # without_pct: 50/100 = 50.0 # excluded_pct: 20/100 = 20.0 - # excluded_match_pct: 10/20 = 50.0 - assert res == "50.0 50.0 20.0 50.0" + assert res == "50.0 50.0 20.0" def test_stats_empty(): s = Stats()