| |
|
|
| from .categories import NodeCategories |
| from .shared import * |
| from .dreamtypes import * |
|
|
|
|
| class DreamImageAreaSampler: |
| NODE_NAME = "Sample Image Area as Palette" |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return { |
| "required": { |
| "image": ("IMAGE",), |
| "samples": ("INT", {"default": 256, "min": 1, "max": 1024 * 4}), |
| "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), |
| "area": (["top-left", "top-center", "top-right", |
| "center-left", "center", "center-right", |
| "bottom-left", "bottom-center", "bottom-right"],) |
| }, |
| } |
|
|
| CATEGORY = NodeCategories.IMAGE_COLORS |
| RETURN_TYPES = (RGBPalette.ID,) |
| RETURN_NAMES = ("palette",) |
| FUNCTION = "result" |
|
|
| @classmethod |
| def IS_CHANGED(cls, *values): |
| return ALWAYS_CHANGED_FLAG |
|
|
| def _get_pixel_area(self, img: DreamImage, area): |
| w = img.width |
| h = img.height |
| wpart = round(w / 3) |
| hpart = round(h / 3) |
| x0 = 0 |
| x1 = wpart - 1 |
| x2 = wpart |
| x3 = wpart + wpart - 1 |
| x4 = wpart + wpart |
| x5 = w - 1 |
| y0 = 0 |
| y1 = hpart - 1 |
| y2 = hpart |
| y3 = hpart + hpart - 1 |
| y4 = hpart + hpart |
| y5 = h - 1 |
| if area == "center": |
| return (x2, y2, x3, y3) |
| elif area == "top-center": |
| return (x2, y0, x3, y1) |
| elif area == "bottom-center": |
| return (x2, y4, x3, y5) |
| elif area == "center-left": |
| return (x0, y2, x1, y3) |
| elif area == "top-left": |
| return (x0, y0, x1, y1) |
| elif area == "bottom-left": |
| return (x0, y4, x1, y5) |
| elif area == "center-right": |
| return (x4, y2, x5, y3) |
| elif area == "top-right": |
| return (x4, y0, x5, y1) |
| elif area == "bottom-right": |
| return (x4, y4, x5, y5) |
|
|
| def result(self, image, samples, seed, area): |
| result = list() |
| r = random.Random() |
| r.seed(seed) |
| for data in image: |
| di = DreamImage(tensor_image=data) |
| area = self._get_pixel_area(di, area) |
|
|
| pixels = list() |
| for i in range(samples): |
| x = r.randint(area[0], area[2]) |
| y = r.randint(area[1], area[3]) |
| pixels.append(di.get_pixel(x, y)) |
| result.append(RGBPalette(colors=pixels)) |
|
|
| return (tuple(result),) |
|
|
|
|
| class DreamImageSampler: |
| NODE_NAME = "Sample Image as Palette" |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return { |
| "required": { |
| "image": ("IMAGE",), |
| "samples": ("INT", {"default": 1024, "min": 1, "max": 1024 * 4}), |
| "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}) |
| }, |
| } |
|
|
| CATEGORY = NodeCategories.IMAGE_COLORS |
| RETURN_TYPES = (RGBPalette.ID,) |
| RETURN_NAMES = ("palette",) |
| FUNCTION = "result" |
|
|
| @classmethod |
| def IS_CHANGED(cls, *values): |
| return ALWAYS_CHANGED_FLAG |
|
|
| def result(self, image, samples, seed): |
| result = list() |
| r = random.Random() |
| r.seed(seed) |
| for data in image: |
| di = DreamImage(tensor_image=data) |
| pixels = list() |
| for i in range(samples): |
| x = r.randint(0, di.width - 1) |
| y = r.randint(0, di.height - 1) |
| pixels.append(di.get_pixel(x, y)) |
| result.append(RGBPalette(colors=pixels)) |
|
|
| return (tuple(result),) |
|
|
|
|
| class DreamColorAlign: |
| NODE_NAME = "Palette Color Align" |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return { |
| "required": SharedTypes.palette | { |
| "target_align": (RGBPalette.ID,), |
| "alignment_factor": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 10.0, "step": 0.1}), |
| } |
| } |
|
|
| CATEGORY = NodeCategories.IMAGE_COLORS |
| RETURN_TYPES = (RGBPalette.ID,) |
| RETURN_NAMES = ("palette",) |
| FUNCTION = "result" |
|
|
| @classmethod |
| def IS_CHANGED(cls, *values): |
| return ALWAYS_CHANGED_FLAG |
|
|
| def result(self, palette: Tuple[RGBPalette], target_align: Tuple[RGBPalette], alignment_factor: float): |
| results = list() |
|
|
| def _limit(c): |
| return max(min(c, 255), 0) |
|
|
| for i in range(len(palette)): |
| p = palette[i] |
| t = target_align[i] |
| (_, _, r1, g1, b1) = p.analyze() |
| (_, _, r2, g2, b2) = t.analyze() |
|
|
| dr = (r2 - r1) * alignment_factor |
| dg = (g2 - g1) * alignment_factor |
| db = (b2 - b1) * alignment_factor |
| new_pixels = list() |
| for pixel in p: |
| r = _limit(round(pixel[0] + (255 * dr))) |
| g = _limit(round(pixel[1] + (255 * dg))) |
| b = _limit(round(pixel[1] + (255 * db))) |
| new_pixels.append((r, g, b)) |
| results.append(RGBPalette(colors=new_pixels)) |
| return (tuple(results),) |
|
|
|
|
| class DreamColorShift: |
| NODE_NAME = "Palette Color Shift" |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return { |
| "required": SharedTypes.palette | { |
| "red_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step": 0.1}), |
| "green_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step": 0.1}), |
| "blue_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step": 0.1}), |
| "fixed_brightness": (["yes", "no"],), |
| } |
| } |
|
|
| CATEGORY = NodeCategories.IMAGE_COLORS |
| RETURN_TYPES = (RGBPalette.ID,) |
| RETURN_NAMES = ("palette",) |
| FUNCTION = "result" |
|
|
| @classmethod |
| def IS_CHANGED(cls, *values): |
| return ALWAYS_CHANGED_FLAG |
|
|
| def result(self, palette, red_multiplier, green_multiplier, blue_multiplier, fixed_brightness): |
| results = list() |
|
|
| def _limit(c): |
| return max(min(c, 255), 0) |
|
|
| for p in palette: |
| new_pixels = list() |
| for pixel in p: |
| s = pixel[0] + pixel[1] + pixel[2] |
| r = _limit(round(pixel[0] * red_multiplier)) |
| g = _limit(round(pixel[1] * green_multiplier)) |
| b = _limit(round(pixel[2] * blue_multiplier)) |
| if fixed_brightness == "yes": |
| brightness_factor = max(s, 1) / float(max(r + g + b, 1)) |
| r = _limit(round(r * brightness_factor)) |
| g = _limit(round(g * brightness_factor)) |
| b = _limit(round(b * brightness_factor)) |
|
|
| new_pixels.append((r, g, b)) |
| results.append(RGBPalette(colors=new_pixels)) |
| return (tuple(results),) |
|
|
|
|
| class DreamImageColorShift: |
| NODE_NAME = "Image Color Shift" |
| ICON = "🖼" |
| @classmethod |
| def INPUT_TYPES(cls): |
| return { |
| "required": {"image": ("IMAGE",), |
| "red_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0}), |
| "green_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0}), |
| "blue_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0}), |
| }, |
|
|
| } |
|
|
| CATEGORY = NodeCategories.IMAGE_COLORS |
| RETURN_TYPES = ("IMAGE",) |
| RETURN_NAMES = ("image",) |
| FUNCTION = "result" |
|
|
| @classmethod |
| def IS_CHANGED(cls, *values): |
| return ALWAYS_CHANGED_FLAG |
|
|
| def result(self, image, red_multiplier, green_multiplier, blue_multiplier): |
| proc = DreamImageProcessor(inputs=image) |
|
|
| def recolor(im: DreamImage, *a, **args): |
| return (im.adjust_colors(red_multiplier, green_multiplier, blue_multiplier),) |
|
|
| return proc.process(recolor) |
|
|
|
|
| class DreamImageBrightness: |
| NODE_NAME = "Image Brightness Adjustment" |
| ICON = "☼" |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return { |
| "required": {"image": ("IMAGE",), |
| "factor": ("FLOAT", {"default": 1.0, "min": 0.0}), |
| }, |
|
|
| } |
|
|
| CATEGORY = NodeCategories.IMAGE_COLORS |
| RETURN_TYPES = ("IMAGE",) |
| RETURN_NAMES = ("image",) |
| FUNCTION = "result" |
|
|
| @classmethod |
| def IS_CHANGED(cls, *values): |
| return ALWAYS_CHANGED_FLAG |
|
|
| def result(self, image, factor): |
| proc = DreamImageProcessor(inputs=image) |
|
|
| def change(im: DreamImage, *a, **args): |
| return (im.change_brightness(factor),) |
|
|
| return proc.process(change) |
|
|
|
|
| class DreamImageContrast: |
| NODE_NAME = "Image Contrast Adjustment" |
| ICON = "◐" |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return { |
| "required": {"image": ("IMAGE",), |
| "factor": ("FLOAT", {"default": 1.0, "min": 0.0}), |
| }, |
|
|
| } |
|
|
| CATEGORY = NodeCategories.IMAGE_COLORS |
| RETURN_TYPES = ("IMAGE",) |
| RETURN_NAMES = ("image",) |
| FUNCTION = "result" |
|
|
| @classmethod |
| def IS_CHANGED(cls, *values): |
| return ALWAYS_CHANGED_FLAG |
|
|
| def result(self, image, factor): |
| proc = DreamImageProcessor(inputs=image) |
|
|
| def change(im: DreamImage, *a, **args): |
| return (im.change_contrast(factor),) |
|
|
| return proc.process(change) |
|
|
|
|
| class DreamComparePalette: |
| NODE_NAME = "Compare Palettes" |
| ICON = "📊" |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return { |
| "required": { |
| "a": (RGBPalette.ID,), |
| "b": (RGBPalette.ID,), |
| }, |
| } |
|
|
| CATEGORY = NodeCategories.IMAGE_COLORS |
| RETURN_TYPES = ("FLOAT", "FLOAT", "FLOAT", "FLOAT") |
| RETURN_NAMES = ( |
| "brightness_multiplier", "contrast_multiplier", "red_multiplier", "green_multiplier", "blue_multiplier") |
| FUNCTION = "result" |
|
|
| @classmethod |
| def IS_CHANGED(cls, *values): |
| return ALWAYS_CHANGED_FLAG |
|
|
| def result(self, a, b): |
| MIN_VALUE = 1 / 255.0 |
|
|
| brightness = list() |
| contrasts = list() |
| reds = list() |
| greens = list() |
| blues = list() |
|
|
| for i in range(min(len(a), len(b))): |
| (bright, ctr, red, green, blue) = a[i].analyze() |
| (bright2, ctr2, red2, green2, blue2) = b[i].analyze() |
| brightness.append(bright2 / max(MIN_VALUE, bright)) |
| contrasts.append(ctr2 / max(MIN_VALUE, ctr)) |
| reds.append(red2 / max(MIN_VALUE, red)) |
| greens.append(green2 / max(MIN_VALUE, green)) |
| blues.append(blue2 / max(MIN_VALUE, blue)) |
|
|
| n = len(brightness) |
|
|
| return (sum(brightness) / n, sum(contrasts) / n, sum(reds) / n, |
| sum(greens) / n, sum(blues) / n) |
|
|
|
|
| class DreamAnalyzePalette: |
| NODE_NAME = "Analyze Palette" |
| ICON = "📊" |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return { |
| "required": SharedTypes.palette |
| , |
| } |
|
|
| CATEGORY = NodeCategories.IMAGE_COLORS |
| RETURN_TYPES = ("FLOAT", "FLOAT", "FLOAT", "FLOAT", "FLOAT") |
| RETURN_NAMES = ("brightness", "contrast", "redness", "greenness", "blueness") |
| FUNCTION = "result" |
|
|
| @classmethod |
| def IS_CHANGED(cls, *values): |
| return ALWAYS_CHANGED_FLAG |
|
|
| def result(self, palette): |
| f = 1.0 / len(palette) |
| (w, c, r, g, b) = (0, 0, 0, 0, 0) |
| for p in palette: |
| (brightness, contrast, red, green, blue) = p.analyze() |
| w += brightness |
| c += contrast |
| r += red |
| g += green |
| b += blue |
|
|
| return w * f, c * f, r * f, g * f, b * f |
|
|