V 2.0.0 - Rasterix - 3 new
This commit is contained in:
@@ -22,6 +22,9 @@ from ..components.images import histogram as histogram
|
||||
from ..components.images import img_posterize as img_posterize
|
||||
from ..components.images import img_solarization_bw as img_solarization_bw
|
||||
from ..components.images import img_clarity as img_clarity
|
||||
from ..components.images import img_dehaze as img_dehaze
|
||||
from ..components.images import img_local_laplacian as img_local_laplacian
|
||||
from ..components.images import img_frequency_separation as img_frequency_separation
|
||||
from ..components import utility
|
||||
from .Dashboard import PrimereModelConceptSelector as PrimereModelConceptSelector
|
||||
import os
|
||||
@@ -1001,4 +1004,87 @@ class PrimereClarity:
|
||||
if use_clarity and strength != 0:
|
||||
pil_img = img_clarity.img_clarity(image=pil_img, strength=strength, radius=radius, midtone_range=midtone_range, edge_preservation=edge_preservation, precision=precision)
|
||||
|
||||
return (utility.image_to_tensor(pil_img),)
|
||||
|
||||
class PrimereDehaze:
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("IMAGE",)
|
||||
FUNCTION = "primere_dehaze"
|
||||
CATEGORY = TREE_RASTERIX
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE", {"forceInput": True}),
|
||||
"use_dehaze": ("BOOLEAN", {"default": False, "label_off": "Ignore dehaze", "label_on": "Apply dehaze"}),
|
||||
"precision": ("BOOLEAN", {"default": False, "label_off": "8 bit", "label_on": "16 bit"}),
|
||||
|
||||
"strength": ("FLOAT", {"default": 0.7, "min": 0.0, "max": 2.0, "step": 0.01}),
|
||||
"radius": ("INT", {"default": 15, "min": 3, "max": 100, "step": 1}),
|
||||
"omega": ("FLOAT", {"default": 0.95, "min": 0.5, "max": 1.0, "step": 0.01}),
|
||||
"t0": ("FLOAT", {"default": 0.1, "min": 0.01, "max": 0.5, "step": 0.01}),
|
||||
"contrast": ("FLOAT", {"default": 1.05, "min": 0.5, "max": 2.0, "step": 0.01}),
|
||||
}
|
||||
}
|
||||
|
||||
def primere_dehaze(self, image, use_dehaze, precision, strength, radius, omega, t0, contrast):
|
||||
pil_img = utility.tensor_to_image(image)
|
||||
if use_dehaze and strength > 0:
|
||||
pil_img = img_dehaze.img_dehaze(image=pil_img, strength=strength, radius=radius, omega=omega, t0=t0, contrast=contrast, precision=precision)
|
||||
|
||||
return (utility.image_to_tensor(pil_img),)
|
||||
|
||||
class PrimereLocalLaplacian:
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("IMAGE",)
|
||||
FUNCTION = "primere_local_laplacian"
|
||||
CATEGORY = TREE_RASTERIX
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE", {"forceInput": True}),
|
||||
"use_local_laplacian": ("BOOLEAN", {"default": False, "label_off": "Ignore local laplacian", "label_on": "Apply local laplacian"}),
|
||||
|
||||
"sigma": ("FLOAT", {"default": 1.0, "min": 0.5, "max": 5.0, "step": 0.1}),
|
||||
"contrast": ("FLOAT", {"default": 1.2, "min": 0.5, "max": 3.0, "step": 0.01}),
|
||||
"detail": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 3.0, "step": 0.01}),
|
||||
"levels": ("INT", {"default": 8, "min": 4, "max": 32, "step": 1}),
|
||||
}
|
||||
}
|
||||
|
||||
def primere_local_laplacian(self, image, use_local_laplacian, sigma, contrast, detail, levels):
|
||||
pil_img = utility.tensor_to_image(image)
|
||||
if use_local_laplacian:
|
||||
pil_img = img_local_laplacian.img_local_laplacian(image=pil_img, sigma=sigma, contrast=contrast, detail=detail, levels=levels)
|
||||
|
||||
return (utility.image_to_tensor(pil_img),)
|
||||
|
||||
class PrimereFrequencySeparation:
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("IMAGE",)
|
||||
FUNCTION = "primere_frequency_separation"
|
||||
CATEGORY = TREE_RASTERIX
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE", {"forceInput": True}),
|
||||
"use_frequency_separation": ("BOOLEAN", {"default": False, "label_off": "Ignore frequency separation", "label_on": "Apply frequency separation"}),
|
||||
|
||||
"radius": ("FLOAT", {"default": 3.0, "min": 0.5, "max": 20.0, "step": 0.1}),
|
||||
"low_freq_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 3.0, "step": 0.01}),
|
||||
"high_freq_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 3.0, "step": 0.01}),
|
||||
"blend_mode": (["add", "multiply", "overlay"], {"default": "add"}),
|
||||
}
|
||||
}
|
||||
|
||||
def primere_frequency_separation(self, image, use_frequency_separation, radius, low_freq_strength, high_freq_strength, blend_mode):
|
||||
pil_img = utility.tensor_to_image(image)
|
||||
if use_frequency_separation:
|
||||
pil_img = img_frequency_separation.img_frequency_separation(image=pil_img, radius=radius, low_freq_strength=low_freq_strength, high_freq_strength=high_freq_strength, blend_mode=blend_mode)
|
||||
|
||||
return (utility.image_to_tensor(pil_img),)
|
||||
@@ -80,6 +80,9 @@ NODE_CLASS_MAPPINGS = {
|
||||
"PrimereSolarizationBW": Rasterix.PrimereSolarizationBW,
|
||||
"PrimereClarity": Rasterix.PrimereClarity,
|
||||
"PrimereHistogram": Rasterix.PrimereHistogram,
|
||||
"PrimereDehaze": Rasterix.PrimereDehaze,
|
||||
"PrimereLocalLaplacian": Rasterix.PrimereLocalLaplacian,
|
||||
"PrimereFrequencySeparation": Rasterix.PrimereFrequencySeparation,
|
||||
|
||||
"PrimerePrompt": Inputs.PrimereDoublePrompt,
|
||||
"PrimereStyleLoader": Inputs.PrimereStyleLoader,
|
||||
@@ -180,6 +183,9 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"PrimereSolarizationBW": "Primere Rasterix (Solarization)",
|
||||
"PrimereClarity": "Primere Rasterix (Clarity)",
|
||||
"PrimereHistogram": "Primere Rasterix (Histogram)",
|
||||
"PrimereDehaze": "Primere Rasterix (Dehaze)",
|
||||
"PrimereLocalLaplacian": "Primere Rasterix (Local Laplacian)",
|
||||
"PrimereFrequencySeparation": "Primere Rasterix (Frequency Separation)",
|
||||
|
||||
"PrimerePrompt": "Primere Prompt",
|
||||
"PrimereStyleLoader": "Primere Styles",
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from scipy.ndimage import minimum_filter, gaussian_filter
|
||||
|
||||
|
||||
def _estimate_atmospheric_light(arr: np.ndarray) -> np.ndarray:
|
||||
flat = arr.reshape(-1, 3)
|
||||
brightest = flat[np.argmax(np.sum(flat, axis=1))]
|
||||
return brightest
|
||||
|
||||
|
||||
def img_dehaze(
|
||||
image: Image.Image,
|
||||
strength: float = 0.7,
|
||||
radius: int = 15,
|
||||
omega: float = 0.95,
|
||||
t0: float = 0.1,
|
||||
contrast: float = 1.05,
|
||||
precision: bool = False,
|
||||
) -> Image.Image:
|
||||
|
||||
img = image.convert("RGB")
|
||||
|
||||
if precision:
|
||||
max_val = 65535.0
|
||||
arr = np.array(img, dtype=np.float32) / 255.0
|
||||
arr = arr * max_val
|
||||
arr = arr / max_val
|
||||
else:
|
||||
arr = np.array(img, dtype=np.float32) / 255.0
|
||||
|
||||
dark = np.min(arr, axis=2)
|
||||
dark = minimum_filter(dark, size=radius)
|
||||
A = _estimate_atmospheric_light(arr)
|
||||
transmission = 1.0 - omega * dark
|
||||
transmission = np.clip(transmission, t0, 1.0)
|
||||
transmission = gaussian_filter(transmission, sigma=radius * 0.25)
|
||||
J = (arr - A) / transmission[..., None] + A
|
||||
out = arr * (1.0 - strength) + J * strength
|
||||
out = (out - 0.5) * contrast + 0.5
|
||||
out = np.clip(out, 0.0, 1.0)
|
||||
out = (out * 255.0).astype(np.uint8)
|
||||
|
||||
return Image.fromarray(out, mode="RGB")
|
||||
@@ -0,0 +1,37 @@
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from scipy.ndimage import gaussian_filter
|
||||
|
||||
|
||||
def img_frequency_separation(
|
||||
image: Image.Image,
|
||||
radius: float = 3.0,
|
||||
low_freq_strength: float = 1.0,
|
||||
high_freq_strength: float = 1.0,
|
||||
blend_mode: str = "add",
|
||||
) -> Image.Image:
|
||||
|
||||
img = image.convert("RGB")
|
||||
arr = np.array(img, dtype=np.float32) / 255.0
|
||||
low = gaussian_filter(arr, sigma=(radius, radius, 0))
|
||||
high = arr - low
|
||||
low_mod = low * low_freq_strength
|
||||
high_mod = high * high_freq_strength
|
||||
if blend_mode == "add":
|
||||
out = low_mod + high_mod
|
||||
elif blend_mode == "multiply":
|
||||
out = low_mod * (1.0 + high_mod)
|
||||
elif blend_mode == "overlay":
|
||||
base = low_mod
|
||||
detail = high_mod
|
||||
out = np.where(
|
||||
base <= 0.5,
|
||||
2.0 * base * (1.0 + detail),
|
||||
1.0 - 2.0 * (1.0 - base) * (1.0 - detail)
|
||||
)
|
||||
else:
|
||||
out = low_mod + high_mod
|
||||
out = np.clip(out, 0.0, 1.0)
|
||||
out = (out * 255.0).astype(np.uint8)
|
||||
|
||||
return Image.fromarray(out, mode="RGB")
|
||||
@@ -0,0 +1,34 @@
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from scipy.ndimage import gaussian_filter
|
||||
|
||||
|
||||
def _to_luminance(arr: np.ndarray) -> np.ndarray:
|
||||
return 0.299 * arr[..., 0] + 0.587 * arr[..., 1] + 0.114 * arr[..., 2]
|
||||
|
||||
|
||||
def img_local_laplacian(
|
||||
image: Image.Image,
|
||||
sigma: float = 1.0,
|
||||
contrast: float = 1.2,
|
||||
detail: float = 1.0,
|
||||
levels: int = 8,
|
||||
) -> Image.Image:
|
||||
|
||||
img = image.convert("RGB")
|
||||
arr = np.array(img, dtype=np.float32) / 255.0
|
||||
luma = _to_luminance(arr)
|
||||
base = gaussian_filter(luma, sigma=sigma)
|
||||
detail_layer = luma - base
|
||||
remapped = base + contrast * (base - 0.5)
|
||||
step = 1.0 / levels
|
||||
quantized = np.floor(luma / step) * step + step * 0.5
|
||||
tone = (remapped * 0.7 + quantized * 0.3)
|
||||
enhanced = tone + detail * detail_layer
|
||||
enhanced = np.clip(enhanced, 0.0, 1.0)
|
||||
scale = enhanced / (luma + 1e-6)
|
||||
out = arr * scale[..., None]
|
||||
out = np.clip(out, 0.0, 1.0)
|
||||
out = (out * 255.0).astype(np.uint8)
|
||||
|
||||
return Image.fromarray(out, mode="RGB")
|
||||
Reference in New Issue
Block a user