add csv output
This commit is contained in:
@@ -8,6 +8,7 @@ from .model.merge2 import StateDictMergerBlockWeightedMulti
|
|||||||
from .image.image import GridImage
|
from .image.image import GridImage
|
||||||
from .image.latenttoimage import LatentToImage, LatentToHist
|
from .image.latenttoimage import LatentToImage, LatentToHist
|
||||||
from .image.blend_extra import Blend2
|
from .image.blend_extra import Blend2
|
||||||
|
from .outputs import SaveText
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
# latent
|
# latent
|
||||||
@@ -70,4 +71,8 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
|
|
||||||
## rearrange images to single image with specified columns and gap
|
## rearrange images to single image with specified columns and gap
|
||||||
'GridImage': GridImage,
|
'GridImage': GridImage,
|
||||||
|
|
||||||
|
# others
|
||||||
|
'SaveText': SaveText,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-6
@@ -1,4 +1,7 @@
|
|||||||
import colorsys
|
import colorsys
|
||||||
|
from io import StringIO
|
||||||
|
import csv
|
||||||
|
from typing import Optional
|
||||||
import torch
|
import torch
|
||||||
import torchvision.transforms.functional
|
import torchvision.transforms.functional
|
||||||
import PIL.Image
|
import PIL.Image
|
||||||
@@ -76,7 +79,7 @@ class LatentToHist:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_TYPES = ('IMAGE',)
|
RETURN_TYPES = ('IMAGE', 'STRING')
|
||||||
FUNCTION = 'execute'
|
FUNCTION = 'execute'
|
||||||
|
|
||||||
CATEGORY = 'latent'
|
CATEGORY = 'latent'
|
||||||
@@ -117,10 +120,12 @@ class LatentToHist:
|
|||||||
else:
|
else:
|
||||||
ymax = float(ymax)
|
ymax = float(ymax)
|
||||||
|
|
||||||
image_tensors = list(self.plot(hists, ymax))
|
sio = StringIO()
|
||||||
|
image_tensors = list(self.plot(hists, ymax, sio))
|
||||||
|
|
||||||
images = torch.cat(image_tensors)
|
images = torch.cat(image_tensors)
|
||||||
return (images,)
|
|
||||||
|
return (images, sio.getvalue())
|
||||||
|
|
||||||
def hist(self, batch_data: torch.Tensor, min_: float, max_: float, bins: int):
|
def hist(self, batch_data: torch.Tensor, min_: float, max_: float, bins: int):
|
||||||
assert batch_data.dim() == 2
|
assert batch_data.dim() == 2
|
||||||
@@ -138,12 +143,18 @@ class LatentToHist:
|
|||||||
|
|
||||||
return hists
|
return hists
|
||||||
|
|
||||||
def plot(self, hists: list, ymax: float):
|
def plot(self, hists: list, ymax: float, sio: Optional[StringIO] = None):
|
||||||
try:
|
try:
|
||||||
from matplotlib import pyplot as plt
|
from matplotlib import pyplot as plt
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise RuntimeError('LatentToHist requires matplotlib. Please install it.')
|
raise RuntimeError('LatentToHist requires matplotlib. Please install it.')
|
||||||
|
|
||||||
|
if sio is not None:
|
||||||
|
writer = csv.writer(sio)
|
||||||
|
writer.writerow(['ch', 'value', 'degree'])
|
||||||
|
else:
|
||||||
|
writer = None
|
||||||
|
|
||||||
def color(c: int, C: int):
|
def color(c: int, C: int):
|
||||||
h = c / C
|
h = c / C
|
||||||
return colorsys.hsv_to_rgb(h, 1.0, 1.0)
|
return colorsys.hsv_to_rgb(h, 1.0, 1.0)
|
||||||
@@ -158,14 +169,22 @@ class LatentToHist:
|
|||||||
for c, (hist, bin_edges) in enumerate(batch_hists):
|
for c, (hist, bin_edges) in enumerate(batch_hists):
|
||||||
bin_edges = bin_edges.view((1,1,-1)) # batch,in_ch,iW
|
bin_edges = bin_edges.view((1,1,-1)) # batch,in_ch,iW
|
||||||
W = W.to(bin_edges.device)
|
W = W.to(bin_edges.device)
|
||||||
x = torch.nn.functional.conv1d(bin_edges, W)
|
x = torch.nn.functional.conv1d(bin_edges, W).squeeze()
|
||||||
|
|
||||||
|
assert x.shape == hist.shape
|
||||||
|
|
||||||
plot = ax.plot(
|
plot = ax.plot(
|
||||||
x.squeeze(), hist, color=color(c,C),
|
x, hist, color=color(c,C),
|
||||||
linewidth=2, marker='o', markersize=4,
|
linewidth=2, marker='o', markersize=4,
|
||||||
)
|
)
|
||||||
|
|
||||||
plots.append(plot[0])
|
plots.append(plot[0])
|
||||||
|
if writer is not None:
|
||||||
|
writer.writerows([
|
||||||
|
# ch, v, d
|
||||||
|
[c, x[i].item(), hist[i].item()]
|
||||||
|
for i in range(x.size(-1))
|
||||||
|
])
|
||||||
|
|
||||||
ax.legend(plots, [f'ch={c}' for c in range(C)], loc=2)
|
ax.legend(plots, [f'ch={c}' for c in range(C)], loc=2)
|
||||||
ax.set_ylim(0, ymax)
|
ax.set_ylim(0, ymax)
|
||||||
@@ -182,4 +201,8 @@ class LatentToHist:
|
|||||||
# (C, H, W)
|
# (C, H, W)
|
||||||
|
|
||||||
image_tensor = einops.rearrange(image_tensor, 'c h w -> h w c').unsqueeze(0)
|
image_tensor = einops.rearrange(image_tensor, 'c h w -> h w c').unsqueeze(0)
|
||||||
|
|
||||||
|
if sio is not None:
|
||||||
|
sio.flush()
|
||||||
|
|
||||||
yield image_tensor
|
yield image_tensor
|
||||||
|
|||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
import os
|
||||||
|
import folder_paths
|
||||||
|
|
||||||
|
class SaveText:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.output_dir = folder_paths.get_output_directory()
|
||||||
|
self.type = "output"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
'required': {
|
||||||
|
'filename_prefix': ('STRING', { 'default': 'ComfyUI' }),
|
||||||
|
'ext': ('STRING', { 'default': 'txt' }),
|
||||||
|
'text': ('STRING', { 'multiline': True, 'default': '' }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OUTPUT_NODE = True
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
|
||||||
|
FUNCTION = 'execute'
|
||||||
|
|
||||||
|
CATEGORY = 'utils'
|
||||||
|
|
||||||
|
def execute(self, filename_prefix: str, ext: str, text: str):
|
||||||
|
def map_filename(filename):
|
||||||
|
prefix_len = len(os.path.basename(filename_prefix))
|
||||||
|
prefix = filename[:prefix_len + 1]
|
||||||
|
try:
|
||||||
|
digits = int(filename[prefix_len + 1:].split('_')[0])
|
||||||
|
except:
|
||||||
|
digits = 0
|
||||||
|
return (digits, prefix)
|
||||||
|
|
||||||
|
subfolder = os.path.dirname(os.path.normpath(filename_prefix))
|
||||||
|
filename = os.path.basename(os.path.normpath(filename_prefix))
|
||||||
|
|
||||||
|
full_output_folder = os.path.join(self.output_dir, subfolder)
|
||||||
|
|
||||||
|
if os.path.commonpath((self.output_dir, os.path.abspath(full_output_folder))) != self.output_dir:
|
||||||
|
print("Saving image outside the output folder is not allowed.")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
counter = max(filter(lambda a: a[1][:-1] == filename and a[1][-1] == "_", map(map_filename, os.listdir(full_output_folder))))[0] + 1
|
||||||
|
except ValueError:
|
||||||
|
counter = 1
|
||||||
|
except FileNotFoundError:
|
||||||
|
os.makedirs(full_output_folder, exist_ok=True)
|
||||||
|
counter = 1
|
||||||
|
|
||||||
|
if ext is None or len(ext) == 0 or ext == '.':
|
||||||
|
ext = '.txt'
|
||||||
|
if not ext.startswith('.'):
|
||||||
|
ext = '.' + ext
|
||||||
|
|
||||||
|
file = f"{filename}_{counter:05}_{ext}"
|
||||||
|
with open(os.path.join(full_output_folder, file), 'w') as io:
|
||||||
|
io.write(text)
|
||||||
|
counter += 1
|
||||||
|
|
||||||
|
return {}
|
||||||
Reference in New Issue
Block a user