Add files via upload

This commit is contained in:
A+43
2025-06-12 14:51:32 +02:00
committed by GitHub
commit 46e69b617c
12 changed files with 742 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
# Changelog
## [1.0.1] - 2025-06-12
- Initial release of ComfyUI ASDF Pixel Sort Nodes
+2
View File
@@ -0,0 +1,2 @@
# Contributing to ComfyUI ASDF Pixel Sort
We welcome contributions! Please follow these guidelines:
+29
View File
@@ -0,0 +1,29 @@
MIT License
Copyright (c) 2024 ComfyUI ASDF Pixel Sort Integration
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
This project integrates the ASDFPixelSort algorithm by Kim Asendorf (2010).
Original ASDFPixelSort: http://kimasendorf.com
The original ASDFPixelSort algorithm and Processing code are used under their
respective licenses and terms of use.
+128
View File
@@ -0,0 +1,128 @@
# ComfyUI ASDF Pixel Sort Nodes
[![ComfyUI](https://img.shields.io/badge/ComfyUI-Compatible-brightgreen)](https://github.com/comfyanonymous/ComfyUI)
[![Processing](https://img.shields.io/badge/Processing-4.3-blue)](https://processing.org)
[![License](https://img.shields.io/badge/License-MIT-yellow)](LICENSE)
[![Version](https://img.shields.io/badge/Version-1.0.0-orange)](CHANGELOG.md)
A ComfyUI integration of Kim Asendorf's iconic **ASDFPixelSort** algorithm, bringing classic pixel sorting effects directly into your ComfyUI workflows.
## ✨ Features
- 🎨 **Authentic Algorithm**: Uses original Kim Asendorf ASDFPixelSort Processing script
- 🔄 **Four Sorting Modes**: White, Black, Bright, and Dark threshold-based sorting
- 🔗 **Seamless Integration**: Native ComfyUI tensor handling and workflow compatibility
- ⚙️ **Full Control**: Complete parameter customization for all sorting thresholds
- 🖥️ **Headless Ready**: Virtual display support for server environments
- 📦 **Easy Install**: Automated setup script for all dependencies
## 🚀 Quick Start
### Installation
1. **Clone this repository** into your ComfyUI custom_nodes directory:
```bash
cd /path/to/ComfyUI/custom_nodes/
git clone https://github.com/your-username/ComfyUI-ASDF-PixelSort.git
```
2. **Install dependencies** (automated):
```bash
cd ComfyUI-ASDF-PixelSort
chmod +x scripts/install.sh
sudo ./scripts/install.sh
```
3. **Restart ComfyUI** to load the new nodes
### Basic Usage
1. Add **LoadImage** node → **Pixel Sort (ASDF)** → **SaveImage**
2. Configure sorting mode and thresholds
3. Queue prompt to process
## 🎛️ Available Nodes
### Pixel Sort (ASDF)
**Location**: `Add Node` → `image` → `effects` → `Pixel Sort (ASDF)`
**Parameters**:
- **Mode**: `white` | `black` | `bright` | `dark`
- **White Threshold**: `-16777216` to `0` (default: `-12345678`)
- **Black Threshold**: `-16777216` to `0` (default: `-3456789`)
- **Bright Threshold**: `0` to `255` (default: `127`)
- **Dark Threshold**: `0` to `255` (default: `223`)
### Pixel Sort Advanced (ASDF)
**Location**: `Add Node` → `image` → `effects` → `Pixel Sort Advanced (ASDF)`
Additional features:
- Optional parameter inputs
- Custom Processing sketch path support
- Extended configuration options
## 🎨 Sorting Modes Explained
| Mode | Description | Effect |
|------|-------------|--------|
| **White** | Sorts pixels based on white threshold | Flowing, organic distortions |
| **Black** | Sorts pixels based on black threshold | Emphasizes shadows and dark regions |
| **Bright** | Sorts pixels based on brightness values | Smooth luminance gradients |
| **Dark** | Sorts pixels based on darkness values | High-contrast dramatic effects |
## 📖 Documentation
- 📋 [Installation Guide](docs/INSTALLATION.md) - Detailed setup instructions
- 🔧 [Troubleshooting](docs/TROUBLESHOOTING.md) - Common issues and solutions
- 📚 [API Reference](docs/API.md) - Complete technical documentation
- 🎯 [Examples](examples/) - Ready-to-use workflow files
## 🛠️ System Requirements
**Minimum**:
- Linux (Ubuntu 20.04+, CentOS 8+, Arch Linux)
- 4GB RAM, 2GB free storage
- Python 3.8+, Java 17+
- ComfyUI v0.3.40+
**Recommended**:
- 8GB+ RAM, SSD storage
- Multi-core processor
- GPU for ComfyUI acceleration
## 🤝 Contributing
We welcome contributions! Please see our [Contributing Guidelines](CONTRIBUTING.md).
1. **Fork** the repository
2. **Create** a feature branch: `git checkout -b feature/amazing-feature`
3. **Commit** your changes: `git commit -m 'Add amazing feature'`
4. **Push** to the branch: `git push origin feature/amazing-feature`
5. **Open** a Pull Request
## 📄 License
This project is licensed under the **MIT License** - see the [LICENSE](LICENSE) file for details.
### Attribution
- **Original Algorithm**: [ASDFPixelSort](http://kimasendorf.com) by Kim Asendorf (2010)
- **ComfyUI Integration**: This repository
- **Processing**: [Processing Foundation](https://processing.org)
## 🙏 Acknowledgments
- **Kim Asendorf** for the original ASDFPixelSort algorithm
- **ComfyUI Community** for the excellent framework
- **Processing Foundation** for the creative coding platform
- **Contributors** who help improve this project
---
<div align="center">
**Made with ❤️ for the ComfyUI community**
[⭐ Star this repo](https://github.com/your-username/ComfyUI-ASDF-PixelSort) • [🐛 Report Bug](https://github.com/your-username/ComfyUI-ASDF-PixelSort/issues) • [💡 Request Feature](https://github.com/your-username/ComfyUI-ASDF-PixelSort/issues)
</div>
+26
View File
@@ -0,0 +1,26 @@
"""
ComfyUI ASDF Pixel Sort Nodes Package
This package provides ComfyUI integration for Kim Asendorf's ASDFPixelSort algorithm.
Includes both basic and advanced pixel sorting nodes with full parameter control.
Author: ComfyUI Integration Team
Based on: ASDFPixelSort by Kim Asendorf (2010)
License: MIT
"""
from .pixel_sort_node import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
# Package metadata
__version__ = "1.0.0"
__author__ = "ComfyUI Integration Team"
__description__ = "ComfyUI nodes for ASDFPixelSort algorithm"
__url__ = "https://github.com/your-repo/ComfyUI-ASDF-PixelSort-Package"
# Export node mappings for ComfyUI
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
# Print package info on import
print(f"🎨 ComfyUI ASDF Pixel Sort Nodes v{__version__} loaded")
print(" Available nodes: Pixel Sort (ASDF), Pixel Sort Advanced (ASDF)")
print(" Category: image/effects")
+1
View File
@@ -0,0 +1 @@
# API Reference
+1
View File
@@ -0,0 +1 @@
# Installation Guide
+1
View File
@@ -0,0 +1 @@
# Troubleshooting
+1
View File
@@ -0,0 +1 @@
{"nodes": [{"type": "LoadImage"}, {"type": "PixelSortNode"}, {"type": "SaveImage"}]}
+160
View File
@@ -0,0 +1,160 @@
"""
ComfyUI Pixel Sort Node
Integrates ASDFPixelSort Processing script with ComfyUI workflows
"""
import subprocess
import os
import tempfile
import torch
import numpy as np
from PIL import Image
class PixelSortNode:
"""
A ComfyUI node that applies pixel sorting effects using the ASDFPixelSort Processing script.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image": ("IMAGE",),
"mode": (["white", "black", "bright", "dark"], {"default": "white"}),
"white_threshold": ("INT", {"default": -12345678, "min": -16777216, "max": 0, "step": 1000}),
"black_threshold": ("INT", {"default": -3456789, "min": -16777216, "max": 0, "step": 1000}),
"bright_threshold": ("INT", {"default": 127, "min": 0, "max": 255, "step": 1}),
"dark_threshold": ("INT", {"default": 223, "min": 0, "max": 255, "step": 1}),
}
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "apply_pixel_sort"
CATEGORY = "image/effects"
def __init__(self):
# Path to Processing installation
self.processing_path = "/root/Processing/processing-4.3/processing-java"
self.sketch_path = "/root/Processing/ASDFPixelSort"
def apply_pixel_sort(self, image, mode, white_threshold, black_threshold, bright_threshold, dark_threshold):
"""
Apply pixel sorting to the input image using the Processing script.
"""
# Convert mode string to number
mode_map = {"white": 0, "black": 1, "bright": 2, "dark": 3}
mode_num = mode_map[mode]
# Create temporary files
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as input_file:
input_path = input_file.name
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as output_file:
output_path = output_file.name
try:
# Convert tensor to PIL Image and save
img_tensor = image[0] # Remove batch dimension
img_array = (img_tensor.cpu().numpy() * 255).astype(np.uint8)
img_pil = Image.fromarray(img_array)
img_pil.save(input_path)
# Prepare command arguments
cmd = [
self.processing_path,
f"--sketch={self.sketch_path}",
"--run",
input_path,
output_path,
str(mode_num),
str(white_threshold),
str(black_threshold),
str(bright_threshold),
str(dark_threshold)
]
# Set up environment for headless operation
env = os.environ.copy()
env["DISPLAY"] = ":99"
# Run the Processing script
print(f"Running pixel sort with mode: {mode}")
result = subprocess.run(cmd, env=env, capture_output=True, text=True, timeout=120)
if result.returncode != 0:
print(f"Processing script error: {result.stderr}")
raise RuntimeError(f"Pixel sort failed: {result.stderr}")
# Load the processed image
if not os.path.exists(output_path):
raise RuntimeError("Output image was not created")
img_sorted = Image.open(output_path).convert("RGB")
img_array = np.array(img_sorted).astype(np.float32) / 255.0
img_tensor = torch.from_numpy(img_array).unsqueeze(0) # Add batch dimension
print(f"Pixel sort completed successfully")
return (img_tensor,)
except subprocess.TimeoutExpired:
raise RuntimeError("Pixel sort operation timed out")
except Exception as e:
raise RuntimeError(f"Pixel sort failed: {str(e)}")
finally:
# Clean up temporary files
try:
if os.path.exists(input_path):
os.unlink(input_path)
if os.path.exists(output_path):
os.unlink(output_path)
except:
pass
class PixelSortAdvancedNode:
"""
Advanced pixel sort node with more control options.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image": ("IMAGE",),
"mode": (["white", "black", "bright", "dark"], {"default": "white"}),
},
"optional": {
"white_threshold": ("INT", {"default": -12345678, "min": -16777216, "max": 0, "step": 1000}),
"black_threshold": ("INT", {"default": -3456789, "min": -16777216, "max": 0, "step": 1000}),
"bright_threshold": ("INT", {"default": 127, "min": 0, "max": 255, "step": 1}),
"dark_threshold": ("INT", {"default": 223, "min": 0, "max": 255, "step": 1}),
"custom_sketch_path": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "apply_pixel_sort_advanced"
CATEGORY = "image/effects"
def apply_pixel_sort_advanced(self, image, mode, white_threshold=-12345678, black_threshold=-3456789,
bright_threshold=127, dark_threshold=223, custom_sketch_path=""):
"""
Advanced pixel sorting with optional custom sketch path.
"""
node = PixelSortNode()
if custom_sketch_path:
node.sketch_path = custom_sketch_path
return node.apply_pixel_sort(image, mode, white_threshold, black_threshold,
bright_threshold, dark_threshold)
# Node registration
NODE_CLASS_MAPPINGS = {
"PixelSortNode": PixelSortNode,
"PixelSortAdvancedNode": PixelSortAdvancedNode
}
NODE_DISPLAY_NAME_MAPPINGS = {
"PixelSortNode": "Pixel Sort (ASDF)",
"PixelSortAdvancedNode": "Pixel Sort Advanced (ASDF)"
}
+377
View File
@@ -0,0 +1,377 @@
/********
ASDF Pixel Sort
Kim Asendorf | 2010 | kimasendorf.com
Sorting modes
0 = white
1 = black
2 = bright
3 = dark
*/
int mode = 0;
// image path is relative to sketch directory
PImage img;
String imgFileName = "mountains";
String fileType = "jpg";
int loops = 1;
// threshold values to determine sorting start and end pixels
// using the absolute rgb value
// r*g*b = 255*255*255 = 16581375
// 0 = white
// -16581375 = black
// sort all pixels whiter than the threshold
int whiteValue = -12345678;
// sort all pixels blacker than the threshold
int blackValue = -3456789;
// using the brightness value
// sort all pixels brighter than the threshold
int brightValue = 127;
// sort all pixels darker than the threshold
int darkValue = 223;
int row = 0;
int column = 0;
boolean saved = false;
void setup() {
img = loadImage(imgFileName + "." + fileType);
// use only numbers (not variables) for the size() command, Processing 3
size(1, 1);
// allow resize and update surface to image dimensions
surface.setResizable(true);
surface.setSize(img.width, img.height);
// load image onto surface - scale to the available width,height for display
image(img, 0, 0, width, height);
}
void draw() {
if (frameCount <= loops) {
// loop through columns
println("Sorting Columns");
while (column < img.width-1) {
img.loadPixels();
sortColumn();
column++;
img.updatePixels();
}
// loop through rows
println("Sorting Rows");
while (row < img.height-1) {
img.loadPixels();
sortRow();
row++;
img.updatePixels();
}
}
// load updated image onto surface and scale to fit the display width and height
image(img, 0, 0, width, height);
if (!saved && frameCount >= loops) {
// save img
img.save(imgFileName + "_" + mode + ".png");
saved = true;
println("Saved frame " + frameCount);
// exiting here can interrupt file save, wait for user to trigger exit
println("Click or press any key to exit...");
}
}
void keyPressed() {
if (saved) {
System.exit(0);
}
}
void mouseClicked() {
if (saved) {
System.exit(0);
}
}
void sortRow() {
// current row
int y = row;
// where to start sorting
int x = 0;
// where to stop sorting
int xEnd = 0;
while (xEnd < img.width-1) {
switch (mode) {
case 0:
x = getFirstNoneWhiteX(x, y);
xEnd = getNextWhiteX(x, y);
break;
case 1:
x = getFirstNoneBlackX(x, y);
xEnd = getNextBlackX(x, y);
break;
case 2:
x = getFirstNoneBrightX(x, y);
xEnd = getNextBrightX(x, y);
break;
case 3:
x = getFirstNoneDarkX(x, y);
xEnd = getNextDarkX(x, y);
break;
default:
break;
}
if (x < 0) break;
int sortingLength = xEnd-x;
color[] unsorted = new color[sortingLength];
color[] sorted = new color[sortingLength];
for (int i = 0; i < sortingLength; i++) {
unsorted[i] = img.pixels[x + i + y * img.width];
}
sorted = sort(unsorted);
for (int i = 0; i < sortingLength; i++) {
img.pixels[x + i + y * img.width] = sorted[i];
}
x = xEnd+1;
}
}
void sortColumn() {
// current column
int x = column;
// where to start sorting
int y = 0;
// where to stop sorting
int yEnd = 0;
while (yEnd < img.height-1) {
switch (mode) {
case 0:
y = getFirstNoneWhiteY(x, y);
yEnd = getNextWhiteY(x, y);
break;
case 1:
y = getFirstNoneBlackY(x, y);
yEnd = getNextBlackY(x, y);
break;
case 2:
y = getFirstNoneBrightY(x, y);
yEnd = getNextBrightY(x, y);
break;
case 3:
y = getFirstNoneDarkY(x, y);
yEnd = getNextDarkY(x, y);
break;
default:
break;
}
if (y < 0) break;
int sortingLength = yEnd-y;
color[] unsorted = new color[sortingLength];
color[] sorted = new color[sortingLength];
for (int i = 0; i < sortingLength; i++) {
unsorted[i] = img.pixels[x + (y+i) * img.width];
}
sorted = sort(unsorted);
for (int i = 0; i < sortingLength; i++) {
img.pixels[x + (y+i) * img.width] = sorted[i];
}
y = yEnd+1;
}
}
// white x
int getFirstNoneWhiteX(int x, int y) {
while (img.pixels[x + y * img.width] < whiteValue) {
x++;
if (x >= img.width) return -1;
}
return x;
}
int getNextWhiteX(int x, int y) {
x++;
while (img.pixels[x + y * img.width] > whiteValue) {
x++;
if (x >= img.width) return img.width-1;
}
return x-1;
}
// black x
int getFirstNoneBlackX(int x, int y) {
while (img.pixels[x + y * img.width] > blackValue) {
x++;
if (x >= img.width) return -1;
}
return x;
}
int getNextBlackX(int x, int y) {
x++;
while (img.pixels[x + y * img.width] < blackValue) {
x++;
if (x >= img.width) return img.width-1;
}
return x-1;
}
// bright x
int getFirstNoneBrightX(int x, int y) {
while (brightness(img.pixels[x + y * img.width]) < brightValue) {
x++;
if (x >= img.width) return -1;
}
return x;
}
int getNextBrightX(int x, int y) {
x++;
while (brightness(img.pixels[x + y * img.width]) > brightValue) {
x++;
if (x >= img.width) return img.width-1;
}
return x-1;
}
// dark x
int getFirstNoneDarkX(int x, int y) {
while (brightness(img.pixels[x + y * img.width]) > darkValue) {
x++;
if (x >= img.width) return -1;
}
return x;
}
int getNextDarkX(int x, int y) {
x++;
while (brightness(img.pixels[x + y * img.width]) < darkValue) {
x++;
if (x >= img.width) return img.width-1;
}
return x-1;
}
// white y
int getFirstNoneWhiteY(int x, int y) {
if (y < img.height) {
while (img.pixels[x + y * img.width] < whiteValue) {
y++;
if (y >= img.height) return -1;
}
}
return y;
}
int getNextWhiteY(int x, int y) {
y++;
if (y < img.height) {
while (img.pixels[x + y * img.width] > whiteValue) {
y++;
if (y >= img.height) return img.height-1;
}
}
return y-1;
}
// black y
int getFirstNoneBlackY(int x, int y) {
if (y < img.height) {
while (img.pixels[x + y * img.width] > blackValue) {
y++;
if (y >= img.height) return -1;
}
}
return y;
}
int getNextBlackY(int x, int y) {
y++;
if (y < img.height) {
while (img.pixels[x + y * img.width] < blackValue) {
y++;
if (y >= img.height) return img.height-1;
}
}
return y-1;
}
// bright y
int getFirstNoneBrightY(int x, int y) {
if (y < img.height) {
while (brightness(img.pixels[x + y * img.width]) < brightValue) {
y++;
if (y >= img.height) return -1;
}
}
return y;
}
int getNextBrightY(int x, int y) {
y++;
if (y < img.height) {
while (brightness(img.pixels[x + y * img.width]) > brightValue) {
y++;
if (y >= img.height) return img.height-1;
}
}
return y-1;
}
// dark y
int getFirstNoneDarkY(int x, int y) {
if (y < img.height) {
while (brightness(img.pixels[x + y * img.width]) > darkValue) {
y++;
if (y >= img.height) return -1;
}
}
return y;
}
int getNextDarkY(int x, int y) {
y++;
if (y < img.height) {
while (brightness(img.pixels[x + y * img.width]) < darkValue) {
y++;
if (y >= img.height) return img.height-1;
}
}
return y-1;
}
+13
View File
@@ -0,0 +1,13 @@
# ComfyUI ASDF Pixel Sort Nodes - Python Dependencies
# Core dependencies (usually provided by ComfyUI)
torch>=1.9.0
torchvision>=0.10.0
numpy>=1.20.0
Pillow>=8.0.0
# Additional dependencies for this package
# (Most should already be available in ComfyUI environment)
# Note: ComfyUI typically provides these dependencies
# This file is for reference and standalone installations