Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
842f640842 | ||
|
|
2f26cbe060 | ||
|
|
f2b7248275 | ||
|
|
c37bac0e9d | ||
|
|
70efc7a0fe | ||
|
|
0943a3339e | ||
|
|
82bd53c6cb |
@@ -0,0 +1,57 @@
|
||||
name: Publish to Comfy registry
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
paths:
|
||||
- "pyproject.toml"
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
publish-node:
|
||||
name: Publish Custom Node to registry
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'EricRollei' }}
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Extract version from pyproject.toml
|
||||
id: version
|
||||
run: |
|
||||
VERSION=$(grep -m1 'version = ' pyproject.toml | cut -d'"' -f2)
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Publish Custom Node
|
||||
id: publish
|
||||
uses: Comfy-Org/publish-node-action@v1
|
||||
with:
|
||||
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
|
||||
|
||||
- name: Create success issue comment
|
||||
if: success()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const version = '${{ steps.version.outputs.version }}';
|
||||
console.log(`✅ Successfully published version ${version} to ComfyUI Registry`);
|
||||
|
||||
- name: Notify on failure
|
||||
if: failure()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const version = '${{ steps.version.outputs.version }}';
|
||||
github.rest.issues.create({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
title: `🚨 Registry publish failed for v${version}`,
|
||||
body: `The automated publish to ComfyUI Registry failed.\n\n**Version:** ${version}\n**Workflow run:** ${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}\n\nPlease check the workflow logs for details.`,
|
||||
labels: ['bug', 'automation']
|
||||
});
|
||||
@@ -35,6 +35,14 @@ from Eric_Image_Processing_Nodes import (
|
||||
create_gaussian_psf
|
||||
)
|
||||
|
||||
# Try to import GPU version
|
||||
try:
|
||||
from ..scripts.richardson_lucy_gpu import richardson_lucy_deconvolution_gpu
|
||||
GPU_RL_AVAILABLE = True
|
||||
except ImportError:
|
||||
GPU_RL_AVAILABLE = False
|
||||
richardson_lucy_deconvolution_gpu = None
|
||||
|
||||
class RichardsonLucyNode(BaseImageProcessingNode):
|
||||
"""
|
||||
Richardson-Lucy deconvolution for advanced image restoration
|
||||
@@ -45,7 +53,7 @@ class RichardsonLucyNode(BaseImageProcessingNode):
|
||||
- Astronomical image restoration
|
||||
- Microscopy deconvolution
|
||||
|
||||
Performance: Medium speed, CPU-based, iterative algorithm
|
||||
Performance: GPU-accelerated (10-50x faster) with CPU fallback
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
@@ -115,6 +123,16 @@ class RichardsonLucyNode(BaseImageProcessingNode):
|
||||
"estimate_motion": ("BOOLEAN", {
|
||||
"default": False,
|
||||
"tooltip": "Auto-estimate motion blur parameters (experimental)"
|
||||
}),
|
||||
|
||||
# GPU acceleration
|
||||
"use_gpu": ("BOOLEAN", {
|
||||
"default": True,
|
||||
"tooltip": "Use GPU acceleration (10-50x faster):\n• True: Use CUDA/MPS if available\n• False: Force CPU processing"
|
||||
}),
|
||||
"precision": (["fp32", "fp16"], {
|
||||
"default": "fp32",
|
||||
"tooltip": "GPU precision (fp16 uses less VRAM, CUDA only)"
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -135,7 +153,9 @@ class RichardsonLucyNode(BaseImageProcessingNode):
|
||||
regularization=0.005,
|
||||
clip_output=True,
|
||||
use_preset="none",
|
||||
estimate_motion=False
|
||||
estimate_motion=False,
|
||||
use_gpu=True,
|
||||
precision="fp32"
|
||||
):
|
||||
"""Apply Richardson-Lucy deconvolution to input image"""
|
||||
|
||||
@@ -163,22 +183,40 @@ class RichardsonLucyNode(BaseImageProcessingNode):
|
||||
motion_angle = estimated_angle
|
||||
motion_length = estimated_length
|
||||
|
||||
# Define processing function
|
||||
# Define processing function - use GPU if available and requested
|
||||
can_use_gpu = use_gpu and GPU_RL_AVAILABLE
|
||||
|
||||
def process_func(img_np, **kwargs):
|
||||
return richardson_lucy_deconvolution(
|
||||
img_np,
|
||||
psf=None, # Generate PSF automatically
|
||||
iterations=iterations,
|
||||
clip=clip_output,
|
||||
blur_type=blur_type,
|
||||
blur_size=blur_size,
|
||||
motion_angle=motion_angle,
|
||||
motion_length=motion_length,
|
||||
regularization=regularization
|
||||
)
|
||||
if can_use_gpu:
|
||||
return richardson_lucy_deconvolution_gpu(
|
||||
img_np,
|
||||
psf=None,
|
||||
iterations=iterations,
|
||||
clip=clip_output,
|
||||
blur_type=blur_type,
|
||||
blur_size=blur_size,
|
||||
motion_angle=motion_angle,
|
||||
motion_length=motion_length,
|
||||
regularization=regularization,
|
||||
device="auto",
|
||||
precision=precision
|
||||
)
|
||||
else:
|
||||
return richardson_lucy_deconvolution(
|
||||
img_np,
|
||||
psf=None, # Generate PSF automatically
|
||||
iterations=iterations,
|
||||
clip=clip_output,
|
||||
blur_type=blur_type,
|
||||
blur_size=blur_size,
|
||||
motion_angle=motion_angle,
|
||||
motion_length=motion_length,
|
||||
regularization=regularization
|
||||
)
|
||||
|
||||
# Process image safely
|
||||
print(f"Richardson-Lucy: {blur_type} blur, {iterations} iterations, regularization={regularization}")
|
||||
device_str = "GPU" if can_use_gpu else "CPU"
|
||||
print(f"Richardson-Lucy ({device_str}): {blur_type} blur, {iterations} iterations, regularization={regularization}")
|
||||
result = self.process_image_safe(image, process_func)
|
||||
|
||||
return (result,)
|
||||
@@ -210,6 +248,10 @@ class RichardsonLucySimpleNode(BaseImageProcessingNode):
|
||||
"motion_direction": (["horizontal", "vertical", "diagonal"], {
|
||||
"default": "horizontal",
|
||||
"tooltip": "Motion direction (for motion_blur type only)"
|
||||
}),
|
||||
"use_gpu": ("BOOLEAN", {
|
||||
"default": True,
|
||||
"tooltip": "Use GPU acceleration (much faster). Falls back to CPU if unavailable."
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -219,10 +261,16 @@ class RichardsonLucySimpleNode(BaseImageProcessingNode):
|
||||
FUNCTION = "restore_simple"
|
||||
CATEGORY = "Eric's Image Processing/Restoration"
|
||||
|
||||
def restore_simple(self, image, correction_type="lens_defocus", strength="medium", motion_direction="horizontal"):
|
||||
def restore_simple(self, image, correction_type="lens_defocus", strength="medium",
|
||||
motion_direction="horizontal", use_gpu=True):
|
||||
"""Simple Richardson-Lucy restoration with automatic parameters"""
|
||||
|
||||
try:
|
||||
# Check if GPU is available
|
||||
can_use_gpu = use_gpu and RICHARDSON_LUCY_GPU_AVAILABLE
|
||||
if use_gpu and not RICHARDSON_LUCY_GPU_AVAILABLE:
|
||||
print("GPU acceleration requested but not available, using CPU")
|
||||
|
||||
# Parameter mapping
|
||||
strength_params = {
|
||||
"light": {"iterations": 8, "blur_factor": 0.7, "reg": 0.002},
|
||||
@@ -254,6 +302,35 @@ class RichardsonLucySimpleNode(BaseImageProcessingNode):
|
||||
motion_length = 12.0 * params["blur_factor"]
|
||||
|
||||
def process_func(img_np, **kwargs):
|
||||
if can_use_gpu:
|
||||
# Use GPU acceleration
|
||||
try:
|
||||
if correction_type == "motion_blur":
|
||||
return richardson_lucy_deconvolution_gpu(
|
||||
img_np,
|
||||
iterations=params["iterations"],
|
||||
blur_type="motion",
|
||||
motion_angle=motion_angle,
|
||||
motion_length=motion_length,
|
||||
regularization=params["reg"],
|
||||
device="auto",
|
||||
precision="fp32"
|
||||
)
|
||||
else:
|
||||
return richardson_lucy_deconvolution_gpu(
|
||||
img_np,
|
||||
iterations=params["iterations"],
|
||||
blur_type="gaussian",
|
||||
blur_size=blur_size,
|
||||
regularization=params["reg"],
|
||||
device="auto",
|
||||
precision="fp32"
|
||||
)
|
||||
except Exception as gpu_err:
|
||||
print(f"GPU processing failed, falling back to CPU: {gpu_err}")
|
||||
# Fall through to CPU
|
||||
|
||||
# CPU path
|
||||
if correction_type == "motion_blur":
|
||||
return richardson_lucy_deconvolution(
|
||||
img_np,
|
||||
@@ -272,7 +349,8 @@ class RichardsonLucySimpleNode(BaseImageProcessingNode):
|
||||
regularization=params["reg"]
|
||||
)
|
||||
|
||||
print(f"Simple RL: {correction_type} ({strength})")
|
||||
device_str = "GPU" if can_use_gpu else "CPU"
|
||||
print(f"Simple RL ({device_str}): {correction_type} ({strength})")
|
||||
result = self.process_image_safe(image, process_func)
|
||||
|
||||
return (result,)
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
[project]
|
||||
name = "comfyui-eric-image-processing-nodes"
|
||||
description = "A comprehensive collection of 40+ advanced image processing nodes for ComfyUI, featuring state-of-the-art denoising (BM3D, DnCNN, SCUNet, SwinIR), enhancement, and restoration techniques with GPU acceleration and specialized film grain processing."
|
||||
version = "1.0.0"
|
||||
license = "LicenseRef-Proprietary"
|
||||
readme = "Docs/README.md"
|
||||
requires-python = ">=3.10"
|
||||
authors = [
|
||||
{ name = "Eric Hiss", email = "eric@historic.camera" }
|
||||
]
|
||||
maintainers = [
|
||||
{ name = "Eric Hiss", email = "eric@historic.camera" }
|
||||
]
|
||||
keywords = [
|
||||
"comfyui",
|
||||
"image-processing",
|
||||
"denoising",
|
||||
"bm3d",
|
||||
"dncnn",
|
||||
"scunet",
|
||||
"swinir",
|
||||
"film-grain",
|
||||
"restoration",
|
||||
"gpu-acceleration",
|
||||
"wavelet",
|
||||
"richardson-lucy"
|
||||
]
|
||||
classifiers = [
|
||||
"Operating System :: OS Independent",
|
||||
"Environment :: GPU :: NVIDIA CUDA",
|
||||
"Environment :: GPU :: Apple Metal",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Topic :: Scientific/Engineering :: Image Processing",
|
||||
"Topic :: Multimedia :: Graphics :: Graphics Conversion",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"Intended Audience :: Science/Research"
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
"numpy>=1.21.0",
|
||||
"opencv-python>=4.5.0",
|
||||
"scikit-image>=0.19.0",
|
||||
"scipy>=1.7.0",
|
||||
"PyWavelets>=1.3.0",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
visualization = ["matplotlib>=3.3.0"]
|
||||
diffbir = ["facexlib>=0.3.0"]
|
||||
dev = [
|
||||
"pytest>=6.0.0",
|
||||
"black>=21.0.0",
|
||||
"flake8>=3.9.0",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/EricRollei/Eric_Image_Processing_Nodes"
|
||||
Repository = "https://github.com/EricRollei/Eric_Image_Processing_Nodes"
|
||||
Documentation = "https://github.com/EricRollei/Eric_Image_Processing_Nodes/blob/main/Docs/README.md"
|
||||
"Bug Tracker" = "https://github.com/EricRollei/Eric_Image_Processing_Nodes/issues"
|
||||
"Model Weights" = "https://github.com/EricRollei/Eric_Image_Processing_Nodes/blob/main/Docs/MODEL_WEIGHTS.md"
|
||||
|
||||
[tool.comfy]
|
||||
PublisherId = "ericrollei"
|
||||
DisplayName = "Eric's Image Processing Nodes"
|
||||
Icon = ""
|
||||
includes = []
|
||||
|
||||
[tool.comfy.metadata]
|
||||
description = "40+ advanced image processing nodes: BM3D/DnCNN/SCUNet/SwinIR denoising, wavelet processing, film grain analysis, Richardson-Lucy deconvolution, and more. GPU accelerated with CPU fallback."
|
||||
tags = ["denoising", "restoration", "enhancement", "film-grain", "gpu", "bm3d", "wavelet"]
|
||||
|
||||
Reference in New Issue
Block a user