1 Commits
Author SHA1 Message Date
Tumpal Gemelli 6b1cdb914b Adding GMI'C Qt node and refactor for batch images
For complete commit messages, please see the devel branch
2024-03-25 20:11:58 +07:00
8 changed files with 575 additions and 1485 deletions
+27 -20
View File
@@ -1,29 +1,36 @@
# G'MIC in ComfyUI
![](./assets/workflow.png)
## Quick Start
![](./screenshot--comfyui_gmic.png)
* Install custom node with: `git clone https://github.com/gemelli/ComfyUI_GMIC`
or copy [gmic.py](https://github.com/gemelli/ComfyUI_GMIC/raw/main/comfy_gmic.py) file to custom nodes folder
* Download [G'MIC Command-line interface (CLI)](https://gmic.eu/download.html)
* Put executable path in your PATH ([how to](https://windowsloop.com/how-to-add-to-windows-path/))
* Download [G'MIC-Qt stand-alone interface](https://gmic.eu/download.html)
* Choose a filter and copy the command
![](./assets/screenshot.png)
* Paste the command to the custom node
## Features
* Wonderful image-processing suite (effects, filtering, geometric manipulation, image data, color.)
* Friendly for beginners and power users.
* On going [documented](https://github.com/gemell1/ComfyUI_GMIC/wiki) workflows.
## Quick Start
- Install custom node with: `git clone https://github.com/gemell1/ComfyUI_GMIC`
or copy [comfy_gmic.py](./comfy_gmic.py) file to custom nodes folder.
- Download [G'MIC Command-line interface (CLI)](https://gmic.eu/download.html) and [G'MIC-Qt stand-alone interface](https://gmic.eu/download.html).
- Put executable path in your PATH. ([how to](https://windowsloop.com/how-to-add-to-windows-path/))
- Drag and drop this [workflow](./workflow.json) to ComfyUi.
```py
### Changelog ----------
- 'March' Breaking changes: please recreate your workflow! 🙏
@ G_MIC Qt Node
@ Batch Images
- "Feb" Better cross platform support.
- "Nov" First upload.
```
---
Tested on Windows10, Comfyui [248aa3e](https://github.com/comfyanonymous/ComfyUI/tree/248aa3e56355d75ac3d8632af769e6c700d9bfac), G'MIC cli 3.2.4
**TODO**:
[ ] Batch image
[ ] Favorites drop-down for easier usage
Tested on Windows 10, ComfyUI [March 09](https://github.com/comfyanonymous/ComfyUI/tree/a9ee9589b72aa0e2931f1c0705524c56adaee26d), G'MIC (Cli and Qt) 3.3.4
---
Other Image Processing ComfyUI custom nodes:
* [ComfyUI-Allor](https://github.com/Nourepide/ComfyUI-Allor): ComfyUI plugin for image processing and work with alpha chanel
* [ComfyUI-post-processing-nodes](https://github.com/EllangoK/ComfyUI-post-processing-nodes): A collection of Post Processing Nodes for ComfyUI, which enable a variety of cool image effects
* [ComfyI2I](https://github.com/ManglerFTW/ComfyI2I): ComfyI2I is a set of custom nodes for ComfyUI that help with image 2 image functions
* [ComfyUI\_ImageProcessing](https://github.com/bvhari/ComfyUI_ImageProcessing): ComfyUI custom nodes to apply various image processing techniques
[Other Image Processing ComfyUI custom nodes](https://github.com/gemell1/ComfyUI_GMIC/wiki)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

-131
View File
@@ -1,131 +0,0 @@
{
"last_node_id": 3,
"last_link_id": 2,
"nodes": [
{
"id": 1,
"type": "LoadImage",
"pos": [
384.08492126464853,
147.57919708251956
],
"size": [
320,
310
],
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
1
],
"shape": 3,
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null,
"shape": 3
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"sample_gmicky.png",
"image"
]
},
{
"id": 3,
"type": "PreviewImage",
"pos": [
1080,
160
],
"size": [
450,
370
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 2
}
],
"properties": {
"Node name for S&R": "PreviewImage"
}
},
{
"id": 2,
"type": "GmicCliWrapper",
"pos": [
740,
150
],
"size": {
"0": 315,
"1": 58
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 1
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
2
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "GmicCliWrapper"
},
"widgets_values": [
"fx_bokeh 3,8,0,30,8,4,0.3,0.2,210,210,80,160,0.7,30,20,20,1,2,170,130,20,110,0.15,0,50,50"
]
}
],
"links": [
[
1,
1,
0,
2,
0,
"IMAGE"
],
[
2,
2,
0,
3,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {},
"version": 0.4
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 KiB

+113 -29
View File
@@ -1,3 +1,4 @@
import os
import random
import shlex
import subprocess
@@ -5,15 +6,15 @@ from pathlib import Path
import folder_paths
from nodes import LoadImage, SaveImage
import torch
class GmicCliWrapper:
def __init__(self):
self.output_dir = folder_paths.get_temp_directory()
self.type = "temp"
self.prefix_append = "_temp_" + ''.join(
random.choice("abcdefghijklmnopqrstupvxyz") for x in range(5))
self.compress_level = 4
@classmethod
def INPUT_TYPES(s):
@@ -27,41 +28,124 @@ class GmicCliWrapper:
}
RETURN_TYPES = ("IMAGE", )
FUNCTION = "gmic_filter"
CATEGORY = "filters"
FUNCTION = "gmicCli"
CATEGORY = "image/preprocessors"
def gmic_filter(self, command, images):
self.compress_level = 4
imgtemp = SaveImage.save_images(self, images)
fname = imgtemp['ui']['images'][0]['filename']
def gmicCli(self, command, images):
temp_dir = Path(self.output_dir)
inpath = temp_dir / fname
outpath = temp_dir / fname.replace('.png', '-out.png')
try:
autodash = "" if command.startswith(('-', '+')) else "-"
gmic_command_args = shlex.split(autodash + command)
subprocess.check_call([
'gmic',
'-input',
inpath,
] + gmic_command_args + [
'-output',
outpath,
])
result_image = LoadImage.load_image(self, str(outpath))
finally:
if outpath.exists():
outpath.unlink()
if inpath.exists():
inpath.unlink()
results = []
for (batch_number, image) in enumerate(images):
imgtemp = SaveImage.save_images(self, image[None])
fname = imgtemp['ui']['images'][0]['filename']
inpath = Path(temp_dir / fname)
outpath = Path(temp_dir / fname.replace('.png', '-out.png'))
try:
autodash = "" if command.startswith(('-', '+')) else "-"
gmic_command_args = shlex.split(autodash + command)
subprocess_args = [
'gmic',
'-input',
str(inpath),
] + gmic_command_args + [
'-output',
str(outpath),
]
subprocess.check_call(subprocess_args)
result_image = LoadImage.load_image(self, str(outpath))
results.append(result_image[0])
finally:
if outpath.exists():
outpath.unlink()
if inpath.exists():
inpath.unlink()
if len(results) > 1:
results = torch.cat(results, dim=0)
else:
results = results[0]
return (results, )
return (result_image[0], )
class GmicQtWrapper:
def __init__(self):
self.output_dir = folder_paths.get_temp_directory()
self.type = "temp"
self.prefix_append = "_temp_" + ''.join(
random.choice("abcdefghijklmnopqrstupvxyz") for x in range(5))
self.compress_level = 4
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"images": ("IMAGE", ),
"reapply_first": ("BOOLEAN", {"default": False}),
"layers": ("BOOLEAN", {"default": False}),
}
}
RETURN_TYPES = ("IMAGE", )
FUNCTION = "gmicQt"
CATEGORY = "image/preprocessors"
def gmicQt(self, images, reapply_first, layers):
temp_dir = Path(self.output_dir)
results = []
multiple_inpath = []
for (batch_number, image) in enumerate(images):
imgtemp = SaveImage.save_images(self, image[None])
fname = imgtemp['ui']['images'][0]['filename']
inpath = Path(temp_dir / fname)
multiple_inpath.append(inpath)
multiple_outpath = []
for inpath in multiple_inpath:
outpath = inpath.with_name("processed-" + inpath.name)
multiple_outpath.append(outpath)
try:
multiple_input = [str(i) for i in multiple_inpath]
placeholder = "processed-%f"
multiple_output = str(Path(temp_dir / placeholder))
subprocess_args = [
'gmic_qt',
'--output',
multiple_output
]
subprocess_args += multiple_input
subprocess.check_call(subprocess_args)
for outpath in multiple_outpath:
result_image = LoadImage.load_image(self, str(outpath))
results.append(result_image[0])
finally:
for outpath in multiple_outpath:
if outpath.exists():
outpath.unlink()
for inpath in multiple_inpath:
if inpath.exists():
inpath.unlink()
if len(results) > 1:
results = torch.cat(results, dim=0)
else:
results = results[0]
return (results, )
NODE_CLASS_MAPPINGS = {
"GmicCliWrapper": GmicCliWrapper,
"GmicQtWrapper": GmicQtWrapper,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"GmicCliWrapper": "GMIC Image Processing",
"GmicCliWrapper": "G'MIC Cli",
"GmicQtWrapper": "G'MIC-Qt",
}
-1305
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 369 KiB

+435
View File
@@ -0,0 +1,435 @@
{
"last_node_id": 10,
"last_link_id": 10,
"nodes": [
{
"id": 2,
"type": "GmicCliWrapper",
"pos": [
826,
56
],
"size": {
"0": 315,
"1": 58
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 1
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
2
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "GmicCliWrapper"
},
"widgets_values": [
"fx_bokeh 3,8,0,30,8,4,0.3,0.2,210,210,80,160,0.7,30,20,20,1,2,170,130,20,110,0.15,0,50,50"
]
},
{
"id": 3,
"type": "PreviewImage",
"pos": [
1204,
56
],
"size": {
"0": 210,
"1": 252
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 2
}
],
"properties": {
"Node name for S&R": "PreviewImage"
}
},
{
"id": 1,
"type": "LoadImage",
"pos": [
448,
70
],
"size": [
322,
308
],
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
1,
3,
5,
6
],
"shape": 3,
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null,
"shape": 3,
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"example.png",
"image"
]
},
{
"id": 7,
"type": "GmicCliWrapper",
"pos": [
826,
392
],
"size": {
"0": 315,
"1": 58
},
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 7
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
8
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "GmicCliWrapper"
},
"widgets_values": [
"fx_bokeh 3,8,0,30,8,4,0.3,0.2,210,210,80,160,0.7,30,20,20,1,2,170,130,20,110,0.15,0,50,50"
]
},
{
"id": 4,
"type": "GmicQtWrapper",
"pos": [
826,
168
],
"size": {
"0": 315,
"1": 82
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 3
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
4
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "GmicQtWrapper"
},
"widgets_values": [
false,
false
]
},
{
"id": 6,
"type": "ImageBatch",
"pos": [
546,
434
],
"size": {
"0": 210,
"1": 46
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "image1",
"type": "IMAGE",
"link": 5
},
{
"name": "image2",
"type": "IMAGE",
"link": 6
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
7,
9
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "ImageBatch"
}
},
{
"id": 5,
"type": "PreviewImage",
"pos": [
1442,
56
],
"size": {
"0": 210,
"1": 252
},
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 4
}
],
"properties": {
"Node name for S&R": "PreviewImage"
}
},
{
"id": 8,
"type": "PreviewImage",
"pos": [
1218,
392
],
"size": {
"0": 210,
"1": 252
},
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 8
}
],
"properties": {
"Node name for S&R": "PreviewImage"
}
},
{
"id": 9,
"type": "GmicQtWrapper",
"pos": [
826,
518
],
"size": {
"0": 315,
"1": 82
},
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 9
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
10
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "GmicQtWrapper"
},
"widgets_values": [
false,
false
]
},
{
"id": 10,
"type": "PreviewImage",
"pos": [
1456,
392
],
"size": {
"0": 210,
"1": 252
},
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 10
}
],
"properties": {
"Node name for S&R": "PreviewImage"
}
}
],
"links": [
[
1,
1,
0,
2,
0,
"IMAGE"
],
[
2,
2,
0,
3,
0,
"IMAGE"
],
[
3,
1,
0,
4,
0,
"IMAGE"
],
[
4,
4,
0,
5,
0,
"IMAGE"
],
[
5,
1,
0,
6,
0,
"IMAGE"
],
[
6,
1,
0,
6,
1,
"IMAGE"
],
[
7,
6,
0,
7,
0,
"IMAGE"
],
[
8,
7,
0,
8,
0,
"IMAGE"
],
[
9,
6,
0,
9,
0,
"IMAGE"
],
[
10,
9,
0,
10,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {},
"version": 0.4
}