From 33cb9fbb508ed03cb1563b9ae86eebcb358d28af Mon Sep 17 00:00:00 2001 From: null Date: Mon, 20 May 2024 14:51:06 +0000 Subject: [PATCH 1/5] chore(pyproject): $'Add pyproject.toml for Custom Node Registry' --- pyproject.toml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..22ca3d5 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "comfyui-crystools" +description = "" +version = "1.0.0" +license = "LICENSE" +dependencies = ["deepdiff", "torch", "numpy", "Pillow>=9.5.0", "pynvml", "py-cpuinfo"] + +[project.urls] +Repository = "https://github.com/crystian/ComfyUI-Crystools" + +[tool.comfy] +PublisherId = "" +DisplayName = "ComfyUI-Crystools" +Icon = "" +Models = [{location = "/checkpoints/model.safetensor", model_url = "https://example.com/model.zip"}] From cbc90bdb9afbd2104cac0856b6296f4d4415a9da Mon Sep 17 00:00:00 2001 From: haohaocreates <162922985+haohaocreates@users.noreply.github.com> Date: Mon, 20 May 2024 20:37:06 -0400 Subject: [PATCH 2/5] Include description in pyproject.toml --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 22ca3d5..a4a08d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "comfyui-crystools" -description = "" +description = "With this suit, you can see the resources monitor, progress bar & time elapsed, metadata and compare between two images, compare between two JSONs, show any value to console/display, pipes, and more!\nThis provides better nodes to load/save images, previews, etc, and see \"hidden\" data without loading a new workflow." version = "1.0.0" license = "LICENSE" dependencies = ["deepdiff", "torch", "numpy", "Pillow>=9.5.0", "pynvml", "py-cpuinfo"] @@ -12,4 +12,3 @@ Repository = "https://github.com/crystian/ComfyUI-Crystools" PublisherId = "" DisplayName = "ComfyUI-Crystools" Icon = "" -Models = [{location = "/checkpoints/model.safetensor", model_url = "https://example.com/model.zip"}] From 985734cd7d004947e2c476efd7dd4ec7427a8d3c Mon Sep 17 00:00:00 2001 From: Ainaemaet Date: Thu, 6 Jun 2024 21:37:36 -0600 Subject: [PATCH 3/5] Fixed UnicodeDecodeError in GPU name retrieval --- general/gpu.py | 287 +++++++++++++++++++++++++------------------------ 1 file changed, 144 insertions(+), 143 deletions(-) diff --git a/general/gpu.py b/general/gpu.py index 0298085..6250d3c 100644 --- a/general/gpu.py +++ b/general/gpu.py @@ -1,162 +1,163 @@ import torch import pynvml import comfy.model_management - from ..core import logger - class CGPUInfo: - """ - This class is responsible for getting information from GPU (ONLY). - """ - cuda = False - pynvmlLoaded = False - cudaAvailable = False - torchDevice = 'cpu' - cudaDevice = 'cpu' - cudaDevicesFound = 0 - switchGPU = True - switchVRAM = True - switchTemperature = True - gpus = [] - gpusUtilization = [] - gpusVRAM = [] - gpusTemperature = [] - - def __init__(self): - try: - pynvml.nvmlInit() - self.pynvmlLoaded = True - except Exception as e: - self.pynvmlLoaded = False - logger.error('Could not init pynvml.' + str(e)) - - if self.pynvmlLoaded and pynvml.nvmlDeviceGetCount() > 0: - self.cudaDevicesFound = pynvml.nvmlDeviceGetCount() - - logger.info(f"GPU/s:") - - # for simulate multiple GPUs (for testing) interchange these comments: - # for deviceIndex in range(3): - # deviceHandle = pynvml.nvmlDeviceGetHandleByIndex(0) - for deviceIndex in range(self.cudaDevicesFound): - deviceHandle = pynvml.nvmlDeviceGetHandleByIndex(deviceIndex) - - gpuName = pynvml.nvmlDeviceGetName(deviceHandle) - - logger.info(f"{deviceIndex}) {gpuName}") - - self.gpus.append({ - 'index': deviceIndex, - 'name': gpuName, - }) - - # same index as gpus, with default values - self.gpusUtilization.append(True) - self.gpusVRAM.append(True) - self.gpusTemperature.append(True) - - self.cuda = True - logger.info(f'NVIDIA Driver: {pynvml.nvmlSystemGetDriverVersion()}') - else: - logger.warn('No GPU with CUDA detected.') - - try: - self.torchDevice = comfy.model_management.get_torch_device_name(comfy.model_management.get_torch_device()) - except Exception as e: - logger.error('Could not pick default device.' + str(e)) - - self.cudaDevice = 'cpu' if self.torchDevice == 'cpu' else 'cuda' - self.cudaAvailable = torch.cuda.is_available() - - if self.cuda and self.cudaAvailable and self.torchDevice == 'cpu': - logger.warn('CUDA is available, but torch is using CPU.') - - def getInfo(self): - logger.debug('Getting GPUs info...') - return self.gpus - - def getStatus(self): - # logger.debug('CGPUInfo getStatus') - gpuUtilization = -1 - gpuTemperature = -1 - vramUsed = -1 - vramTotal = -1 - vramPercent = -1 - - gpuType = '' + """ + This class is responsible for getting information from GPU (ONLY). + """ + cuda = False + pynvmlLoaded = False + cudaAvailable = False + torchDevice = 'cpu' + cudaDevice = 'cpu' + cudaDevicesFound = 0 + switchGPU = True + switchVRAM = True + switchTemperature = True gpus = [] + gpusUtilization = [] + gpusVRAM = [] + gpusTemperature = [] - if self.cudaDevice == 'cpu': - gpuType = 'cpu' - gpus.append({ - 'gpu_utilization': 0, - 'gpu_temperature': 0, - 'vram_total': 0, - 'vram_used': 0, - 'vram_used_percent': 0, - }) - else: - gpuType = self.cudaDevice + def __init__(self): + try: + pynvml.nvmlInit() + self.pynvmlLoaded = True + except Exception as e: + self.pynvmlLoaded = False + logger.error('Could not init pynvml.' + str(e)) - if self.pynvmlLoaded and self.cuda and self.cudaAvailable: + if self.pynvmlLoaded and pynvml.nvmlDeviceGetCount() > 0: + self.cudaDevicesFound = pynvml.nvmlDeviceGetCount() - # for simulate multiple GPUs (for testing) interchange these comments: - # for deviceIndex in range(3): - # deviceHandle = pynvml.nvmlDeviceGetHandleByIndex(0) - for deviceIndex in range(self.cudaDevicesFound): - deviceHandle = pynvml.nvmlDeviceGetHandleByIndex(deviceIndex) + logger.info(f"GPU/s:") - gpuUtilization = 0 - vramPercent = 0 - vramUsed = 0 - vramTotal = 0 - gpuTemperature = 0 + # for simulate multiple GPUs (for testing) interchange these comments: + # for deviceIndex in range(3): + # deviceHandle = pynvml.nvmlDeviceGetHandleByIndex(0) + for deviceIndex in range(self.cudaDevicesFound): + deviceHandle = pynvml.nvmlDeviceGetHandleByIndex(deviceIndex) - # GPU Utilization - if self.switchGPU and self.gpusUtilization[deviceIndex]: - try: - utilization = pynvml.nvmlDeviceGetUtilizationRates(deviceHandle) - gpuUtilization = utilization.gpu - except Exception as e: - if str(e) == "Unknown Error": - logger.error('For some reason, pynvml is not working in a laptop with only battery, try to connect and turn on the monitor') - else: - logger.error('Could not get GPU utilization.' + str(e)) + try: + gpuName = pynvml.nvmlDeviceGetName(deviceHandle).decode('utf-8', errors='ignore') + except UnicodeDecodeError as e: + gpuName = 'Unknown GPU (decoding error)' + print(f"UnicodeDecodeError: {e}") - logger.error('Monitor of GPU is turning off (not on UI!)') - self.switchGPU = False + logger.info(f"{deviceIndex}) {gpuName}") - # VRAM - if self.switchVRAM and self.gpusVRAM[deviceIndex]: - # Torch or pynvml?, pynvml is more accurate with the system, torch is more accurate with comfyUI - memory = pynvml.nvmlDeviceGetMemoryInfo(deviceHandle) - vramUsed = memory.used - vramTotal = memory.total + self.gpus.append({ + 'index': deviceIndex, + 'name': gpuName, + }) - # device = torch.device(gpuType) - # vramUsed = torch.cuda.memory_allocated(device) - # vramTotal = torch.cuda.get_device_properties(device).total_memory + # same index as gpus, with default values + self.gpusUtilization.append(True) + self.gpusVRAM.append(True) + self.gpusTemperature.append(True) - vramPercent = vramUsed / vramTotal * 100 + self.cuda = True + logger.info(f'NVIDIA Driver: {pynvml.nvmlSystemGetDriverVersion()}') + else: + logger.warn('No GPU with CUDA detected.') - # Temperature - if self.switchTemperature and self.gpusTemperature[deviceIndex]: - try: - gpuTemperature = pynvml.nvmlDeviceGetTemperature(deviceHandle, 0) - except Exception as e: - logger.error('Could not get GPU temperature. Turning off this feature. ' + str(e)) - self.switchTemperature = False + try: + self.torchDevice = comfy.model_management.get_torch_device_name(comfy.model_management.get_torch_device()) + except Exception as e: + logger.error('Could not pick default device.' + str(e)) - gpus.append({ - 'gpu_utilization': gpuUtilization, - 'gpu_temperature': gpuTemperature, - 'vram_total': vramTotal, - 'vram_used': vramUsed, - 'vram_used_percent': vramPercent, - }) + self.cudaDevice = 'cpu' if self.torchDevice == 'cpu' else 'cuda' + self.cudaAvailable = torch.cuda.is_available() - return { - 'device_type': gpuType, - 'gpus': gpus, - } + if self.cuda and self.cudaAvailable and self.torchDevice == 'cpu': + logger.warn('CUDA is available, but torch is using CPU.') + + def getInfo(self): + logger.debug('Getting GPUs info...') + return self.gpus + + def getStatus(self): + # logger.debug('CGPUInfo getStatus') + gpuUtilization = -1 + gpuTemperature = -1 + vramUsed = -1 + vramTotal = -1 + vramPercent = -1 + + gpuType = '' + gpus = [] + + if self.cudaDevice == 'cpu': + gpuType = 'cpu' + gpus.append({ + 'gpu_utilization': 0, + 'gpu_temperature': 0, + 'vram_total': 0, + 'vram_used': 0, + 'vram_used_percent': 0, + }) + else: + gpuType = self.cudaDevice + + if self.pynvmlLoaded and self.cuda and self.cudaAvailable: + # for simulate multiple GPUs (for testing) interchange these comments: + # for deviceIndex in range(3): + # deviceHandle = pynvml.nvmlDeviceGetHandleByIndex(0) + for deviceIndex in range(self.cudaDevicesFound): + deviceHandle = pynvml.nvmlDeviceGetHandleByIndex(deviceIndex) + + gpuUtilization = 0 + vramPercent = 0 + vramUsed = 0 + vramTotal = 0 + gpuTemperature = 0 + + # GPU Utilization + if self.switchGPU and self.gpusUtilization[deviceIndex]: + try: + utilization = pynvml.nvmlDeviceGetUtilizationRates(deviceHandle) + gpuUtilization = utilization.gpu + except Exception as e: + if str(e) == "Unknown Error": + logger.error('For some reason, pynvml is not working in a laptop with only battery, try to connect and turn on the monitor') + else: + logger.error('Could not get GPU utilization.' + str(e)) + + logger.error('Monitor of GPU is turning off (not on UI!)') + self.switchGPU = False + + # VRAM + if self.switchVRAM and self.gpusVRAM[deviceIndex]: + # Torch or pynvml?, pynvml is more accurate with the system, torch is more accurate with comfyUI + memory = pynvml.nvmlDeviceGetMemoryInfo(deviceHandle) + vramUsed = memory.used + vramTotal = memory.total + + # device = torch.device(gpuType) + # vramUsed = torch.cuda.memory_allocated(device) + # vramTotal = torch.cuda.get_device_properties(device).total_memory + + vramPercent = vramUsed / vramTotal * 100 + + # Temperature + if self.switchTemperature and self.gpusTemperature[deviceIndex]: + try: + gpuTemperature = pynvml.nvmlDeviceGetTemperature(deviceHandle, 0) + except Exception as e: + logger.error('Could not get GPU temperature. Turning off this feature. ' + str(e)) + self.switchTemperature = False + + gpus.append({ + 'gpu_utilization': gpuUtilization, + 'gpu_temperature': gpuTemperature, + 'vram_total': vramTotal, + 'vram_used': vramUsed, + 'vram_used_percent': vramPercent, + }) + + return { + 'device_type': gpuType, + 'gpus': gpus, + } From 9c9d58854ed1feae0eb847954939b7eb455bb5b4 Mon Sep 17 00:00:00 2001 From: petercham <3765653+petercham@users.noreply.github.com> Date: Thu, 20 Jun 2024 12:06:36 +0800 Subject: [PATCH 4/5] =?UTF-8?q?Update=20image.py=EF=BC=8CSupported=20webp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supports extraction of prompt and workflow from exif information of webp format images. --- nodes/image.py | 49 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/nodes/image.py b/nodes/image.py index 8de2c8a..0df1799 100644 --- a/nodes/image.py +++ b/nodes/image.py @@ -262,7 +262,13 @@ class CImageLoadWithMetadata: def execute(self, image): image_path = folder_paths.get_annotated_filepath(image) - img, prompt, metadata = buildMetadata(image_path) + img = Image.open(image_path) + if img.format == 'WEBP': + # Use piexif to extract EXIF data from WebP image + exif_data = piexif.load(image_path) + prompt, metadata = self.process_exif_data(exif_data) + else: + img, prompt, metadata = buildMetadata(image_path) img = ImageOps.exif_transpose(img) image = img.convert("RGB") @@ -275,22 +281,33 @@ class CImageLoadWithMetadata: mask = torch.zeros((64, 64), dtype=torch.float32, device="cpu") return image, mask.unsqueeze(0), prompt, metadata + + def process_exif_data(self, exif_data): + metadata = {} + # 检查 '0th' 键下的 271 值,提取 Prompt 信息 + if '0th' in exif_data and 271 in exif_data['0th']: + prompt_data = exif_data['0th'][271].decode('utf-8') + # 移除可能的前缀 'Prompt:' + prompt_data = prompt_data.replace('Prompt:', '', 1) + # 假设 prompt_data 是一个字符串,尝试将其转换为 JSON 对象 + try: + metadata['prompt'] = json.loads(prompt_data) + except json.JSONDecodeError: + metadata['prompt'] = prompt_data - @classmethod - def IS_CHANGED(cls, image): - image_path = folder_paths.get_annotated_filepath(image) - m = hashlib.sha256() - with open(image_path, 'rb') as f: - m.update(f.read()) - return m.digest().hex() - - @classmethod - def VALIDATE_INPUTS(cls, image): - if not folder_paths.exists_annotated_filepath(image): - return "Invalid image file: {}".format(image) - - return True - + # 检查 '0th' 键下的 270 值,提取 Workflow 信息 + if '0th' in exif_data and 270 in exif_data['0th']: + workflow_data = exif_data['0th'][270].decode('utf-8') + # 移除可能的前缀 'Workflow:' + workflow_data = workflow_data.replace('Workflow:', '', 1) + try: + # 尝试将字节字符串转换为 JSON 对象 + metadata['workflow'] = json.loads(workflow_data) + except json.JSONDecodeError: + # 如果转换失败,则将原始字符串存储在 metadata 中 + metadata['workflow'] = workflow_data + metadata.update(exif_data) + return metadata class CImageSaveWithExtraMetadata(SaveImage): def __init__(self): From 4fa0b9ddeebd90596223e2fee4955e504f2414fd Mon Sep 17 00:00:00 2001 From: Crystian Date: Mon, 1 Jul 2024 01:21:08 +0200 Subject: [PATCH 5/5] some fixes: utf-8 from nvidia driver is supported comfyui files added read metadata from webp --- .others/version.py | 20 ++++++++++++++++++++ __init__.py | 2 +- core/version.py | 2 +- docs/screwdriver.png | Bin 0 -> 2644 bytes general/gpu.py | 8 +++++++- nodes/image.py | 37 +++++++++++++++++++++++++++++-------- pyproject.toml | 28 ++++++++++++++-------------- requirements.txt | 1 + version | 2 +- 9 files changed, 74 insertions(+), 26 deletions(-) create mode 100644 docs/screwdriver.png diff --git a/.others/version.py b/.others/version.py index 50922f6..ac4beff 100644 --- a/.others/version.py +++ b/.others/version.py @@ -38,3 +38,23 @@ print(f"> File2 {file2}") with open(file2, "w") as f2: f2.writelines([f"version = \"{version}\"\n"]) + +# modify pyproject.toml file with @version +file3 = os.path.join(os.path.dirname(__file__), "../pyproject.toml") +print(f"> File3: {file3}") + +file3VersionFound = False +# lines = None +with open(file3, "r") as f: + lines = f.readlines() + for line in lines: + + if "version = " in line: + file3VersionFound = True + lines[lines.index(line)] = f"version = \"{version}\"\n" + +if not file3VersionFound: + raise RuntimeError(f"Version label not found in file: {file3}") + +with open(file3, "w") as f3: + f3.writelines(lines) diff --git a/__init__.py b/__init__.py index 55bde3b..ed1d33f 100644 --- a/__init__.py +++ b/__init__.py @@ -2,7 +2,7 @@ @author: Crystian @title: Crystools @nickname: Crystools -@version: 1.12.0 +@version: 1.13.0 @project: "https://github.com/crystian/ComfyUI-Crystools", @description: Plugins for multiples uses, mainly for debugging, you need them! IG: https://www.instagram.com/crystian.ia """ diff --git a/core/version.py b/core/version.py index 6f73501..e3d9739 100644 --- a/core/version.py +++ b/core/version.py @@ -1 +1 @@ -version = "1.12.0" +version = "1.13.0" diff --git a/docs/screwdriver.png b/docs/screwdriver.png new file mode 100644 index 0000000000000000000000000000000000000000..61a05747f309e5839bc3eab633be3153f06a23fe GIT binary patch literal 2644 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.5.0", "pynvml", "py-cpuinfo"] - -[project.urls] -Repository = "https://github.com/crystian/ComfyUI-Crystools" - -[tool.comfy] -PublisherId = "" -DisplayName = "ComfyUI-Crystools" -Icon = "" +[project] +name = "comfyui-crystools" +description = "With this suit, you can see the resources monitor, progress bar & time elapsed, metadata and compare between two images, compare between two JSONs, show any value to console/display, pipes, and more!\nThis provides better nodes to load/save images, previews, etc, and see \"hidden\" data without loading a new workflow." +version = "1.13.0" +license = "LICENSE" +dependencies = ["deepdiff", "torch", "numpy", "Pillow>=9.5.0", "pynvml", "py-cpuinfo"] + +[project.urls] +Repository = "https://github.com/crystian/ComfyUI-Crystools" + +[tool.comfy] +PublisherId = "crystian" +DisplayName = "ComfyUI-Crystools" +Icon = "https://raw.githubusercontent.com/crystian/ComfyUI-Crystools/main/docs/screwdriver.png" diff --git a/requirements.txt b/requirements.txt index 21f0496..d61acb5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ numpy Pillow>=9.5.0 pynvml py-cpuinfo +piexif diff --git a/version b/version index 0eed1a2..feaae22 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.12.0 +1.13.0