From a3e92da60fc8730deceee50868a79fac4de9a4e2 Mon Sep 17 00:00:00 2001 From: jueming Date: Thu, 8 May 2025 16:33:40 +0800 Subject: [PATCH] fix image preview --- js/image_downloader_preview.js | 239 ++++++++++++++++++++------------- js/index.js | 3 +- nodes/image_downloader.py | 30 +++-- 3 files changed, 167 insertions(+), 105 deletions(-) diff --git a/js/image_downloader_preview.js b/js/image_downloader_preview.js index dfc647e..62fb534 100644 --- a/js/image_downloader_preview.js +++ b/js/image_downloader_preview.js @@ -89,6 +89,31 @@ app.registerExtension({ // 创建预览图像对象 this._preview_image = new Image(); this._preview_path = null; + + // 检查是否有image_data小部件,如果没有则创建一个隐藏的 + let image_data_widget = this.widgets.find(w => w.name === "image_data"); + if (!image_data_widget) { + image_data_widget = this.addWidget("text", "image_data", "", null); + image_data_widget.hidden = true; + logDebug("创建隐藏的image_data小部件"); + } else { + logDebug("找到现有的image_data小部件"); + } + + // 保存对image_data小部件的引用 + this._image_data_widget = image_data_widget; + + // 设置图像加载事件处理 + this._preview_image.onload = () => { + logDebug(`图像加载成功: ${this._preview_image.width}x${this._preview_image.height}`); + this.setDirtyCanvas(true, true); + }; + + this._preview_image.onerror = (err) => { + logDebug(`图像加载失败`); + logDetailedDebug("加载错误:", err); + }; + logDebug("创建预览图像对象"); // 使节点可序列化 @@ -145,62 +170,87 @@ app.registerExtension({ ctx.strokeRect(previewX, previewY, previewWidth, previewHeight); logDebug("绘制预览区域背景"); - // 如果有预览图像且已加载完成 - if (this._preview_image && this._preview_image.complete && this._preview_image.width > 0) { - logDebug(`有预览图像: ${this._preview_image.width}x${this._preview_image.height}`); - try { - // 计算图像显示大小,保持比例 - const img = this._preview_image; - const imgRatio = img.width / img.height; - let drawWidth, drawHeight; - - if (imgRatio > previewWidth / previewHeight) { - // 图像较宽,以宽度为限 - drawWidth = previewWidth - 10; - drawHeight = drawWidth / imgRatio; - logDebug(`图像较宽,调整为 ${drawWidth}x${drawHeight}`); - } else { - // 图像较高,以高度为限 - drawHeight = previewHeight - 10; - drawWidth = drawHeight * imgRatio; - logDebug(`图像较高,调整为 ${drawWidth}x${drawHeight}`); + // 获取image_data小部件中的值 + const imageData = this._image_data_widget ? this._image_data_widget.value : ""; + + // 如果有图像数据,则加载并显示 + if (imageData && imageData !== "") { + logDebug("从image_data小部件获取到数据"); + + // 如果图像源不是当前的数据,则更新 + if (this._preview_image.src !== imageData) { + logDebug("更新预览图像源"); + this._preview_image.src = imageData; + } + + // 如果有预览图像且已加载完成 + if (this._preview_image && this._preview_image.complete && this._preview_image.width > 0) { + logDebug(`有预览图像: ${this._preview_image.width}x${this._preview_image.height}`); + try { + // 计算图像显示大小,保持比例 + const img = this._preview_image; + const imgRatio = img.width / img.height; + let drawWidth, drawHeight; + + if (imgRatio > previewWidth / previewHeight) { + // 图像较宽,以宽度为限 + drawWidth = previewWidth - 10; + drawHeight = drawWidth / imgRatio; + logDebug(`图像较宽,调整为 ${drawWidth}x${drawHeight}`); + } else { + // 图像较高,以高度为限 + drawHeight = previewHeight - 10; + drawWidth = drawHeight * imgRatio; + logDebug(`图像较高,调整为 ${drawWidth}x${drawHeight}`); + } + + // 计算居中位置 + const drawX = previewX + (previewWidth - drawWidth) / 2; + const drawY = previewY + (previewHeight - drawHeight) / 2; + logDebug(`绘制位置: x=${drawX}, y=${drawY}`); + + // 绘制预览图像 + ctx.drawImage(img, drawX, drawY, drawWidth, drawHeight); + logDebug("绘制预览图像完成"); + + // 显示图像尺寸 + ctx.fillStyle = "#ffffff"; + ctx.font = "12px Arial"; + ctx.textAlign = "left"; + ctx.fillText(`${img.naturalWidth}x${img.naturalHeight}`, + previewX + 5, previewY + previewHeight - 5); + logDebug("绘制图像尺寸信息"); + } catch (error) { + logDebug(`绘制图像错误: ${error.message}`); + // 显示错误信息 + ctx.fillStyle = "#ff5555"; + ctx.font = "12px Arial"; + ctx.textAlign = "center"; + ctx.fillText("图像加载或绘制出错", + previewX + previewWidth/2, previewY + previewHeight/2); + ctx.textAlign = "left"; + } + } else { + logDebug("无预览图像或图像未加载完成"); + if (this._preview_image) { + logDetailedDebug("预览图像状态:", { + complete: this._preview_image.complete, + width: this._preview_image.width, + src: this._preview_image.src ? this._preview_image.src.substring(0, 50) + "..." : "" + }); } - // 计算居中位置 - const drawX = previewX + (previewWidth - drawWidth) / 2; - const drawY = previewY + (previewHeight - drawHeight) / 2; - logDebug(`绘制位置: x=${drawX}, y=${drawY}`); - - // 绘制预览图像 - ctx.drawImage(img, drawX, drawY, drawWidth, drawHeight); - logDebug("绘制预览图像完成"); - - // 显示图像尺寸 - ctx.fillStyle = "#ffffff"; - ctx.font = "12px Arial"; - ctx.textAlign = "left"; - ctx.fillText(`${img.naturalWidth}x${img.naturalHeight}`, - previewX + 5, previewY + previewHeight - 5); - logDebug("绘制图像尺寸信息"); - } catch (error) { - logDebug(`绘制图像错误: ${error.message}`); - // 显示错误信息 - ctx.fillStyle = "#ff5555"; - ctx.font = "12px Arial"; + // 没有图像时显示提示 + ctx.fillStyle = "#aaaaaa"; + ctx.font = "14px Arial"; ctx.textAlign = "center"; - ctx.fillText("图像加载或绘制出错", - previewX + previewWidth/2, previewY + previewHeight/2); + ctx.fillText("图片正在加载...", + previewX + previewWidth/2, previewY + previewHeight/2); ctx.textAlign = "left"; + logDebug("绘制加载提示文本"); } } else { - logDebug("无预览图像或图像未加载完成"); - if (this._preview_image) { - logDetailedDebug("预览图像状态:", { - complete: this._preview_image.complete, - width: this._preview_image.width, - src: this._preview_image.src - }); - } + logDebug("无图像数据"); // 没有图像时显示提示 ctx.fillStyle = "#aaaaaa"; @@ -242,50 +292,53 @@ app.registerExtension({ // 输出消息 logDetailedDebug("输出消息:", message.outputs); - // 检查是否有图像路径输出 - const imagePath = message.outputs.image_path; - if (!imagePath || typeof imagePath !== "string") { - logDebug("无效的图像路径"); - return; - } - - logDebug(`获取到图像路径: ${imagePath}`); - - // 如果路径无效或与上次相同,则不重新加载 - if (imagePath.startsWith("错误:") || imagePath === this._preview_path) { - logDebug("图像路径无效或与上次相同"); - return; - } - - this._preview_path = imagePath; - logDebug(`设置新的预览路径: ${this._preview_path}`); - - // 创建正确的预览URL - const previewUrl = `/view?filename=${encodeURIComponent(imagePath)}&ts=${Date.now()}`; - logDebug(`预览URL: ${previewUrl}`); - - // 设置图像加载事件 - this._preview_image.onload = () => { - logDebug(`图像加载成功: ${this._preview_image.width}x${this._preview_image.height}`); + // 检查是否有图像数据输出 + const imageData = message.outputs.image_data; + if (imageData && typeof imageData === "string" && imageData !== "") { + logDebug("发现image_data输出"); + + // 更新image_data小部件 + if (this._image_data_widget) { + this._image_data_widget.value = imageData; + logDebug("已更新image_data小部件"); + } else { + logDebug("没有找到image_data小部件"); + } + + // 强制请求重绘 this.setDirtyCanvas(true, true); - }; - - this._preview_image.onerror = (err) => { - logDebug(`图像加载失败`); - logDetailedDebug("加载错误:", err); - // 尝试使用备选URL加载 - const alternativeUrl = `/output/${encodeURIComponent(imagePath)}?ts=${Date.now()}`; - logDebug(`尝试备选URL: ${alternativeUrl}`); - this._preview_image.src = alternativeUrl; - }; - - // 加载图像 - this._preview_image.src = previewUrl; - logDebug(`开始加载图像: ${this._preview_image.src}`); - - // 强制请求重绘 - this.setDirtyCanvas(true, true); - logDebug("请求画布更新"); + logDebug("请求画布更新"); + } else { + // 检查是否有图像路径输出 + const imagePath = message.outputs.image_path; + if (!imagePath || typeof imagePath !== "string") { + logDebug("无效的图像路径或数据"); + return; + } + + logDebug(`获取到图像路径: ${imagePath}`); + + // 如果路径无效或与上次相同,则不重新加载 + if (imagePath.startsWith("错误:") || imagePath === this._preview_path) { + logDebug("图像路径无效或与上次相同"); + return; + } + + this._preview_path = imagePath; + logDebug(`设置新的预览路径: ${this._preview_path}`); + + // 创建正确的预览URL + const previewUrl = `/view?filename=${encodeURIComponent(imagePath)}&ts=${Date.now()}`; + logDebug(`预览URL: ${previewUrl}`); + + // 加载图像 + this._preview_image.src = previewUrl; + logDebug(`开始加载图像: ${this._preview_image.src}`); + + // 强制请求重绘 + this.setDirtyCanvas(true, true); + logDebug("请求画布更新"); + } }; } }); diff --git a/js/index.js b/js/index.js index e1b42ee..6da254d 100644 --- a/js/index.js +++ b/js/index.js @@ -1,4 +1,5 @@ // KLingAI扩展入口点 import './image_downloader_preview.js'; -console.log("[JM-KLingAI] 扩展入口点加载完成"); \ No newline at end of file +// 记录扩展加载信息 +console.log("[JM-KLingAI] 扩展入口点加载完成,版本 1.0.1"); \ No newline at end of file diff --git a/nodes/image_downloader.py b/nodes/image_downloader.py index 9df8029..02f0491 100644 --- a/nodes/image_downloader.py +++ b/nodes/image_downloader.py @@ -29,6 +29,7 @@ class KLingAIImageDownloader: self.last_downloaded_image = None self.last_image_path = None self.preview_available = False # 标记预览是否可用 + self.image_data = "" # 存储base64图片数据 print(f"初始化KLingAIImageDownloader,输出目录: {self.default_output_dir}") @classmethod @@ -48,15 +49,17 @@ class KLingAIImageDownloader: "multiline": False, "placeholder": "Optional: Custom output directory path within outputs folder" }), + # 隐藏字段用于存储base64图片数据 + "image_data": ("STRING", {"default": "", "multiline": True, "visible": False}), } } - RETURN_TYPES = ("IMAGE", "STRING", "STRING") - RETURN_NAMES = ("IMAGE", "image_path", "image_url") + RETURN_TYPES = ("IMAGE", "STRING", "STRING", "STRING") + RETURN_NAMES = ("IMAGE", "image_path", "image_url", "image_data") FUNCTION = "download_image" CATEGORY = "JM-KLingAI-API" OUTPUT_NODE = True # 标记为可作为终端节点的节点 - OUTPUT_IS_LIST = (False, False, False) # 指示输出不是列表 + OUTPUT_IS_LIST = (False, False, False, False) # 指示输出不是列表 def get_next_sequence_number(self, directory, filename_prefix): """ @@ -107,7 +110,7 @@ class KLingAIImageDownloader: img_str = base64.b64encode(buffered.getvalue()).decode("utf-8") return f"data:image/png;base64,{img_str}" - def download_image(self, image_url, filename_prefix="KLingAI", custom_output_dir=""): + def download_image(self, image_url, filename_prefix="KLingAI", custom_output_dir="", image_data=""): """ Download image from URL and save to local directory """ @@ -120,7 +123,7 @@ class KLingAIImageDownloader: print(error_msg) # 创建小的空白图像用于显示错误信息 empty_tensor = torch.zeros((1, 64, 64, 3), dtype=torch.float32) - return (empty_tensor, error_msg, image_url) + return (empty_tensor, error_msg, image_url, "") if not filename_prefix: filename_prefix = "KLingAI" @@ -195,6 +198,11 @@ class KLingAIImageDownloader: # 存储最后下载的图像以便在UI中显示 self.last_downloaded_image = pil_image + # 转换为base64格式用于前端显示 + base64_data = self.image_to_base64(pil_image) + self.image_data = base64_data + print(f"生成base64图片数据,长度: {len(base64_data) if base64_data else 0}") + # 转换为numpy数组,并规范化到0-1范围 image_array = np.array(pil_image).astype(np.float32) / 255.0 @@ -203,31 +211,31 @@ class KLingAIImageDownloader: print(f"转换为PyTorch张量,形状: {image_tensor.shape}") print(f"成功加载图片,准备返回 - 张量形状: {image_tensor.shape}") - return (image_tensor, rel_filepath, image_url) + return (image_tensor, rel_filepath, image_url, base64_data) except Exception as e: print(f"图片加载错误: {str(e)}") # 创建小的空白图像 empty_tensor = torch.zeros((1, 64, 64, 3), dtype=torch.float32) - return (empty_tensor, rel_filepath, image_url) + return (empty_tensor, rel_filepath, image_url, "") except ValueError as ve: error_msg = f"参数错误: {str(ve)}" print(error_msg) # 创建小的空白图像 empty_tensor = torch.zeros((1, 64, 64, 3), dtype=torch.float32) - return (empty_tensor, error_msg, image_url) + return (empty_tensor, error_msg, image_url, "") except requests.exceptions.RequestException as re: error_msg = f"下载错误: {str(re)}" print(error_msg) # 创建小的空白图像 empty_tensor = torch.zeros((1, 64, 64, 3), dtype=torch.float32) - return (empty_tensor, error_msg, image_url) + return (empty_tensor, error_msg, image_url, "") except Exception as e: error_msg = f"图片下载错误: {str(e)}" print(error_msg) # 创建小的空白图像 empty_tensor = torch.zeros((1, 64, 64, 3), dtype=torch.float32) - return (empty_tensor, error_msg, image_url) + return (empty_tensor, error_msg, image_url, "") def save_image_preview_info(self, filepath): """ @@ -274,6 +282,6 @@ class KLingAIImageDownloader: # 确保节点可以在没有连接的情况下运行 @classmethod - def IS_CHANGED(cls, image_url, filename_prefix="KLingAI", custom_output_dir=""): + def IS_CHANGED(cls, image_url, filename_prefix="KLingAI", custom_output_dir="", image_data=""): # 返回当前时间,确保节点总是执行 return time.time() \ No newline at end of file