7 Commits
Author SHA1 Message Date
Gourieff | 古仁 fa813002f2 UPD: What's new sec 2026-10-10 17:32:20 +07:00
Gourieff | 古仁 e5850cc5c3 UPD: Usage sec 2026-10-10 17:11:12 +07:00
Gourieff | 古仁 5ec405e393 VersionUP
0.7.2 Beta1
2026-10-10 16:45:42 +07:00
Gourieff | 古仁 3725d4bf63 ADD: Nodes subcategories 2026-10-10 16:44:36 +07:00
Gourieff | 古仁 d7cabb2921 UPD: DLSS5 Enhancer to subcategory 2026-10-10 16:32:03 +07:00
Gourieff | 古仁 067ba6de36 Merge branch 'cinematic-enhance' into evolve 2026-10-10 16:05:46 +07:00
Gourieff | 古仁 3f66ebcba6 ADD: Cinematic Enhancer Node
v1
2026-10-10 15:54:56 +07:00
8 changed files with 361 additions and 28 deletions
+16 -5
View File
@@ -2,7 +2,7 @@
<img src="https://github.com/Gourieff/Assets/raw/main/sd-webui-reactor/ReActor_logo_NEW_EN.png?raw=true" alt="logo" width="180px"/>
![Version](https://img.shields.io/badge/node_version-0.7.1-brightgreen?style=for-the-badge&labelColor=darkgreen)
![Version](https://img.shields.io/badge/node_version-0.7.2_beta1-green?style=for-the-badge&labelColor=darkgreen)
<a href="https://boosty.to/artgourieff" target="_blank">
<img src="https://lovemet.ru/img/boosty.jpg" width="108" alt="Support Me on Boosty"/>
@@ -46,12 +46,22 @@
## What's new in the latest update
### 0.7.2 <sub><sup>BETA1</sup></sub>
- **New `Cinematic Enhancer` node**. Forget about plastic skin and boring pictures. This Node can help make any photorealistic image more soulful. You can add different effects (simple or advanced grain, vignette, blur, color correction, HDR) and apply LUT.<br><u>Requirements:</u>
- Put LUT-files in `models/luts`, you can find some collection [here](https://huggingface.co/datasets/Gourieff/ReActor/tree/main/luts)<br>
- `pip install colour-science` in your Comfy Python -OR- just run install.bat (Win users) in the ReActor's folder
*Inspired by such nodes as: Image Apply LUT ([essentials](https://github.com/cubiq/ComfyUI_essentials)), LayerFilter: Film V2 ([LayerStyle](https://github.com/chflame163/ComfyUI_LayerStyle)), Color Correction ([post-processing-nodes](https://github.com/EllangoK/ComfyUI-post-processing-nodes)), HDR Effects ([SuperBeasts](https://github.com/SuperBeastsAI/ComfyUI-SuperBeasts))*
- Nodes divided into subcategories.
### 0.7.1
- **New `DLSS5 Frame Enhancer` node**. NVIDIA's latest DLSS 5 technology for enhancing image quality (both overall frame or faces by mask). <br><u>See [Installation Instructions](https://github.com/Gourieff/ComfyUI-ReActor/blob/main/r_dlssnr/dll/README.md).</u><br><pre>The integration operates in an isolated Host mode, fully eliminating conflicts with ComfyUI's CUDA context.<br>Huge thanks to the author of the [Merserk/dlss5-visual-enhancer](https://github.com/Merserk/dlss5-visual-enhancer) project for the C++ wrapper (neuroframe_engine.dll and neuroframe_caller.dll), which formed the base of the computational bridge for this node.<br>Also thanks Gemini 3.1 Pro (via [Google Gemini](https://gemini.google.com/app)) for the contribution.</pre><u>Requirements:</u><br>
-- Windows 10/11<br>
-- NVIDIA display driver >= 616.x<br>
-- NVIDIA RTX 40/50-series GPU<br>
- **New `DLSS5 Frame Enhancer` node**. NVIDIA's latest DLSS 5 technology for enhancing image quality (both overall frame or faces by mask). <br><u>See [Installation Instructions](https://github.com/Gourieff/ComfyUI-ReActor/blob/main/r_dlssnr/dll/README.md).</u><br><pre>The integration operates in an isolated Host mode, fully eliminating conflicts with ComfyUI's CUDA context.<br>Huge thanks to the author of the [Merserk/dlss5-visual-enhancer](https://github.com/Merserk/dlss5-visual-enhancer) project for the C++ wrapper (neuroframe_engine.dll and neuroframe_caller.dll), which formed the base of the computational bridge for this node.<br>Also thanks Gemini 3.1 Pro (via [Google Gemini](https://gemini.google.com/app)) for the contribution.</pre><u>Requirements:</u>
- Windows 10/11
- NVIDIA display driver >= 616.x
- NVIDIA RTX 40/50-series GPU<br>
(compatibility with older RTX series is unconfirmed)
- Other fixes and improvements
@@ -305,6 +315,7 @@ List of Nodes:
- ImageRGBA2RGB (Convert RGBA to RGB)
- ReActorUnload (Unload ReActor models from VRAM)
- DLSS5FrameEnhancer (Enhance frame quality with NVIDIA DLSS 5)
- ReActorCinematicEnhancer (Add cinematic effects to any image)
Connect all required slots and run the query.
+16 -5
View File
@@ -2,7 +2,7 @@
<img src="https://github.com/Gourieff/Assets/raw/main/sd-webui-reactor/ReActor_logo_NEW_RU.png?raw=true" alt="logo" width="180px"/>
![Version](https://img.shields.io/badge/версия_нода-0.7.1-brightgreen?style=for-the-badge&labelColor=darkgreen)
![Version](https://img.shields.io/badge/версия_нода-0.7.2_beta1-green?style=for-the-badge&labelColor=darkgreen)
<a href="https://boosty.to/artgourieff" target="_blank">
<img src="https://lovemet.ru/img/boosty.jpg" width="108" alt="Поддержать проект на Boosty"/>
@@ -46,12 +46,22 @@
## Что нового в последнем обновлении
### 0.7.2 <sub><sup>BETA1</sup></sub>
- **Новый узел `Cinematic Enhancer`**. Забудьте о пластиковой коже и скучной картинке. Этот узел поможет вдохнуть душу в любое фотореалистичное изображение. Вы можете добавлять разные эффекты (простое и продвинутое зерно, виньетку, размытие, коррекцию цвета, HDR) и применить LUT.<br><u>Требования:</u>
- Положите LUT-файлы в `models/luts`, небольшую коллекцию можете найти [тут](https://huggingface.co/datasets/Gourieff/ReActor/tree/main/luts)<br>
- `pip install colour-science` в Пайтон вашего Comfy -ИЛИ- просто запустите install.bat (пользователи Win) внутри папки РеАктора
*Вдохновлено такими узлами как: Image Apply LUT ([essentials](https://github.com/cubiq/ComfyUI_essentials)), LayerFilter: Film V2 ([LayerStyle](https://github.com/chflame163/ComfyUI_LayerStyle)), Color Correction ([post-processing-nodes](https://github.com/EllangoK/ComfyUI-post-processing-nodes)), HDR Effects ([SuperBeasts](https://github.com/SuperBeastsAI/ComfyUI-SuperBeasts))*
- Разделение узлов на подкатегории.
### 0.7.1
- **Новый узел `DLSS5 Frame Enhancer`**. Новейшая технология DLSS 5 от NVIDIA для улучшения качества изображения (кадра целиком или лиц по маске).<br><u>Следуйте [инструкции по установке](https://github.com/Gourieff/ComfyUI-ReActor/blob/main/r_dlssnr/dll/README.md).</u><br><pre>Интеграция работает в изолированном Host-режиме, что полностью исключает конфликты с CUDA-контекстом ComfyUI.<br>Огромная благодарность автору проекта [Merserk/dlss5-visual-enhancer](https://github.com/Merserk/dlss5-visual-enhancer) за C++ обертку (neuroframe_engine.dll и neuroframe_caller.dll), которая легла в основу вычислительного моста для данного узла.<br>Также благодарность Gemini 3.1 Pro (через [Google Gemini](https://gemini.google.com/app)) за ассистирование и помощь.</pre><u>Требования:</u><br>
-- Windows 10/11<br>
-- NVIDIA display driver >= 616.x<br>
-- NVIDIA RTX 40/50-series GPU<br>
- **Новый узел `DLSS5 Frame Enhancer`**. Новейшая технология DLSS 5 от NVIDIA для улучшения качества изображения (кадра целиком или лиц по маске).<br><u>Следуйте [инструкции по установке](https://github.com/Gourieff/ComfyUI-ReActor/blob/main/r_dlssnr/dll/README.md).</u><br><pre>Интеграция работает в изолированном Host-режиме, что полностью исключает конфликты с CUDA-контекстом ComfyUI.<br>Огромная благодарность автору проекта [Merserk/dlss5-visual-enhancer](https://github.com/Merserk/dlss5-visual-enhancer) за C++ обертку (neuroframe_engine.dll и neuroframe_caller.dll), которая легла в основу вычислительного моста для данного узла.<br>Также благодарность Gemini 3.1 Pro (через [Google Gemini](https://gemini.google.com/app)) за ассистирование и помощь.</pre><u>Требования:</u>
- Windows 10/11
- NVIDIA display driver >= 616.x
- NVIDIA RTX 40/50-series GPU
(совместимость с более старыми сериями RTX не подтверждена)
- Прочие улучшение и исправления
@@ -307,6 +317,7 @@ Basic workflow [💾](https://github.com/Gourieff/Assets/blob/main/comfyui-react
- ImageRGBA2RGB (Конвертировать RGBA в RGB)
- ReActorUnload (Выгрузить модели РеАктора из VRAM)
- DLSS5FrameEnhancer (Улучшение детализации кадра с NVIDIA DLSS 5)
- ReActorCinematicEnhancer (Добавление кинематографических эффектов к изображению)
Соедините все необходимые слоты (slots) и запустите очередь (query).
+19 -16
View File
@@ -67,6 +67,7 @@ import scripts.r_masking.segs as masking_segs
import scripts.reactor_sfw as sfw
from r_dlssnr.dlss5_node import DLSS5FrameEnhancer
from r_nodes.cinematic_enhancer import ReActorCinematicEnhancer
models_dir = folder_paths.models_dir
@@ -173,7 +174,7 @@ class reactor:
RETURN_TYPES = ("IMAGE","FACE_MODEL","IMAGE")
RETURN_NAMES = ("SWAPPED_IMAGE","FACE_MODEL","ORIGINAL_IMAGE")
FUNCTION = "execute"
CATEGORY = "🌌 ReActor"
CATEGORY = "🌌 ReActor/Face Swap"
def __init__(self):
# self.face_helper = None
@@ -546,7 +547,7 @@ class ReActorPlusOpt:
RETURN_TYPES = ("IMAGE","FACE_MODEL","IMAGE")
RETURN_NAMES = ("SWAPPED_IMAGE","FACE_MODEL","ORIGINAL_IMAGE")
FUNCTION = "execute"
CATEGORY = "🌌 ReActor"
CATEGORY = "🌌 ReActor/Face Swap"
def __init__(self):
# self.face_helper = None
@@ -601,7 +602,7 @@ class LoadFaceModel:
RETURN_TYPES = ("FACE_MODEL","STRING")
RETURN_NAMES = ("FACE_MODEL","FACE_MODEL_NAME")
FUNCTION = "load_model"
CATEGORY = "🌌 ReActor"
CATEGORY = "🌌 ReActor/Face Swap"
def load_model(self, face_model):
self.face_model = face_model
@@ -635,7 +636,7 @@ class ReActorWeight:
OUTPUT_NODE = True
CATEGORY = "🌌 ReActor"
CATEGORY = "🌌 ReActor/Face Swap"
def set_weight(self, input_image, faceswap_weight, face_model=None, source_image=None):
@@ -739,7 +740,7 @@ class BuildFaceModel:
OUTPUT_NODE = True
CATEGORY = "🌌 ReActor"
CATEGORY = "🌌 ReActor/Face Swap"
def build_face_model(self, image: Image.Image, det_size=(640, 640)):
logging.StreamHandler.terminator = "\n"
@@ -869,7 +870,7 @@ class SaveFaceModel:
OUTPUT_NODE = True
CATEGORY = "🌌 ReActor"
CATEGORY = "🌌 ReActor/Face Swap"
def save_model(self, save_mode, face_model_name, select_face_index, image=None, face_model=None, det_size=(640, 640)):
if save_mode and image is not None:
@@ -910,7 +911,7 @@ class RestoreFace:
RETURN_TYPES = ("IMAGE",)
FUNCTION = "execute"
CATEGORY = "🌌 ReActor"
CATEGORY = "🌌 ReActor/PostProcessing"
def execute(self, image, model, visibility, codeformer_weight, facedetection):
result = reactor.restore_face(
@@ -941,7 +942,7 @@ class RestoreFaceAdvanced:
RETURN_TYPES = ("IMAGE",)
FUNCTION = "execute"
CATEGORY = "🌌 ReActor"
CATEGORY = "🌌 ReActor/PostProcessing"
def execute(
self, image, model, visibility, codeformer_weight, facedetection, face_selection, sort_by="area", reverse_order=False, take_start=0, take_count=1
@@ -1224,7 +1225,7 @@ class MaskHelper:
RETURN_TYPES = ("IMAGE","MASK","IMAGE","IMAGE")
RETURN_NAMES = ("IMAGE","MASK","MASK_PREVIEW","SWAPPED_FACE")
FUNCTION = "execute"
CATEGORY = "🌌 ReActor"
CATEGORY = "🌌 ReActor/Masking"
def execute(self, image, swapped_image, bbox_model_name, bbox_threshold, bbox_dilation, bbox_crop_factor, bbox_drop_size, sam_model_name, sam_dilation, sam_threshold, bbox_expansion, mask_hint_threshold, mask_hint_use_negative, morphology_operation, morphology_distance, blur_radius, sigma_factor, mask_optional=None):
device = model_management.get_torch_device()
@@ -1527,7 +1528,7 @@ class ImageDublicator:
RETURN_NAMES = ("IMAGES",)
OUTPUT_IS_LIST = (True,)
FUNCTION = "execute"
CATEGORY = "🌌 ReActor"
CATEGORY = "🌌 ReActor/Utils"
def execute(self, image, count):
images = [image for i in range(count)]
@@ -1545,7 +1546,7 @@ class ImageRGBA2RGB:
RETURN_TYPES = ("IMAGE",)
FUNCTION = "execute"
CATEGORY = "🌌 ReActor"
CATEGORY = "🌌 ReActor/Utils"
def execute(self, image):
out = rgba2rgb_tensor(image)
@@ -1576,7 +1577,7 @@ class MakeFaceModelBatch:
RETURN_NAMES = ("FACE_MODELS",)
FUNCTION = "execute"
CATEGORY = "🌌 ReActor"
CATEGORY = "🌌 ReActor/Face Swap"
def execute(self, **kwargs):
if len(kwargs) > 0:
@@ -1609,7 +1610,7 @@ class ReActorOptions:
RETURN_TYPES = ("OPTIONS",)
FUNCTION = "execute"
CATEGORY = "🌌 ReActor"
CATEGORY = "🌌 ReActor/Face Swap"
def execute(self,input_faces_order, input_faces_index, detect_gender_input, source_faces_order, source_faces_index, detect_gender_source, console_log_level, restore_swapped_only):
options: dict = {
@@ -1641,7 +1642,7 @@ class ReActorFaceBoost:
RETURN_TYPES = ("FACE_BOOST",)
FUNCTION = "execute"
CATEGORY = "🌌 ReActor"
CATEGORY = "🌌 ReActor/Face Swap"
def execute(self,enabled,boost_model,interpolation,visibility,codeformer_weight,restore_with_main_after):
face_boost: dict = {
@@ -1666,7 +1667,7 @@ class ReActorUnload:
RETURN_TYPES = ("IMAGE",)
FUNCTION = "execute"
CATEGORY = "🌌 ReActor"
CATEGORY = "🌌 ReActor/Utils"
def execute(self, trigger):
unload_all_models()
@@ -1686,7 +1687,7 @@ class ReActorFaceSimilarity:
RETURN_TYPES = ("FLOAT", "STRING")
RETURN_NAMES = ("similarity_float", "similarity_text")
FUNCTION = "compare_faces"
CATEGORY = "🌌 ReActor"
CATEGORY = "🌌 ReActor/Utils"
def compare_faces(self, image1, image2):
@@ -1747,6 +1748,7 @@ NODE_CLASS_MAPPINGS = {
"ImageRGBA2RGB": ImageRGBA2RGB,
"ReActorUnload": ReActorUnload,
"DLSS5FrameEnhancer": DLSS5FrameEnhancer,
"ReActorCinematicEnhancer": ReActorCinematicEnhancer,
}
NODE_DISPLAY_NAME_MAPPINGS = {
@@ -1770,4 +1772,5 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"ImageRGBA2RGB": "Convert RGBA to RGB 🌌 ReActor",
"ReActorUnload": "Unload ReActor Models 🌌 ReActor",
"DLSS5FrameEnhancer": "DLSS5 Frame Enhancer 🌌 ReActor",
"ReActorCinematicEnhancer": "Cinematic Enhancer 🌌 ReActor",
}
+1 -1
View File
@@ -41,7 +41,7 @@ class DLSS5FrameEnhancer:
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("enhanced_image",)
FUNCTION = "enhance"
CATEGORY = "🌌 ReActor"
CATEGORY = "🌌 ReActor/PostProcessing"
DESCRIPTION = (
"Requirements:\n"
"- NVIDIA display driver >= 616.x\n"
View File
+307
View File
@@ -0,0 +1,307 @@
import os
import torch
import numpy as np
import cv2
import folder_paths
from pathlib import Path
from PIL import Image, ImageEnhance, ImageCms
import comfy.model_management as model_management
from scripts.reactor_logger import logger
from r_modules.shared import state
from reactor_utils import (
progress_bar,
progress_bar_reset
)
try:
from colour.io.luts.iridas_cube import read_LUT_IridasCube
AVAILABLE = True
except Exception as e:
AVAILABLE = False
logger.error(f"{e}, ReActorCinematicEnhancer is not available, please install `colour` library: `pip install colour-science`")
folder_paths.add_model_folder_path("luts", os.path.join(folder_paths.models_dir, "luts"))
# Профили для HDR (создаются один раз при импорте для экономии ресурсов)
sRGB_profile = ImageCms.createProfile('sRGB')
Lab_profile = ImageCms.createProfile('LAB')
def get_luts():
luts = folder_paths.get_filename_list("luts")
luts = [x for x in luts if x.lower().endswith(".cube") or x.lower().endswith(".spi1d") or x.lower().endswith(".spi3d") or x.lower().endswith(".spimtx") or x.lower().endswith(".csp")]
luts.sort(key=str.lower)
luts.insert(0, "none")
return luts
class ReActorCinematicEnhancer:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image": ("IMAGE", ),
# --- Global Blend ---
"intensity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "0..1, def: 0.5"}),
# --- LUT ---
"lut_file": (get_luts(),),
"lut_strength": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.1, "tooltip": "0..1, def: 0.5"}),
"lut_gamma_correction": ("BOOLEAN", { "default": True }),
# --- Film Effect ---
"film_grain_method": (["advanced","fast"], {"default": "advanced"}),
"film_grain_power": ("FLOAT", {"default": 0.1, "min": 0, "max": 1, "step": 0.01, "tooltip": "0..1, def: 0.1"}),
"film_grain_scale": ("FLOAT", {"default": 1, "min": 0.1, "max": 10, "step": 0.1, "tooltip": "0.1..1, def: 0.1"}),
"film_grain_sat": ("FLOAT", {"default": 0.15, "min": 0, "max": 1, "step": 0.01, "tooltip": "(advanced only) 0..1, def: 0.15"}),
"film_grain_shadows": ("FLOAT", {"default": 0.2, "min": 0, "max": 1, "step": 0.01, "tooltip": "(advanced only) 0..1, def: 0.2"}),
"film_grain_highs": ("FLOAT", {"default": 0.2, "min": 0, "max": 1, "step": 0.01, "tooltip": "(advanced only) 0..1, def: 0.2"}),
"film_blur_strength": ("INT", {"default": 1, "min": 0, "max": 256, "step": 1, "tooltip": "(by depth_map) 0..256, def: 1"}),
"film_focal_depth": ("FLOAT", {"default": 0.9, "min": 0.0, "max": 1, "step": 0.01, "tooltip": "(by depth_map) 0..1, def: 0.9"}),
"film_focus_spread": ("FLOAT", {"default": 2.2, "min": 0.1, "max": 8, "step": 0.1, "tooltip": "(by depth_map) 0..8, def: 2.2"}),
"film_vignette_intensity": ("FLOAT", {"default": 0.1, "min": 0, "max": 1, "step": 0.01, "tooltip": "0..1, def: 0.1"}),
# --- Color Correct ---
"cc_temperature": ("FLOAT", {"default": 0, "min": -100, "max": 100, "step": 5, "tooltip": "-100..100, def: 0"}),
"cc_hue": ("FLOAT", {"default": 0, "min": -90, "max": 90, "step": 5, "tooltip": "-90..90, def: 0"}),
"cc_brightness": ("FLOAT", {"default": 0, "min": -100, "max": 100, "step": 5, "tooltip": "-100..100, def: 0"}),
"cc_contrast": ("FLOAT", {"default": 0, "min": -100, "max": 100, "step": 5, "tooltip": "-100..100, def: 0"}),
"cc_saturation": ("FLOAT", {"default": 0, "min": -100, "max": 100, "step": 5, "tooltip": "-100..100, def: 0"}),
"cc_gamma": ("FLOAT", {"default": 1, "min": 0.2, "max": 2.2, "step": 0.1, "tooltip": "0.2..2.2, def: 1.0"}),
# --- HDR Effects ---
"hdr_intensity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 5.0, "step": 0.01, "tooltip": "0..5, def: 0.5"}),
"hdr_shadow_intensity": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "0..1, def: 0.25"}),
"hdr_highlight_intensity": ("FLOAT", {"default": 0.75, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "0..1, def: 0.75"}),
"hdr_contrast": ("FLOAT", {"default": 0.1, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "0..1, def: 0.1"}),
"hdr_enhance_color": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "0..1, def: 0.25"}),
},
"optional": {
"depth_map": ("IMAGE",),
}
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("image",)
FUNCTION = "execute"
CATEGORY = "🌌 ReActor/PostProcessing"
DESCRIPTION = (
"Requirements:\n"
"- pip install colour-science\n"
"- put LUT-files in models/luts\n"
"depth_map is optional"
)
def execute(self, image, intensity, lut_file, lut_strength, lut_gamma_correction,
film_grain_method, film_grain_power, film_grain_scale, film_grain_sat, film_grain_shadows, film_grain_highs,film_blur_strength, film_focal_depth, film_focus_spread, film_vignette_intensity,
cc_temperature, cc_hue, cc_brightness, cc_contrast, cc_saturation, cc_gamma,
hdr_intensity, hdr_shadow_intensity, hdr_highlight_intensity, hdr_contrast, hdr_enhance_color,
depth_map=None):
if not AVAILABLE:
logger.error("ReActorCinematicEnhancer is not available, please install `colour` library: `pip install colour-science`")
return (image,)
# 1. Загрузка LUT
if lut_file != "none":
lut_file_path = folder_paths.get_full_path("luts", lut_file)
lut = read_LUT_IridasCube(lut_file_path) if lut_file_path and Path(lut_file_path).exists() else None
if lut:
lut.name = lut_file
logger.status(f"LUT {lut_file} selected")
else:
lut = None
batch_size = image.shape[0]
result_batch = []
pbar = progress_bar(batch_size)
logger.status(f"Enhancing...")
for b in range(batch_size):
if state.interrupted or model_management.processing_interrupted():
logger.status("Interrupted by User")
break
# --- БАЗА: NumPy (0-1) ---
orig_np = image[b].cpu().numpy().copy()
img_np = orig_np.copy()
# --- 1. LUT ---
if lut:
lut_img = img_np.copy()
if lut_gamma_correction:
lut_img = np.clip(lut_img ** (1/2.2), 0, 1)
lut_img = lut.apply(lut_img)
if lut_gamma_correction:
lut_img = np.clip(lut_img ** 2.2, 0, 1)
if lut_strength < 1.0:
img_np = lut_strength * lut_img + (1 - lut_strength) * img_np
else:
img_np = lut_img
# Подготовка маски глубины (если есть)
depth_np = None
if depth_map is not None:
d_tensor = depth_map[b if depth_map.shape[0] > 1 else 0].cpu().numpy()
if d_tensor.shape[:2] != img_np.shape[:2]:
d_tensor = cv2.resize(d_tensor, (img_np.shape[1], img_np.shape[0]), interpolation=cv2.INTER_LINEAR)
# Если глубина RGB, берем один канал
depth_np = d_tensor[:,:,0] if d_tensor.ndim == 3 else d_tensor
# --- 2. FILM EFFECT ---
# Виньетка (OpenCV)
if film_vignette_intensity > 0:
h, w = img_np.shape[:2]
Y, X = np.ogrid[:h, :w]
center_y, center_x = h / 2, w / 2
dist_from_center = np.sqrt((X - center_x)**2 + (Y - center_y)**2)
max_dist = np.sqrt(center_x**2 + center_y**2)
vignette = 1 - (dist_from_center / max_dist) * film_vignette_intensity
img_np = np.clip(img_np * vignette[..., np.newaxis], 0, 1)
# Глубинное размытие (пошаговая реализация)
if film_blur_strength > 0 and depth_np is not None:
# Нормализуем разницу глубины относительно точки фокуса
depth_diff = np.abs(depth_np - film_focal_depth) * film_focus_spread
depth_diff = np.clip(depth_diff, 0, 1)
# Пошаговый блендинг (5 шагов для плавности)
steps = 5
blurred_fg = np.zeros_like(img_np)
for step in range(steps + 1):
step_blur = int((film_blur_strength * step / steps) // 2 * 2 + 1)
if step_blur > 1:
blur_layer = cv2.GaussianBlur(img_np, (step_blur, step_blur), 0)
else:
blur_layer = img_np
# Маска весов для текущего шага
mask = np.clip(1.0 - np.abs(depth_diff * steps - step), 0, 1)
blurred_fg += blur_layer * mask[..., np.newaxis]
img_np = blurred_fg
# --- 3. COLOR CORRECT ---
fg_img = Image.fromarray((img_np * 255).astype(np.uint8))
# Яркость и Контраст (PIL)
if cc_brightness != 0:
fg_img = ImageEnhance.Brightness(fg_img).enhance(1 + (cc_brightness / 100))
if cc_contrast != 0:
fg_img = ImageEnhance.Contrast(fg_img).enhance(1 + (cc_contrast / 100))
fg_cv = np.array(fg_img).astype(np.float32) / 255.0
# Температура
temp_val = cc_temperature / 100
if temp_val > 0:
fg_cv[:, :, 0] = np.clip(fg_cv[:, :, 0] * (1 + temp_val), 0, 1)
fg_cv[:, :, 1] = np.clip(fg_cv[:, :, 1] * (1 + temp_val * 0.4), 0, 1)
elif temp_val < 0:
fg_cv[:, :, 2] = np.clip(fg_cv[:, :, 2] * (1 - temp_val), 0, 1)
# Гамма
if cc_gamma != 1.0:
fg_cv = np.clip(np.power(fg_cv, cc_gamma), 0, 1)
# HSV для Hue и Saturation
if cc_hue != 0 or cc_saturation != 0:
hsv = cv2.cvtColor((fg_cv * 255).astype(np.uint8), cv2.COLOR_RGB2HSV).astype(np.float32)
if cc_hue != 0:
hsv[:, :, 0] = (hsv[:, :, 0] + (cc_hue / 2)) % 180 # В OpenCV Hue до 180
if cc_saturation != 0:
hsv[:, :, 1] = np.clip(hsv[:, :, 1] * (1 + cc_saturation / 100), 0, 255)
fg_cv = cv2.cvtColor(hsv.astype(np.uint8), cv2.COLOR_HSV2RGB).astype(np.float32) / 255.0
img_np = fg_cv
# --- 4. FILM GRAIN ---
if film_grain_power > 0:
# --- 4.1 ADVANCED NATIVE GRAIN ---
if film_grain_method == "advanced":
# 1) Генерируем базовый цветной шум
noise_rgb = np.random.normal(0, film_grain_power, img_np.shape).astype(np.float32)
# 2) Генерируем монохромный шум (усреднение по каналам)
noise_mono = np.mean(noise_rgb, axis=-1, keepdims=True)
# 3) Смешиваем цветность зерна (grain_sat)
noise = noise_rgb * film_grain_sat + noise_mono * (1.0 - film_grain_sat)
# 4) Масштабирование зерна
if film_grain_scale != 1.0:
h, w = img_np.shape[:2]
small_noise = cv2.resize(noise, (int(w / film_grain_scale), int(h / film_grain_scale)))
noise = cv2.resize(small_noise, (w, h), interpolation=cv2.INTER_LINEAR)
# 5) Кинематографичная маскировка по яркости (Luminance)
# Вычисляем воспринимаемую яркость пикселей
lum = 0.299 * img_np[:,:,0] + 0.587 * img_np[:,:,1] + 0.114 * img_np[:,:,2]
lum = lum[..., np.newaxis]
# Создаем карту весов шума:
# Средние тона (0.5) получают вес 1.0.
# Тени (0.0) получают вес film_grain_shadows.
# Блики (1.0) получают вес film_grain_highs.
grain_mask = np.where(
lum < 0.5,
film_grain_shadows + (1.0 - film_grain_shadows) * (lum * 2.0), # Плавный переход от теней к средним
1.0 - (1.0 - film_grain_highs) * ((lum - 0.5) * 2.0) # Плавный переход от средних к бликам
)
# Накладываем шум с учетом яркостной маски
img_np = np.clip(img_np + noise * grain_mask, 0, 1)
# --- 4.2 FAST GRAIN ---
else:
noise = np.random.normal(0, film_grain_power, img_np.shape).astype(np.float32)
# Масштабирование шума
if film_grain_scale != 1.0:
h, w = img_np.shape[:2]
small_noise = cv2.resize(noise, (int(w / film_grain_scale), int(h / film_grain_scale)))
noise = cv2.resize(small_noise, (w, h), interpolation=cv2.INTER_LINEAR)
img_np = np.clip(img_np + noise * 0.5, 0, 1)
# --- 5. HDR EFFECTS ---
if hdr_intensity > 0:
final_pil = Image.fromarray((img_np * 255).astype(np.uint8))
# Основная математика тонирования LAB
img_lab = ImageCms.profileToProfile(final_pil, sRGB_profile, Lab_profile, outputMode='LAB')
luminance, a, b = img_lab.split()
base = np.array(luminance, dtype=np.float32)
scaled_shadow = (hdr_shadow_intensity ** 2) * hdr_intensity
scaled_highlight = (hdr_highlight_intensity ** 2) * hdr_intensity
shadow_mask = np.clip((1 - (base / 255)) ** 2, 0, 1)
highlight_mask = np.clip((base / 255) ** 2, 0, 1)
adj_shadows = np.clip(base * (1 - shadow_mask * scaled_shadow), 0, 255)
adj_highlights = np.clip(base + (255 - base) * highlight_mask * scaled_highlight, 0, 255)
adj_lum = np.clip(adj_shadows + adj_highlights - base, 0, 255)
final_lum = np.clip(base * (1 - hdr_intensity) + adj_lum * hdr_intensity, 0, 255).astype(np.uint8)
merged_lab = Image.merge('LAB', (Image.fromarray(final_lum), a, b))
final_pil = ImageCms.profileToProfile(merged_lab, Lab_profile, sRGB_profile, outputMode='RGB')
if hdr_contrast > 0:
final_pil = ImageEnhance.Contrast(final_pil).enhance(1 + hdr_contrast)
if hdr_enhance_color > 0:
final_pil = ImageEnhance.Color(final_pil).enhance(1 + hdr_enhance_color * 0.2)
# Возвращаем HDR в рабочий массив для финального смешивания
img_np = np.array(final_pil).astype(np.float32) / 255.0
# --- 6. GLOBAL INTENSITY BLEND ---
if intensity < 1.0:
img_np = orig_np * (1.0 - intensity) + img_np * intensity
# Возврат в тензор
out_tensor = torch.from_numpy(img_np)
result_batch.append(out_tensor)
pbar.update(1)
progress_bar_reset(pbar)
return (torch.stack(result_batch),)
+1
View File
@@ -4,3 +4,4 @@ opencv-python>=4.7.0.72
numpy
segment_anything
ultralytics
colour-science
+1 -1
View File
@@ -1,5 +1,5 @@
app_title = "ReActor Node for ComfyUI"
version_flag = "v0.7.1"
version_flag = "v0.7.2-b1"
COLORS = {
"CYAN": "\033[0;36m", # CYAN