|
|
|
import math |
|
|
|
from .categories import NodeCategories |
|
from .shared import * |
|
from .dreamtypes import * |
|
|
|
|
|
def _generate_noise(image: DreamImage, color_function, rng: random.Random, block_size, blur_amount, |
|
density) -> DreamImage: |
|
w = block_size[0] |
|
h = block_size[1] |
|
blur_radius = round(max(image.width, image.height) * blur_amount * 0.25) |
|
if w <= (image.width // 128) or h <= (image.height // 128): |
|
return image |
|
max_placements = round(density * (image.width * image.height)) |
|
num = min(max_placements, round((image.width * image.height * 2) / (w * h))) |
|
for i in range(num): |
|
x = rng.randint(-w + 1, image.width - 1) |
|
y = rng.randint(-h + 1, image.height - 1) |
|
image.color_area(x, y, w, h, color_function(x + (w >> 1), y + (h >> 1))) |
|
image = image.blur(blur_radius) |
|
return _generate_noise(image, color_function, rng, (w >> 1, h >> 1), blur_amount, density) |
|
|
|
|
|
class DreamNoiseFromPalette: |
|
NODE_NAME = "Noise from Palette" |
|
ICON = "🌫" |
|
|
|
@classmethod |
|
def INPUT_TYPES(cls): |
|
return { |
|
"required": SharedTypes.palette | { |
|
"width": ("INT", {"default": 512, "min": 1, "max": 8192}), |
|
"height": ("INT", {"default": 512, "min": 1, "max": 8192}), |
|
"blur_amount": ("FLOAT", {"default": 0.3, "min": 0, "max": 1.0, "step": 0.05}), |
|
"density": ("FLOAT", {"default": 0.5, "min": 0.1, "max": 1.0, "step": 0.025}), |
|
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}) |
|
}, |
|
} |
|
|
|
CATEGORY = NodeCategories.IMAGE_GENERATE |
|
RETURN_TYPES = ("IMAGE",) |
|
RETURN_NAMES = ("image",) |
|
FUNCTION = "result" |
|
|
|
@classmethod |
|
def IS_CHANGED(cls, *values): |
|
return ALWAYS_CHANGED_FLAG |
|
|
|
def result(self, palette: Tuple[RGBPalette], width, height, seed, blur_amount, density): |
|
outputs = list() |
|
rng = random.Random() |
|
for p in palette: |
|
seed += 1 |
|
color_iterator = p.random_iteration(seed) |
|
image = DreamImage(pil_image=Image.new("RGB", (width, height), color=next(color_iterator))) |
|
image = _generate_noise(image, lambda x, y: next(color_iterator), rng, |
|
(image.width >> 1, image.height >> 1), blur_amount, density) |
|
outputs.append(image) |
|
|
|
return (DreamImage.join_to_tensor_data(outputs),) |
|
|
|
|
|
class DreamNoiseFromAreaPalettes: |
|
NODE_NAME = "Noise from Area Palettes" |
|
|
|
@classmethod |
|
def INPUT_TYPES(cls): |
|
return { |
|
"optional": { |
|
"top_left_palette": (RGBPalette.ID,), |
|
"top_center_palette": (RGBPalette.ID,), |
|
"top_right_palette": (RGBPalette.ID,), |
|
"center_left_palette": (RGBPalette.ID,), |
|
"center_palette": (RGBPalette.ID,), |
|
"center_right_palette": (RGBPalette.ID,), |
|
"bottom_left_palette": (RGBPalette.ID,), |
|
"bottom_center_palette": (RGBPalette.ID,), |
|
"bottom_right_palette": (RGBPalette.ID,), |
|
}, |
|
"required": { |
|
"area_sharpness": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.05}), |
|
"width": ("INT", {"default": 512, "min": 1, "max": 8192}), |
|
"height": ("INT", {"default": 512, "min": 1, "max": 8192}), |
|
"blur_amount": ("FLOAT", {"default": 0.3, "min": 0, "max": 1.0, "step": 0.05}), |
|
"density": ("FLOAT", {"default": 0.5, "min": 0.1, "max": 1.0, "step": 0.025}), |
|
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), |
|
}, |
|
} |
|
|
|
CATEGORY = NodeCategories.IMAGE_GENERATE |
|
ICON = "🌫" |
|
RETURN_TYPES = ("IMAGE",) |
|
RETURN_NAMES = ("image",) |
|
FUNCTION = "result" |
|
|
|
@classmethod |
|
def IS_CHANGED(cls, *values): |
|
return ALWAYS_CHANGED_FLAG |
|
|
|
def _area_coordinates(self, width, height): |
|
dx = width / 6 |
|
dy = height / 6 |
|
return { |
|
"top_left_palette": (dx, dy), |
|
"top_center_palette": (dx * 3, dy), |
|
"top_right_palette": (dx * 5, dy), |
|
"center_left_palette": (dx, dy * 3), |
|
"center_palette": (dx * 3, dy * 3), |
|
"center_right_palette": (dx * 5, dy * 3), |
|
"bottom_left_palette": (dx * 1, dy * 5), |
|
"bottom_center_palette": (dx * 3, dy * 5), |
|
"bottom_right_palette": (dx * 5, dy * 5), |
|
} |
|
|
|
def _pick_random_area(self, active_coordinates, x, y, rng, area_sharpness): |
|
def _dst(x1, y1, x2, y2): |
|
a = x1 - x2 |
|
b = y1 - y2 |
|
return math.sqrt(a * a + b * b) |
|
|
|
distances = list(map(lambda item: (item[0], _dst(item[1][0], item[1][1], x, y)), active_coordinates)) |
|
areas_by_weight = list( |
|
map(lambda item: (math.pow((1.0 / max(1, item[1])), 0.5 + 4.5 * area_sharpness), item[0]), distances)) |
|
return pick_random_by_weight(areas_by_weight, rng) |
|
|
|
def _setup_initial_colors(self, image: DreamImage, color_func): |
|
w = image.width |
|
h = image.height |
|
wpart = round(w / 3) |
|
hpart = round(h / 3) |
|
for i in range(3): |
|
for j in range(3): |
|
image.color_area(wpart * i, hpart * j, w, h, |
|
color_func(wpart * i + w // 2, hpart * j + h // 2)) |
|
|
|
def result(self, width, height, seed, blur_amount, density, area_sharpness, **palettes): |
|
outputs = list() |
|
rng = random.Random() |
|
coordinates = self._area_coordinates(width, height) |
|
active_palettes = list(filter(lambda pair: pair[1] is not None and len(pair[1]) > 0, palettes.items())) |
|
active_coordinates = list(map(lambda item: (item[0], coordinates[item[0]]), active_palettes)) |
|
|
|
n = max(list(map(len, palettes.values())) + [0]) |
|
for b in range(n): |
|
batch_palettes = dict(map(lambda item: (item[0], item[1][b].random_iteration(seed)), active_palettes)) |
|
|
|
def _color_func(x, y): |
|
name = self._pick_random_area(active_coordinates, x, y, rng, area_sharpness) |
|
rgb = batch_palettes[name] |
|
return next(rgb) |
|
|
|
image = DreamImage(pil_image=Image.new("RGB", (width, height))) |
|
self._setup_initial_colors(image, _color_func) |
|
image = _generate_noise(image, _color_func, rng, (round(image.width / 3), round(image.height / 3)), |
|
blur_amount, density) |
|
outputs.append(image) |
|
|
|
if not outputs: |
|
outputs.append(DreamImage(pil_image=Image.new("RGB", (width, height)))) |
|
|
|
return (DreamImage.join_to_tensor_data(outputs),) |
|
|