preview from image and metadata

This commit is contained in:
Crystian
2023-12-25 03:30:52 +01:00
parent c098167a24
commit 853dbfeef0
6 changed files with 168 additions and 121 deletions
+1 -1
View File
@@ -137,7 +137,7 @@ primerp pryecto en python
todo
test
persistir imagen
no sobrevive al f5 por mas que se vea la data
agregar desde menu o doble click
## Installation
+5 -3
View File
@@ -11,7 +11,7 @@ from .nodes.primitive import CBoolean, CText, CTextML, CInteger, CFloat
from .nodes.switch import CSwitchBooleanAny, CSwitchBooleanLatent, CSwitchBooleanConditioning, CSwitchBooleanImage, \
CSwitchBooleanString
from .nodes.debugger import CConsoleAny
from .nodes.image import CImagePreviewAdvance, CImageLoadWithMetadata, CImageShowResolution
from .nodes.image import CImagePreviewFromImage, CImageLoadWithMetadata, CImageShowResolution, CImagePreviewFromMetadata
from .nodes.list import CListAny, CListString
from .nodes.pipe import CPipeToAny, CPipeFromAny
from .nodes.utils import CUtilsCompareJsons, CUtilsStatSystem, CMetadataExtractor, CMetadataCompare
@@ -39,7 +39,8 @@ NODE_CLASS_MAPPINGS = {
CLASSES.CIMAGE_LOAD_METADATA_NAME.value: CImageLoadWithMetadata,
CLASSES.CIMAGE_SHOW_RESOLUTION_NAME.value: CImageShowResolution,
CLASSES.CIMAGE_PREVIEW_ADVANCED_NAME.value: CImagePreviewAdvance,
CLASSES.CIMAGE_PREVIEW_IMAGE_NAME.value: CImagePreviewFromImage,
CLASSES.CIMAGE_PREVIEW_METADATA_NAME.value: CImagePreviewFromMetadata,
CLASSES.CUTILS_METADATA_EXTRACTOR_NAME.value: CMetadataExtractor,
CLASSES.CUTILS_METADATA_COMPARATOR_NAME.value: CMetadataCompare,
@@ -70,7 +71,8 @@ NODE_DISPLAY_NAME_MAPPINGS = {
CLASSES.CIMAGE_LOAD_METADATA_NAME.value: CLASSES.CIMAGE_LOAD_METADATA_DESC.value,
CLASSES.CIMAGE_SHOW_RESOLUTION_NAME.value: CLASSES.CIMAGE_SHOW_RESOLUTION_DESC.value,
CLASSES.CIMAGE_PREVIEW_ADVANCED_NAME.value: CLASSES.CIMAGE_PREVIEW_ADVANCED_DESC.value,
CLASSES.CIMAGE_PREVIEW_IMAGE_NAME.value: CLASSES.CIMAGE_PREVIEW_IMAGE_DESC.value,
CLASSES.CIMAGE_PREVIEW_METADATA_NAME.value: CLASSES.CIMAGE_PREVIEW_METADATA_DESC.value,
CLASSES.CUTILS_METADATA_EXTRACTOR_NAME.value: CLASSES.CUTILS_METADATA_EXTRACTOR_DESC.value,
CLASSES.CUTILS_METADATA_COMPARATOR_NAME.value: CLASSES.CUTILS_METADATA_COMPARATOR_DESC.value,
+4 -2
View File
@@ -46,8 +46,10 @@ class CLASSES(Enum):
CIMAGE_LOAD_METADATA_DESC = "Load image with metadata [Crystools]"
CIMAGE_SHOW_RESOLUTION_NAME = "Show resolution [Crystools]"
CIMAGE_SHOW_RESOLUTION_DESC = "Show resolution [Crystools]"
CIMAGE_PREVIEW_ADVANCED_NAME = "Preview image advanced [Crystools]"
CIMAGE_PREVIEW_ADVANCED_DESC = "Preview image advanced (prompt) [Crystools]"
CIMAGE_PREVIEW_IMAGE_NAME = "Preview from image [Crystools]"
CIMAGE_PREVIEW_IMAGE_DESC = "Preview from image (show prompt) [Crystools]"
CIMAGE_PREVIEW_METADATA_NAME = "Preview from metadata [Crystools]"
CIMAGE_PREVIEW_METADATA_DESC = "Preview from metadata (show prompt) [Crystools]"
CUTILS_METADATA_EXTRACTOR_NAME = "Metadata extractor [Crystools]"
CUTILS_METADATA_EXTRACTOR_DESC = "Metadata extractor [Crystools]"
+153 -110
View File
@@ -17,15 +17,14 @@ from ..core import CATEGORY, CONFIG, METADATA_RAW,TEXTS, setWidgetValues, logger
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "comfy"))
class CImagePreviewAdvance(PreviewImage):
class CImagePreviewFromImage(PreviewImage):
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 = 1
self.output_dir = folder_paths.get_temp_directory()
self.type = "temp"
self.prefix_append = "_" + ''.join(random.choice("abcdefghijklmnopqrstupvxyz") for x in range(5))
self.compress_level = 1
self.data_cached = None
# self.old_data = None
# self.old_text = None
self.data_cached_text = None
@classmethod
def INPUT_TYPES(cls):
@@ -35,7 +34,6 @@ class CImagePreviewAdvance(PreviewImage):
},
"optional": {
"image": ("IMAGE",),
"metadata_raw": METADATA_RAW,
},
"hidden": {
"prompt": "PROMPT",
@@ -48,82 +46,116 @@ class CImagePreviewAdvance(PreviewImage):
RETURN_NAMES = ("Metadata RAW",)
OUTPUT_NODE = True
FUNCTION = "preview"
FUNCTION = "execute"
def preview(self, image=None, metadata_raw=None, prompt=None, extra_pnginfo=None):
result = []
def execute(self, image=None, prompt=None, extra_pnginfo=None):
text = ""
images = []
title = ""
data = {
"result": [],
"result": [''],
"ui": {
"text": [],
"text": [''],
"images": [],
}
}
# TODO Refactor with unit tests
if image is not None:
saved = self.save_images(image, "crystools/i", prompt, extra_pnginfo)
image = saved["ui"]["images"][0]
image_path = Path(self.output_dir).joinpath(image["subfolder"], image["filename"])
img, promptFromImage, metadata = buildMetadata(image_path)
images = [image]
result = metadata
data["result"] = [result]
data["ui"]["images"] = images
title = "Source: Image link \n"
text += buildPreviewText(metadata, promptFromImage)
text += f"Current prompt (NO FROM IMAGE!):\n"
text += json.dumps(json.loads(json.loads(promptFromImage)), indent=CONFIG["indent"])
self.data_cached_text = text
self.data_cached = data
elif image is None and self.data_cached is not None:
title = "Source: Image link - CACHED\n"
data = self.data_cached
text = self.data_cached_text
else:
logger.debug("Source: Empty on CImagePreviewFromImage")
text = "Source: Empty"
data['ui']['text'] = [title + text]
return data
class CImagePreviewFromMetadata(PreviewImage):
def __init__(self):
self.data_cached = None
self.data_cached_text = None
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
# if it is required, in next node does not receive any value even the cache!
},
"optional": {
"metadata_raw": METADATA_RAW,
},
}
CATEGORY = CATEGORY.MAIN.value + CATEGORY.IMAGE.value
RETURN_TYPES = ("METADATA_RAW",)
RETURN_NAMES = ("Metadata RAW",)
OUTPUT_NODE = True
FUNCTION = "execute"
def execute(self, metadata_raw=None):
text = ""
title = ""
data = {
"result": [''],
"ui": {
"text": [''],
"images": [],
}
}
# TODO define snakecase?
if metadata_raw is not None:
text += "Source: Metadata RAW\n"
text += json.dumps(metadata_raw, indent=CONFIG["indent"])
promptFromImage = metadata_raw["prompt"]
title = "Source: Metadata RAW\n"
text += buildPreviewText(metadata_raw, promptFromImage)
text += f"Prompt from image:\n"
text += json.dumps(promptFromImage, indent=CONFIG["indent"])
images = self.resolveImage(metadata_raw["fileinfo"]["filename"])
result = metadata_raw
elif self.data_cached is not None:
text += "Source: Metadata RAW - CACHED\n"
text += json.dumps(self.data_cached, indent=CONFIG["indent"])
data["result"] = [result]
data["ui"]["images"] = images
self.data_cached_text = text
self.data_cached = data
elif metadata_raw is None and self.data_cached is not None:
title = "Source: Metadata RAW - CACHED\n"
data = self.data_cached
text = self.data_cached_text
images = self.resolveImage(self.data_cached["fileinfo"]["filename"])
result = self.data_cached
else:
text += "Source: Empty"
data["result"] = [result]
data["ui"]["text"] = [text]
data["ui"]["images"] = images
self.data_cached = metadata_raw
logger.debug("Source: Empty on CImagePreviewFromMetadata")
text = "Source: Empty"
data["ui"]["text"] = [title + text]
return data
# source = ""
# saved["ui"]["text"] = [source + text + json.dumps(prompt, indent=CONFIG["indent"])]
# saved["result"] = [metadataFromPng]
# if image is None and self.old_data is None:
# text = "No image linked"
# return {"ui": {"text": [text]}, "result": [None]}
#
# if image is None and self.old_data is not None:
# prompt_to_return = self.old_data["prompt"]
#
# source = f"Source: Cache\n"
# return {"ui": {"text": [source + self.old_text + json.dumps(json.loads(prompt_to_return), indent=CONFIG["indent"])]}, "result": [self.old_data]}
# saved = self.save_images(image, "crystools", prompt, extra_pnginfo)
# filename = saved["ui"]["images"][0]["filename"]
# image_path = os.path.join(self.output_dir, filename)
# img = Image.open(image_path)
# time = os.path.getmtime(image_path)
# time_human = datetime.datetime.fromtimestamp(time)
# text += f"File: {image_path}\n"
# text += f"Resolution: {img.width}x{img.height}\n"
# text += f"Date: {time_human}\n"
# text += f"Size: {get_size(image_path)}\n"
# text += f"Current prompt (NO FROM IMAGE!):\n"
# metadataFromPng = {}
#
# if isinstance(img, PngImageFile):
# metadataFromPng = img.info
#
# source = f"Source: {source}\n"
# saved["ui"]["text"] = [source + text + json.dumps(prompt, indent=CONFIG["indent"])]
# saved["result"] = [metadataFromPng]
#
# self.old_data = metadataFromPng
# self.old_text = text
def resolveImage(self, filename=None):
images = []
@@ -224,50 +256,8 @@ class CImageLoadWithMetadata:
def execute(self, image):
image_path = folder_paths.get_annotated_filepath(image)
if Path(image_path).is_file() is False:
raise Exception(TEXTS.FILE_NOT_FOUND.value)
img = Image.open(image_path)
metadata = {}
prompt = {}
metadata["fileinfo"] = {
"filename": Path(image_path).as_posix(),
"resolution": {
"x": img.width,
"y": img.height,
},
"resolution_text": f"{img.width}x{img.height}",
"date": str(datetime.datetime.fromtimestamp(os.path.getmtime(image_path))),
"size": str(get_size(image_path)),
}
# only for png files
if isinstance(img, PngImageFile):
metadataFromImg = img.info
# for all metadataFromImg convert to string (but not for workflow and prompt!)
for k, v in metadataFromImg.items():
# from ComfyUI
if k == "workflow":
try:
metadata["workflow"] = json.loads(metadataFromImg["workflow"])
except Exception as e:
logger.warn(f"Error parsing metadataFromImg 'workflow': {e}")
# from ComfyUI
elif k == "prompt":
try:
metadata["prompt"] = json.loads(metadataFromImg["prompt"])
# extract prompt from metadataFromImg
prompt = json.dumps(metadataFromImg["prompt"], indent=CONFIG["indent"])
except Exception as e:
logger.warn(f"Error parsing metadataFromImg 'prompt': {e}")
else:
metadata[str(k)] = str(v)
img, prompt, metadata = buildMetadata(image_path)
img = ImageOps.exif_transpose(img)
image = img.convert("RGB")
@@ -295,3 +285,56 @@ class CImageLoadWithMetadata:
return "Invalid image file: {}".format(image)
return True
def buildMetadata(image_path):
if Path(image_path).is_file() is False:
raise Exception(TEXTS.FILE_NOT_FOUND.value)
img = Image.open(image_path)
metadata = {}
prompt = {}
metadata["fileinfo"] = {
"filename": Path(image_path).as_posix(),
"resolution": f"{img.width}x{img.height}",
"date": str(datetime.datetime.fromtimestamp(os.path.getmtime(image_path))),
"size": str(get_size(image_path)),
}
# only for png files
if isinstance(img, PngImageFile):
metadataFromImg = img.info
# for all metadataFromImg convert to string (but not for workflow and prompt!)
for k, v in metadataFromImg.items():
# from ComfyUI
if k == "workflow":
try:
metadata["workflow"] = json.loads(metadataFromImg["workflow"])
except Exception as e:
logger.warn(f"Error parsing metadataFromImg 'workflow': {e}")
# from ComfyUI
elif k == "prompt":
try:
metadata["prompt"] = json.loads(metadataFromImg["prompt"])
# extract prompt from metadataFromImg
prompt = json.dumps(metadataFromImg["prompt"], indent=CONFIG["indent"])
except Exception as e:
logger.warn(f"Error parsing metadataFromImg 'prompt': {e}")
else:
metadata[str(k)] = str(v)
return img, prompt, metadata
def buildPreviewText(metadata, prompt):
text = f"File: {metadata['fileinfo']['filename']}\n"
text += f"Resolution: {metadata['fileinfo']['resolution']}\n"
text += f"Date: {metadata['fileinfo']['date']}\n"
text += f"Size: {metadata['fileinfo']['size']}\n"
return text
+3 -3
View File
@@ -9,20 +9,20 @@ class CMetadataExtractor:
def INPUT_TYPES(cls):
return {
"required": {
"metadata_raw": METADATA_RAW,
},
"optional": {
"metadata_raw": METADATA_RAW,
}
}
CATEGORY = CATEGORY.MAIN.value + CATEGORY.UTILS.value
RETURN_TYPES = ("JSON", "JSON", "JSON", "STRING", "STRING")
RETURN_NAMES = ("prompt", "workflow", "file info", "raw to property", "raw to csv")
OUTPUT_NODE = True
# OUTPUT_NODE = True
FUNCTION = "execute"
def execute(self, metadata_raw):
def execute(self, metadata_raw=None):
prompt = {}
workflow = {}
fileinfo = {}
+2 -2
View File
@@ -13,8 +13,8 @@ app.registerExtension({
app.registerExtension({
name: 'Crystools.Image.PreviewImageAdvance',
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name === 'Preview image advanced [Crystools]') {
displayContext(nodeType, app, 0, true);
if (nodeData.name === 'Preview from image [Crystools]' || nodeData.name === 'Preview from metadata [Crystools]') {
displayContext(nodeType, app, 0);
}
},
});