From 54b283a744bad7b2df02cc78ae5954f426a26570 Mon Sep 17 00:00:00 2001 From: TJ16th Date: Mon, 10 Nov 2025 02:51:28 +0900 Subject: [PATCH] ImagePaletteExtractor: change outputs to JSON-only (custom_json); update tests --- image_palette_extractor.py | 13 ++++++------- js/color_palette.js | 19 +++++++++++++++++++ tests/test_image_palette_extractor.py | 11 +++++++---- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/image_palette_extractor.py b/image_palette_extractor.py index 4f524f9..0f47e75 100644 --- a/image_palette_extractor.py +++ b/image_palette_extractor.py @@ -125,9 +125,8 @@ def _hex_from_rgb01(rgb: np.ndarray) -> str: class ImagePaletteExtractor: """ - Extract 8 representative colors from an IMAGE and output: - - custom_json: {"colors":["#RRGGBB", ...]} (8 colors) - - color_0 .. color_7: each #RRGGBB + Extract 8 representative colors from an IMAGE and output only a JSON string: + custom_json = {"colors":["#RRGGBB", ...]} (8 colors) """ @classmethod @@ -142,8 +141,8 @@ class ImagePaletteExtractor: } } - RETURN_TYPES = tuple(["STRING"] * 9) - RETURN_NAMES = ("custom_json",) + tuple([f"color_{i}" for i in range(8)]) + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("custom_json",) FUNCTION = "extract_palette" CATEGORY = "TJnodes/color" @@ -242,7 +241,7 @@ class ImagePaletteExtractor: order = np.argsort(-counts) # frequency return centers_rgb[order], counts[order] - def extract_palette(self, image, sample_max_pixels: int = 100_000, merge_delta: float = 6.0, seed: int = 42, sort: str = "frequency") -> Tuple[str, str, str, str, str, str, str, str, str]: + def extract_palette(self, image, sample_max_pixels: int = 100_000, merge_delta: float = 6.0, seed: int = 42, sort: str = "frequency") -> Tuple[str]: arr = self._to_numpy_rgb01(image) samples = self._downsample_or_sample(arr, sample_max_pixels, seed) lab = _rgb_to_lab(samples) @@ -263,4 +262,4 @@ class ImagePaletteExtractor: while len(hexes) < k: hexes.append("#000000") custom_json = json.dumps({"colors": hexes}, ensure_ascii=False) - return (custom_json, *hexes[:k]) + return (custom_json,) diff --git a/js/color_palette.js b/js/color_palette.js index 4b300aa..b9ee911 100644 --- a/js/color_palette.js +++ b/js/color_palette.js @@ -123,6 +123,25 @@ app.registerExtension({ const r = onDrawForeground?.apply(this, arguments); const ui = attachPaletteUI(this); positionChips(this, ui.container); + + // custom の場合は custom_json を毎フレーム監視して即時更新 + const presetWidget = this.widgets?.find(w => w.name === 'preset'); + const customWidget = this.widgets?.find(w => w.name === 'custom_json'); + if (presetWidget && presetWidget.value === 'custom' && customWidget) { + const currentText = customWidget.value || ''; + if (this.__last_custom_text__ !== currentText) { + this.__last_custom_text__ = currentText; + const colors = parseCustomColors(currentText); + if (colors) { + this.__palette_colors__ = colors; + setErrorVisual(this, false); + } else { + this.__palette_colors__ = null; + setErrorVisual(this, !!currentText.trim()); + } + } + } + updateChips(this, ui.chips); return r; }; diff --git a/tests/test_image_palette_extractor.py b/tests/test_image_palette_extractor.py index ba60f99..480c0a2 100644 --- a/tests/test_image_palette_extractor.py +++ b/tests/test_image_palette_extractor.py @@ -36,12 +36,15 @@ class TestImagePaletteExtractor(unittest.TestCase): node = ImagePaletteExtractor() image = self._make_test_image() out = node.extract_palette(image, sample_max_pixels=50_000, seed=1) - self.assertEqual(len(out), 9) + self.assertEqual(len(out), 1) custom_json = out[0] - colors = out[1:] self.assertTrue(custom_json.startswith('{')) - self.assertEqual(len(colors), 8) - self.assertTrue(all(isinstance(c, str) and c.startswith('#') for c in colors)) + # Verify JSON parsable and has 8 colors + import json + data = json.loads(custom_json) + self.assertIn('colors', data) + self.assertEqual(len(data['colors']), 8) + self.assertTrue(all(isinstance(c, str) and c.startswith('#') for c in data['colors'])) if __name__ == '__main__':