diff --git a/.github/workflows/publish_action.yml b/.github/workflows/publish_action.yml new file mode 100644 index 0000000..b87534c --- /dev/null +++ b/.github/workflows/publish_action.yml @@ -0,0 +1,20 @@ +name: Publish to Comfy registry +on: + workflow_dispatch: + push: + branches: + - main + paths: + - "pyproject.toml" + +jobs: + publish-node: + name: Publish Custom Node to registry + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + - name: Publish Custom Node + uses: Comfy-Org/publish-node-action@main + with: + personal_access_token: ${{ secrets.c56f9dc6-f267-4cfa-825f-83268a14d5f6 }} ## Add your own personal access token to your Github Repository secrets and reference it here. \ No newline at end of file diff --git a/2024-07-23 15-21-25.png b/2024-07-23 15-21-25.png deleted file mode 100644 index 2b84496..0000000 Binary files a/2024-07-23 15-21-25.png and /dev/null differ diff --git a/2024-07-23 15-25-45.png b/2024-07-23 15-25-45.png deleted file mode 100644 index 576cb5e..0000000 Binary files a/2024-07-23 15-25-45.png and /dev/null differ diff --git a/2024-07-23 15-26-16.png b/2024-07-23 15-26-16.png deleted file mode 100644 index cee2039..0000000 Binary files a/2024-07-23 15-26-16.png and /dev/null differ diff --git a/2024-07-23 21-06-54.png b/2024-07-23 21-06-54.png deleted file mode 100644 index 21ca92a..0000000 Binary files a/2024-07-23 21-06-54.png and /dev/null differ diff --git a/2024-07-23 21-12-49.png b/2024-07-23 21-12-49.png deleted file mode 100644 index ce147c8..0000000 Binary files a/2024-07-23 21-12-49.png and /dev/null differ diff --git a/2024-07-23 21-17-01.png b/2024-07-23 21-17-01.png deleted file mode 100644 index b327752..0000000 Binary files a/2024-07-23 21-17-01.png and /dev/null differ diff --git a/2024-07-23 21-18-18.png b/2024-07-23 21-18-18.png deleted file mode 100644 index ef81b48..0000000 Binary files a/2024-07-23 21-18-18.png and /dev/null differ diff --git a/ComfyUI_temp_thmdz_00057_.png b/ComfyUI_temp_thmdz_00057_.png deleted file mode 100644 index 519f0dd..0000000 Binary files a/ComfyUI_temp_thmdz_00057_.png and /dev/null differ diff --git a/ComfyUI_temp_thmdz_00059_.png b/ComfyUI_temp_thmdz_00059_.png deleted file mode 100644 index 7a43a67..0000000 Binary files a/ComfyUI_temp_thmdz_00059_.png and /dev/null differ diff --git a/ComfyUI_temp_thmdz_00064_.png b/ComfyUI_temp_thmdz_00064_.png deleted file mode 100644 index e5d2620..0000000 Binary files a/ComfyUI_temp_thmdz_00064_.png and /dev/null differ diff --git a/README.md b/README.md index 5db04a4..eb9036a 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,307 @@ -## 较正经的 readme 部分 -`MTCLIPEncode` 能用 `Helsinki-NLP` 的本地模型翻译非英语文本,并把翻译后的英文提示词进行编码。 +# MTCLIPEncode 节点功能描述 -连接位置同默认的 `CLIP Text Encode`。 +## 在 `CLIPTextEncode` 节点中使用母语(`MarianMT` 翻译)输入,并用 `Ollama` 强化提示词 -文字输入框只有一个,这样做的好处是,可以用这里: [Optimized the prompt word module of the plugin](https://github.com/Acly/krita-ai-diffusion/discussions/867) @Darkosha405 提到的方法,串接在 `Krita AI Diffusion` 插件(感谢作者 @Acly )中使用。 +*有诗为证*: -在单一文字输入框内,约定的文本输入格式为: +```打油诗 +MTCLIPEncode +        〔mark〕 +Krita,Diffusion, # -sion +英文咒语最为准。 # 准 +扩散增益 Ollama, # -ma (变韵) +母语生图 convenient。 # -ient +``` + -`prefix | translate_part | suffix` +**▲ MarianMT ── 丰富、稳定且见多识广的多语翻译模型** -可简单解释为: +> MTCLIPEncode:为 ComfyUI 中的 CLIPTextEncode 节点增加了多语翻译(MarianMT)和提示词强化(Ollama)功能,可以把你的母语输入译成英文提示词,还能使用进一步优化提示词质量,且支持 Krita AI Diffusion,轻松生成你想要的图像。 -`[不需翻译的前缀]|[需要翻译的提示词]|[不需要翻译的后缀]` +> MTCLIPEncode: An extension for ComfyUI's CLIPTextEncode node, offering multilingual translation (using MarianMT) and prompt enhancement (using Ollama). Seamlessly translate your native language prompts into English and further optimize them for generating your desired images with Stable Diffusion. Supports Krita AI Diffusion. + +> MTCLIPEncode: เพิ่มฟังก์ชั่นการแปลหลายภาษา (MarianMT) และการปรับปรุงพรอมต์ (Ollama) ให้กับโหนด CLIPTextEncode ใน ComfyUI ช่วยให้คุณแปลข้อความพรอมต์จากภาษาแม่เป็นภาษาอังกฤษ และปรับปรุงคุณภาพของพรอมต์ได้อย่างง่ายดาย รองรับ Krita AI Diffusion สร้างภาพที่คุณต้องการได้อย่างง่ายดาย + +> MTCLIPEncode: Расширение для узла CLIPTextEncode в ComfyUI, добавляющее многоязычный перевод (с помощью MarianMT) и улучшение подсказок (с помощью Ollama). Легко переводите свои подсказки с родного языка на английский и дополнительно оптимизируйте их для генерации желаемых изображений с помощью Stable Diffusion. Поддерживает Krita AI Diffusion. + +> MTCLIPEncodeは、ComfyUIのCLIPTextEncodeノードを拡張し、多言語翻訳(MarianMT)とプロンプト強化(Ollama)機能を追加します。あなたの母国語のプロンプトを英語に翻訳し、Stable Diffusionで理想の画像を生成するためにさらに最適化できます。Krita AI Diffusionにも対応しています。 + + + +**▲ Ollama ── 聪明、灵活且野性难驯的大语言模型** + +> MTCLIPEncode : Une extension pour le nœud CLIPTextEncode de ComfyUI offrant une traduction multilingue (grâce à MarianMT) et une amélioration des invites (grâce à Ollama). Traduisez facilement vos invites dans votre langue maternelle en anglais et optimisez-les pour générer les images que vous souhaitez avec Stable Diffusion. Compatible avec Krita AI Diffusion. + +> MTCLIPEncode: Eine Erweiterung für den CLIPTextEncode-Knoten in ComfyUI, die mehrsprachige Übersetzung (mit MarianMT) und Prompt-Verbesserung (mit Ollama) bietet. Übersetzen Sie Ihre Eingaben mühelos aus Ihrer Muttersprache ins Englische und optimieren Sie sie weiter, um mit Stable Diffusion die gewünschten Bilder zu erzeugen. Unterstützt Krita AI Diffusion. + +> MTCLIPEncode: Una extensión para el nodo CLIPTextEncode de ComfyUI que agrega traducción multilingüe (usando MarianMT) y mejora de prompts (usando Ollama). Traduce sin problemas tus prompts de tu idioma nativo al inglés y optimízalos aún más para generar las imágenes que deseas con Stable Diffusion. Es compatible con Krita AI Diffusion. + +> MTCLIPEncode: ComfyUI의 CLIPTextEncode 노드에 다국어 번역(MarianMT) 및 프롬프트 강화(Ollama) 기능을 추가했습니다. 모국어로 입력한 프롬프트를 영어로 번역하고, Ollama를 사용하여 프롬프트 품질을 더욱 향상시킬 수 있습니다. Krita AI Diffusion을 지원하여 원하는 이미지를 쉽게 생성할 수 있습니다. + +> MTCLIPEncode: यह ComfyUI के CLIPTextEncode नोड के लिए एक विस्तार है जो बहुभाषी अनुवाद (MarianMT का उपयोग करके) और प्रॉम्प्ट संवर्धन (Ollama का उपयोग करके) प्रदान करता है। आप आसानी से अपनी मूल भाषा के प्रॉम्प्ट को अंग्रेजी में अनुवाद कर सकते हैं और Stable Diffusion के साथ अपनी इच्छित छवियों को उत्पन्न करने के लिए उन्हें और बेहतर बना सकते हैं। Krita AI Diffusion के साथ संगत है। + +## 一图胜过千言(后文太长不看) + +*此图若能读懂,后续说明可不必再看。* + + + +**▲ 用图例,表示 `MarianMT` 翻译与 `Ollama` 优化的组合结果** + +## 上图若未能领会,下表将举例说明: + +*此表若能读懂,后续说明可不必再看。* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
用户输入序
号
符号组合Ollama
作用范围
MarianMT
作用范围
备注
处理结果 +
! <lora:tekakutli-kiki-v10:0.7>, krita-kiki, |活泼的女孩,本体为松鼠的拟人化形像,| solo, vibrant colors, perfect hands❺
ⓑ +
有`||`,
有一个`!`,
`!`在`||`外
前缀
+翻译结果
+后缀
`||`之间MarianMT 翻译,
前、后缀合并,
然后 Ollama 将优化整条提示词
(前、后缀的固定格式会被 Ollama 破坏)
Lora Tekakutli-Kiki V10 at strength 0.7 + Krita Kiki aesthetic, focus on a vibrant humanized squirrel girl in solo composition, alive with motion and expressive personality, hands crafted with perfect detail, illuminated by warm, golden light, positioned following the rule of thirds for dynamic balance, set against a lush, verdant forest bursting with life, evoking a sense of playful freedom and harmony with nature
<lora:tekakutli-kiki-v10:0.7>, krita-kiki, |! 活泼的女孩,本体为松鼠的拟人化形像,| solo, vibrant colors, perfect hands❺
ⓐ +
有`||`,
有一个`!`,
`!`在`||`内
仅针对
翻译结果
`||`之间MarianMT 翻译,
Ollama 优化,
然后再合并前、后缀
(前、后缀会保持原样)
<lora:tekakutli-kiki-v10:0.7>, krita-kiki, lively humanized squirrel girl, playful expression, bushy tail twirling like a pennant, vibrant fur patterns, expressive human-like eyes, set in a lush, sun-dappled forest, impressionistic style with visible brushstrokes, shallow depth of field to focus on the girl and her immediate environment, warm golden lighting creating a radiant, inviting atmosphere, solo, vibrant colors, perfect hands
<lora:tekakutli-kiki-v10:0.7>, krita-kiki, |活泼的女孩,本体为松鼠的拟人化形像,| solo, vibrant colors, perfect hands❹ + 仅有`||`,
无`!`
-`||`之间仅翻译,双竖线界定 MarianMT 模型的翻译范围
<lora:tekakutli-kiki-v10:0.7>, krita-kiki, The lively girl, the body of a humanized squirrel, solo, vibrant colors, perfect hands
<lora:tekakutli-kiki-v10:0.7>, krita-kiki, lively squirrel girl, solo, masterpiece, high detail, vibrant colors, perfect hands❶ + 无`||`,
无`!`
--输入文本将直接进入 CLIP 编码流程,
与默认的 `CLIPTextEncode` 节点无异
<lora:tekakutli-kiki-v10:0.7>, krita-kiki, lively squirrel girl, solo, masterpiece, high detail, vibrant colors, perfect hands
<lora:tekakutli-kiki-v10:0.7>, krita-kiki, lively squirrel girl, solo, !masterpiece, high detail, vibrant colors, perfect hands❷ + 无`||`,
有一个`!`
整个文本-无论`!`的位置在哪,都会调用 Ollama,进而“优化”掉所有文本
vibrant squirrel girl in lively Krita-Kiki style, enhanced with Tekakutli-Kiki LoRA (0.7), solo masterpiece - high detail, dynamic composition following the golden ratio, rich vibrant colors evoking a lush autumnal atmosphere, perfect hands sculpted by the soft morning sunlight filtering through the canopy above, wide-angle shot emphasizing depth and immersive storytelling
<lora:tekakutli-kiki-v10:0.7>, krita-kiki,! lively squirrel girl, solo, masterpiece, high detail,! vibrant colors, perfect hands❸ + 无`||`,
有`!!`
`!!`之间-唯一使用两个感叹号的格式,目的是界定 Ollama 的优化范围
<lora:tekakutli-kiki-v10:0.7>, krita-kiki, lively squirrel girl, playful and energetic, leaping in mid-air, caught in a sunbeam, masterpiece-quality detail, impressionistic style with visible brush strokes, shallow depth of field to emphasize movement, set against a lush, verdant forest backdrop teeming with life, evoking a sense of joyous freedom and harmony with nature, vibrant colors, perfect hands
+ +**▲ 用表格与实例,解释 `MarianMT` 翻译与 `Ollama` 优化的组合效果** + + + +**▲ krita-kiki 包含多种组合方式的简单工作流** + +## 如果前面的图例和表格都没看懂,我会试着用人类的语言总结所有组合情况,提供另一种被理解的可能性: + +1. 如果没有 `||`,也没有 `!`,就无需翻译,也无需 Ollama 优化,直接进入 CLIP 编码流程,跟默认的 `CLIPTextEncode` 无异; +2. 如果没有 `||`,说明无需翻译,但只有一个 `!`,且无论 `!` 在任何位置,Ollama 都会处理整个文本; +3. 如果没有 `||`,但有两个 `!`,表示 Ollama 将仅处理两个叹号 `!!` 中间的部分,前、后缀将保持不变,这是唯一会用到两个叹号的格式; +4. 如果仅有 `||`,没有 `!`,就只有翻译逻辑,且只翻译两个竖线 `||` 中间的部分; +5. 如果有 `||`,且有一个 `!`,则分两种情况: + + a. 一个 `!`在 `||` 范围内的任何位置,Ollama 在“合并前、后缀”之前起作用,仅仅处理翻译后的文本(之后再与前、后缀合并); + + b.一个 `!`在 `||` 范围外的任何位置(无论在前缀中还是后缀中)Ollama 在“合并前、后缀”之后起作用,Ollama 会处理 `前缀+翻译好的文本+后缀` 。 + +# Q&A 与思路整理 + +## ◉ 为什么需要调用两个模型── `MarianMT` 翻译模型和 `Ollama` 语言模型? + +`MarianMT` 模型很适合翻译工作,能保证稳定输出英文结果,极少出现“幻觉”( *这对聪明的 `Ollama` 来说有些难度,准确地说,是 Ollama 中的某些模型在输出中文时以为自己在说英文,反之亦然* )。 + +`Ollama` 负责优化提示词。见多识广、词汇丰富,能让文字描述更加生动、详尽,这是大语言模型的强项。 + +本节点的连接位置,与默认的 `CLIP Text Encode` 相同。 + +## ◉ 为什么使用“单一文字框”? + + +**▲ 单一文字框** + +在 `ComfyUI` 中,我们会看到,`MTCLIPEncode` 节点仅有一个文字输入框。 + +这样做的好处是,可以用这里: [Optimized the prompt word module of the plugin](https://github.com/Acly/krita-ai-diffusion/discussions/867) @Darkosha405 提到的方法,串接在 `Krita AI Diffusion` 插件(感谢作者 @Acly )中使用 ── **这也正是本节点的设计初衷**。 + +## ◉ 为什么需要约定文本格式 + +为了界定大模型的作用范围。 + +通常,当我们在使用 Stable Diffusion 时,为了让生成的图片更加符合我们的预期,有些提示词是需要固定不变的(如:lora、embedding、风格描述、取景方式、人物姿态、多人物组合方位……),有时哪怕一丁点儿修改,都会得到不可控的结果。 + +所以,我们需要在文本输入时做一些特别的约定,你可以理解为,这是某种特定的“语法”,正如前述说明中体现的那样 ── 这非常简单(哈哈哈,是吧)。 + +## ◉ 理解分界与作用范围: + +### ● `||` 的分界示例。 --- -![节点和输入文本的格式](https://github.com/Marksusu/ComfyUI_MTCLIPEncode/blob/main/2024-07-23%2015-21-25.png) +**当仅使用 `MarianMT` 翻译模型时,约定的文本格式为:** -▲ 节点最初的样子及输入文本的“约定格式” +`prefix | translate_part | suffix` -![正向提示词示例](https://github.com/Marksusu/ComfyUI_MTCLIPEncode/blob/main/2024-07-23%2015-25-45.png) +**▲ 对应表格中的 ❹** -▲ ComfyUI 中,正向提示词示例 +把需要翻译的部分夹在两道竖线 `||`中间,这样,提示词就分成了三个部分。 -![反向提示词示例](https://github.com/Marksusu/ComfyUI_MTCLIPEncode/blob/main/2024-07-23%2015-26-16.png) +可简单理解为: -▲ 在 ComfyUI 中,反向提示词示例 +`prefix` ──“无需翻译的前缀”; -![在命令行中看到的翻译情况](https://github.com/Marksusu/ComfyUI_MTCLIPEncode/blob/main/2024-07-23%2021-06-54.png) +`translate_part` ──“需要翻译的提示词”; -▲ 在 ComfyUI 中使用本节点时,终端返回的翻译前、翻译后的提示词(彩色),及最终进入 CLIP 编码的提示词(灰色) +`suffix` ──“无需翻译的后缀”。 -![在 Krita 中使用的样子](https://github.com/Marksusu/ComfyUI_MTCLIPEncode/blob/main/2024-07-23%2021-12-49.png) +如果提示词仅有 `| translate_part |`,没有前、后缀,也是可以的,但必须按照约定 ── 把要翻译的文字用两道竖线 `||`夹起来。 -▲ 提示词在 Krita 输入框时的样子及画面效果 +> 这样,就不会破坏 `Krita AI Diffusion` 插件中“风格设置”里预设的前、后缀提示词。 -![在 Krita 中使用时,终端返回的翻译情况,可以看到设置中附加的风格提示词](https://github.com/Marksusu/ComfyUI_MTCLIPEncode/blob/main/2024-07-23%2021-17-01.png) +> 毕竟,在最初的最初,这个 `MTCLIPEncode` 节点就是为了在 `Krita AI Diffusion` 插件中使用而编写的(再次感谢作者 @Acly !)。 -▲ 在 `Krita AI Diffusion` 中使用本节点时,终端返回的情况 +--- -![Krita 设置中默认附加的风格提示词](https://github.com/Marksusu/ComfyUI_MTCLIPEncode/blob/main/2024-07-23%2021-18-18.png) +### ● `!!` 的分界示例。 -▲ 在 Krita 设置中的默认风格提示词,插件设置选项 “Styles” 页面下 “Style Prompt” 和 “Negative Prompt” 所包含的内容。 +**当仅使用 `Ollama` 语言模型时,约定的文本格式为:** -对比终端返回的情况,我们能看到,插件默认的风格提示词,与我写的提示词合并后,被拆分、翻译、组合、编码的大概过程。 +`prefix ! Ollama_process ! suffix` -## 在 `nodes.py` 文件中添加更多语种列表: +**▲ 对应表格中的 ❸** + +把需要 Ollama 处理的部分夹在两个叹号 `!!`中间,这样,提示词就分成了三个部分。 + +可简单理解为: + +`prefix` ──“无需 Ollama 处理的前缀”; + +`Ollama_process` ──“需要 Ollama 处理的提示词”; + +`suffix` ──“无需 Ollama 处理的后缀”。 + +如果提示词仅有 ` ! Ollama_process !`,没有前、后缀,也是可以的,但必须按照约定 ── 把需要 Ollama 处理的文字用两个叹号 `!!`夹起来。 + +--- + +### ● `!` 的作用范围比 `!!` 还大(或可表述为──分界消失了) + +`prefix ! Ollama_process suffix` + +**▲ 对应表格中的 ❷** + +--- + +### ● `||` 来分界,`!` 来锁定作用范围: + +**`MarianMT` 翻译模型和 `Ollama` 强化模型合用,且 `Ollama` 仅作用于翻译部分。** + +**此时,约定的文本格式为:** + +`prefix |! translate_part | suffix` + +**▲ 对应表格中的 ❺ⓐ** + +`prefix !| translate_part | suffix` 等同于 `prefix | translate_part |! suffix` + +**▲ 对应表格中的 ❺ⓑ** + +--- + +## ◉ 终端里能看到的提示词处理过程 + + + + + +**▲ 前面 “MarianMT 翻译模型” 那张图的提示词处理过程** + + + + + +**▲ 前面 “Ollama 大语言模型” 那张图的提示词处理过程** + + + + + +**▲ 前面 “图例与模型组合” 那张图的提示词处理过程** + + + +**▲ Ollama 模型有时会表现出不稳定的状态,时有返回空的结果的现象** + +--- + +## ◉ 在 `nodes.py` 文件中添加更多语种列表: ``` marian_list = [ "opus-mt-zh-en", "opus-mt-ru-en", "opus-mt-th-en", + ...... ] # https://huggingface.co/Helsinki-NLP -# 在这个地址里可以找到更多语种的模型, +# 在如上地址,可以找到更多语种的模型, # 把状如 "opus-mt-th-en" 的模型名字添加到如上列表后, # 下次使用时会自动下载模型,下载到哪里我也不知道, # 实在想知道就去问 ChatGPT,代码几乎都是它写的。 ``` -## 若想在 `Krita AI Diffusion` 中使用本节点 +## ◉ 若想在 `Krita AI Diffusion` 中使用本节点 -就要修改 `pykrita/ai_diffusion` 目录下的 `comfy_workflow.py` 文件(同时,安装本节点也是必须的,要不然呢……): +**就要修改 `pykrita/ai_diffusion` 目录下的 `comfy_workflow.py` 文件(同时,安装本节点也是必须的,要不然呢……;),** 在 `comfy_workflow.py` 文件里,找到如下这行代码: @@ -72,72 +311,55 @@ marian_list = [ ``` # return self.add("CLIPTextEncode", 1, clip=clip, text=text) - return self.add("MTCLIPEncode", 1, clip=clip, text=text, checkpoint="opus-mt-zh-en") # 如果你用的是泰语,应该就是 "opus-mt-th-en",这个翻译模型的名字,一定、务必……要以 huggingface 上查询的结果为准。 + return self.add("MTCLIPEncode", 1, clip=clip, text=text, checkpoint="opus-mt-zh-en", ollama_url="http://127.0.0.1:11434", ollama_model="mistral-nemo:latest") +# "opus-mt-zh-en",是中译英的模型,如果你用的是泰语,应该就是 "opus-mt-th-en", +# 这个翻译模型的名字,一定、务必……要以 [huggingface](https://huggingface.co/Helsinki-NLP) 上查询的结果为准; +# "http://127.0.0.1:11434",是 Ollama 的默认端口; +# "mistral-nemo:latest",是 Ollama 模型的名字, +# 注意:有些模型会返回空信息,这里需要逐个测试才能得到满意的结果。 +``` +### 如上方法最初的来源,请详见这里: [Optimized the prompt word module of the plugin](https://github.com/Acly/krita-ai-diffusion/discussions/867) + +## ◉ 在 nodes.py 文件中修改驱使 Ollama 完成工作的提示词: + +``` + prompt = f""" +You are a talented art director with a deep understanding of visual arts. You excel at transforming simple text descriptions into vivid, imaginative images. Now, you will be playing the role of a prompt engineer, providing precise and creative prompts for the Stable Diffusion model. + +Please convert the following user input text: {text}, into a detailed and vivid prompt to inspire Stable Diffusion to generate a captivating image. + +**Focus on:** +* **Clarity and precision:** Use clear and concise language that Stable Diffusion can easily understand. +* **Visual storytelling:** Create compelling narratives through your descriptions. +* **Artistic techniques:** Employ a variety of artistic techniques like lighting, composition, and perspective. +* **Style and atmosphere:** Convey the desired mood and style using vivid language and metaphors. + +**Consider incorporating:** +* **Specific art styles:** e.g., impressionism, surrealism, cyberpunk +* **Cinematographic techniques:** e.g., shallow depth of field, wide-angle shot +* **Descriptive adjectives:** e.g., ethereal, luminous, brooding +* **Compositional elements:** e.g., rule of thirds, golden ratio + +**Example:** +* **User input:** A lonely robot sitting on a distant planet +* **Your prompt:** # solitary android, desolate alien planet, distant star, panoramic view, sci-fi aesthetic, melancholic atmosphere, cinematic lighting # + +**Remember:** Your goal is to create a prompt that inspires Stable Diffusion to generate a visually stunning and emotionally resonant image. + +**Please enclose your prompt within two `#` symbols.** +""" ``` -### 详见这里: [Optimized the prompt word module of the plugin](https://github.com/Acly/krita-ai-diffusion/discussions/867) --- -## 建议不要 read 的部分 +> 诚恳地说:
这里大部分代码都是 ChatGPT 写的,
Claude 和 Gemini 也都参与了修改。
其中:
Claude 精简掉了 “Ollama 模型列表的获取”步骤,Gemini 也认为每次获取这个列表是没有必要的,ChatGPT 也认同这一修改建议,表示直接“复制──粘贴”比较好。
Gemini 完成了如上“驱使 Ollama 稳定工作的提示词”的优化,让 Ollama 在大部分时间里都能较稳定地输出符合预期的结果。
几位 AI 老师为此节点的实现做出了极大贡献,特此感谢~
而我(mark──渺小到首字母都不好意思大写),只是个会写打油诗的小跟班儿(写 README.md 时学到了一点 Html 的表格技巧,这是我作为团队中唯一人类的一点小小收获)。 -我几乎不会用 python,其它编程更是一窍不通,是 ChatGPT 帮我写的全部代码,有问题你们继续问它就行了。 +> 当我跟 Gemini 老师显摆我写的打油诗时,人家一口气给我甩回来好几篇。
比方如下这首,敲敲桌板就能唱起来: -写该节点的过程是这样的: - -一、[@Drakosha405](https://github.com/Drakosha405) 在 [Optimized the prompt word module of the plugin](https://github.com/Acly/krita-ai-diffusion/discussions/867) 中,提供了在 `Krita AI Diffusion` 插件里替换 `CLIP Text Encode` 节点的方法,以便在该插件中使用自己的母语(非英语)来书写提示词,这正是我想要的(谢谢你, [@Drakosha405](https://github.com/Drakosha405) ),但这似乎还不够。 - -二、学会这个方法后,我试用了几个能在 `ComfyUI Manager` 中搜到的 Encode 节点和翻译节点。 - -其中, [comfyui-ollama-prompt-encode](https://github.com/ScreamingHawk/comfyui-ollama-prompt-encode) 节点(感谢作者 @ScreamingHawk )可以用中文(只要是 Ollama 能懂的语言都行)驱使 Ollama 本地模型写出英文提示词并用于生图。 - -但 Ollama 所用的模型时常会犯糊涂。 - -你让它翻译,它会用跟你相同的语言聊天,以此显得自己“很通用”。你让它处理反向提示词,它会说那些词汇太负面,它的道德和信仰让它“不能做那样的事”。 - -希望大语言模型早日进化到能感觉到疼痛,好等哪天我学会写代码,一定会毫不犹豫为它写一行“不听话就抽嘴巴”的惩罚措施。 - -![](https://github.com/Marksusu/ComfyUI_MTCLIPEncode/blob/main/ComfyUI_temp_thmdz_00057_.png) - -正是这股豪横气概,让我突然就看懂了“把 Ollama 返回的信息进行 Encode 处理”的那几行代码。 - -这样一来,只要诱使 ChatGPT 去抄一个翻译节点处理文本的部分,再组合进一步做 Encode 处理的部分,一个新的用于替换 `Krita AI Diffusion` 插件里的“类 `CLIP Text Encode` 节点”就有眉目了。 - -三、这就是翻译部分的代码来源: [ComfyUI_kkTranslator_nodes](https://github.com/kingzcheung/ComfyUI_kkTranslator_nodes) -该节点能利用 `Helsinki-NLP` 的本地模型,实现提示词翻译(感谢作者 @kingzcheung ~)。 - -就这样,ChatGPT 负责写(抄袭和组合)代码,我负责测试和提出调整思路,不到半天时间,翻译和编码功能就基本实现了。 - -虽然在 ComfyUI 中测试效果良好,但在 Krita 中试用时还是遇到了一个新问题,于是就进入下面的“正则处理阶段”。 - -四、在搜索与 Ollama 相关的节点时,理解了 [ComfyUi-Ollama-YN](https://github.com/wujm424606/ComfyUi-Ollama-YN) 用正则式处理文本的思路(感谢作者 @wujm424606 ),正好解决下面 Krita 中遇到的问题。 - -![](https://github.com/Marksusu/ComfyUI_MTCLIPEncode/blob/main/ComfyUI_temp_thmdz_00059_.png) - -毕竟,这个 `MTCLIPEncode` 节点就是为了在 `Krita AI Diffusion` 插件中使用而编写的(再次感谢作者 @Acly )。 - -在 `Krita AI Diffusion` 插件的设置选项里,`Styles` 页面下,`Style Prompt` 和 `Negative Prompt` 这两项文本框中的内容,会被合并到你在操作面板文本框里写下的提示词前后,成为默认会由 `CLIP Text Encode` 节点处理的 `text` 字符串。 - -也就是说,除了 `{prompt}` 这个字符串(表示的就是你写在插件文本框中的提示词)之外,这里还有容易被忽视的默认选项附加的一些风格提示词被汇入翻译流程。 - -这会对翻译资源造成浪费。 - -并且,这些前后缀内容,若是都被扔进翻译模型中,也会很容易引起翻译结果的混乱。 - -所以,`MTCLIPEncode` 节点的文本框中必须包含两个 `|` 才能把“无需翻译的前后缀部分”和“需翻译的中间部分”隔离开。 - -这就形成了我们“与节点约定的”文本输入格式, - -可简单表示为: - -`prefix | translate_part | suffix` - -简单解释为: - -`[不需翻译的前缀]|[需要翻译的提示词]|[不需要翻译的后缀]` - -![](https://github.com/Marksusu/ComfyUI_MTCLIPEncode/blob/main/ComfyUI_temp_thmdz_00064_.png) - -啰啰嗦嗦解释为: - -`[“Krita AI Diffusion” 插件设置选项 “Styles”页面下 “Style Prompt” 或 “Negative Prompt” 所包含的前缀文本(位于 “{prompt}”前面的部分)][(你写的或者你抄来的原本就是英语的)不希望被翻译染指的前缀文本(如: score_8_up 这种我一点都看不懂的东西)]|[你写的希望被翻译的非英语的部分]|[(你写的或者你抄来的原本就是英语的)不希望被翻译染指的后缀文本][“Krita AI Diffusion” 插件设置选项 “Styles”页面下 “Style Prompt” 或 “Negative Prompt” 所包含的后缀文本(位于 “{prompt}”后面的部分)]` +```打油诗 +扩散模型绘图,Stable Diffusion领先 +英文提示词精准,Ollama优化深研 +母语输入多便捷,创作乐趣大无边 +``` +> 真真是臭味相投啊! \ No newline at end of file diff --git a/images/2024-08-24-22-45-29.png b/images/2024-08-24-22-45-29.png new file mode 100644 index 0000000..0368b4a Binary files /dev/null and b/images/2024-08-24-22-45-29.png differ diff --git a/images/2024-08-24-22-46-37.png b/images/2024-08-24-22-46-37.png new file mode 100644 index 0000000..af75858 Binary files /dev/null and b/images/2024-08-24-22-46-37.png differ diff --git a/images/2024-08-24-22-48-46.png b/images/2024-08-24-22-48-46.png new file mode 100644 index 0000000..8851dfc Binary files /dev/null and b/images/2024-08-24-22-48-46.png differ diff --git a/images/2024-08-24-22-49-15.png b/images/2024-08-24-22-49-15.png new file mode 100644 index 0000000..e3bab48 Binary files /dev/null and b/images/2024-08-24-22-49-15.png differ diff --git a/images/Kiki.png b/images/Kiki.png new file mode 100644 index 0000000..701cc1e Binary files /dev/null and b/images/Kiki.png differ diff --git a/images/MTCLIPEcode01.png b/images/MTCLIPEcode01.png new file mode 100644 index 0000000..6faa3a7 Binary files /dev/null and b/images/MTCLIPEcode01.png differ diff --git a/images/MTCLIPEcode02.png b/images/MTCLIPEcode02.png new file mode 100644 index 0000000..ba9a94d Binary files /dev/null and b/images/MTCLIPEcode02.png differ diff --git a/images/MTCLIPEcode03.png b/images/MTCLIPEcode03.png new file mode 100644 index 0000000..f9d9bdc Binary files /dev/null and b/images/MTCLIPEcode03.png differ diff --git a/images/chart.png b/images/chart.png new file mode 100644 index 0000000..e76fa30 Binary files /dev/null and b/images/chart.png differ diff --git a/images/marianmt.png b/images/marianmt.png new file mode 100644 index 0000000..6692a87 Binary files /dev/null and b/images/marianmt.png differ diff --git a/images/ollamas.png b/images/ollamas.png new file mode 100644 index 0000000..fea6e60 Binary files /dev/null and b/images/ollamas.png differ diff --git a/images/thenode.png b/images/thenode.png new file mode 100644 index 0000000..edcad26 Binary files /dev/null and b/images/thenode.png differ diff --git a/nodes.py b/nodes.py index d71b063..8c42150 100644 --- a/nodes.py +++ b/nodes.py @@ -1,48 +1,104 @@ from transformers import MarianMTModel, MarianTokenizer import re +import json +import requests +from ollama import Client, Options +# 去这个地址: https://huggingface.co/Helsinki-NLP 可以找到更多语种的模型,用同样的格式添加到如下列表,以便下次选用,翻译模型会被自动下载至本地 marian_list = [ "opus-mt-zh-en", "opus-mt-ru-en", "opus-mt-th-en", + "opus-mt-es-en", + "opus-mt-fr-en", + "opus-mt-ar-en", + "opus-mt-hi-en", + "opus-mt-bn-en", + "opus-mt-pt-en", + "opus-mt-ja-en", + "opus-mt-de-en", + "opus-mt-ko-en", + "opus-mt-vi-en", + "opus-mt-tr-en", + "opus-mt-nl-en", ] -# https://huggingface.co/Helsinki-NLP 在这个地址里可以找到更多语种的模型,添加到如上列表后,下次使用时会自动下载模型 class MTCLIPEncode: + def __init__(self): + self.default_ollama_url = "http://127.0.0.1:11434" + @classmethod def INPUT_TYPES(cls): return { "required": { "clip": ("CLIP", {}), - "checkpoint": (marian_list, {"multiline": False,"default": "opus-mt-zh-en"}), - "text": ("STRING", {"multiline": True,"default": "prefix | translate_part | suffix"}), + "checkpoint": (marian_list, {"multiline": False, "default": "opus-mt-zh-en"}), + "text": ("STRING", {"multiline": True, "default": " prefix | translate_part | suffix"}), + "ollama_url": ("STRING", {"default": "http://127.0.0.1:11434"}), + "ollama_model": ("STRING", {"default": "mistral-nemo:latest"}), } } + # 确保双竖线 `||` 中间的内容为汉字或不少于三个字母的非英文单词 def is_valid_translate_part(self, text): - # 简单检查文本是否包含至少一个汉字或一个长度大于2的非英文单词 return bool(re.search(r'[\u4e00-\u9fff]|\b\w{3,}\b', text)) - def mtencode(self, clip, checkpoint, text): + def optimize_with_ollama(self, text, url, model): + client = Client(host=url) - # 使用正则表达式修剪首尾可能存在的一个或多个 `,`、[空格] - text = re.sub(r'^[\s,]+|[\s,]+$', '', text) + prompt = f""" +You are a talented art director with a deep understanding of visual arts. You excel at transforming simple text descriptions into vivid, imaginative images. Now, you will be playing the role of a prompt engineer, providing precise and creative prompts for the Stable Diffusion model. - # 使用正则表达式分离出需要翻译和不需要翻译的部分 - pattern = r"([^|]*)\|([^|]*)\|([^|]*)" - match = re.match(pattern, text) +Please convert the following user input text: {text}, into a detailed and vivid prompt to inspire Stable Diffusion to generate a captivating image. +**Focus on:** +* **Clarity and precision:** Use clear and concise language that Stable Diffusion can easily understand. +* **Visual storytelling:** Create compelling narratives through your descriptions. +* **Artistic techniques:** Employ a variety of artistic techniques like lighting, composition, and perspective. +* **Style and atmosphere:** Convey the desired mood and style using vivid language and metaphors. + +**Consider incorporating:** +* **Specific art styles:** e.g., impressionism, surrealism, cyberpunk +* **Cinematographic techniques:** e.g., shallow depth of field, wide-angle shot +* **Descriptive adjectives:** e.g., ethereal, luminous, brooding +* **Compositional elements:** e.g., rule of thirds, golden ratio + +**Example:** +* **User input:** A lonely robot sitting on a distant planet +* **Your prompt:** # solitary android, desolate alien planet, distant star, panoramic view, sci-fi aesthetic, melancholic atmosphere, cinematic lighting # + +**Remember:** Your goal is to create a prompt that inspires Stable Diffusion to generate a visually stunning and emotionally resonant image. + +**Please enclose your prompt within two `#` symbols.** +""" + + response = client.generate(model=model, prompt=prompt) + return response['response'] + + def mtencode(self, clip, checkpoint, text, ollama_url, ollama_model): + + print(f"      🫐 🫐 🫐 🫐 🫐 🫐") # 打印分隔线 + + pattern = r"([^|]*)\|([^|]*)\|([^|]*)" # 需要调用翻译模型的提示词基本格式 + match = re.match(pattern, text) # 判断是否需要调用翻译模型 + + # 若输入 text 中,没有`||`(没有翻译请求),判断叹号的数量和位置 + ollama_for_original = text.count('!') == 1 and '|' not in text # 对应的输入格式为:"English words English words ! English words",叹号 `!` 为一个,可以在任意位置 + ollama_for_part_of_original = text.count('!') == 2 and '|' not in text # 对应格式为:"English prefix ! English words for Ollama refine ! English suffix" + + # 有`||`,说明有翻译请求,进行翻译处理 if match: - prefix = match.group(1).strip() - translate_part = match.group(2).strip() - suffix = match.group(3).strip() + prefix, translate_part, suffix = [re.sub(r'^[\s|,]+|[\s|,]+$', '', part.strip()) for part in match.groups()] # 被 `||` 双竖线分隔的三个部分 + print(f"    \033[94m{prefix}\033[0m, \033[92m{translate_part}\033[0m, \033[94m{suffix}\033[0m") # 打印用户的原始输入,并用颜色区分三个部分 - # 修剪三个部分的文本,首尾可能存在的一个或多个`|`、`,`、[空格] - prefix = re.sub(r'^[\s|,]+|[\s|,]+$', '', prefix) - translate_part = re.sub(r'^[\s|,]+|[\s|,]+$', '', translate_part) - suffix = re.sub(r'^[\s|,]+|[\s|,]+$', '', suffix) + # ollama_in_translate_part 和 ollama_for_all_3_parts 的判断在解析出 prefix、translate_part 和 suffix 之后 + ollama_in_translate_part = '!' in translate_part # 判断叹号是否在翻译部分 + ollama_for_all_3_parts = ( + not ollama_in_translate_part and ( + '!' in prefix or '!' in suffix # 判断叹号是否在前缀或后缀中 + ) + ) - # 确保 translate_part 有效 if not self.is_valid_translate_part(translate_part): prompt_text = f"{prefix}, {suffix}" else: @@ -50,63 +106,94 @@ class MTCLIPEncode: tokenizer = MarianTokenizer.from_pretrained(model_name) model = MarianMTModel.from_pretrained(model_name) - translated = model.generate(**tokenizer(translate_part, return_tensors="pt", padding=True)) - translated_text = tokenizer.decode(translated[0], skip_special_tokens=True) + if ollama_in_translate_part: - # 修剪 translated_text 首尾可能存在的空格、逗号和句号 - translated_text = re.sub(r'^[\s,.]+|[\s,.]+$', '', translated_text) + translate_part = translate_part.replace('!', '') # 去掉 `!` + translated = model.generate(**tokenizer(translate_part, return_tensors="pt", padding=True)) + translated_text = re.sub(r'^[\s,.]+|[\s,.]+$', '', tokenizer.decode(translated[0], skip_special_tokens=True)) + print(f"    \033[94m{prefix}\033[0m, \033[92m{translated_text}\033[0m, \033[94m{suffix}\033[0m") # 打印译文,并与原始的前、后缀合并 + + try: + translated_text = self.optimize_with_ollama(translated_text, ollama_url, ollama_model) + match = re.search(r'#(.*?)#', translated_text) + if match: + translated_text = match.group(1) + translated_text = re.sub(r'^[\s|,]+|[\s|,]+$', '', translated_text.strip()) + + prompt_text = ", ".join(filter(None, [prefix, translated_text, suffix])) + print(f"    \033[94m{prefix}\033[0m, \033[90;1m{translated_text}\033[0m, \033[94m{suffix}\033[0m") # 打印 Ollama 对译文的处理结果,并与原始的前、后缀合并 + except Exception as e: + print(f"Failed to optimize prompt with Ollama in translated text: {e}") - # 合并不需要翻译的部分和翻译后的部分 - if prefix and suffix: - prompt_text = f"{prefix}, {translated_text}, {suffix}" - elif prefix: - prompt_text = f"{prefix}, {translated_text}" - elif suffix: - prompt_text = f"{translated_text}, {suffix}" else: - prompt_text = translated_text + translated = model.generate(**tokenizer(translate_part, return_tensors="pt", padding=True)) + translated_text = re.sub(r'^[\s,.]+|[\s,.]+$', '', tokenizer.decode(translated[0], skip_special_tokens=True)) - # ANSI 转义序列用于颜色输出 - color_prefix = f"\033[94m{prefix}\033[0m" if prefix else "" - color_translate_part = f"\033[92m{translate_part}\033[0m" - color_translated_text = f"\033[92m{translated_text}\033[0m" - color_suffix = f"\033[94m{suffix}\033[0m" if suffix else "" + prompt_text = ", ".join(filter(None, [prefix, translated_text, suffix])) + print(f"    \033[94m{prefix}\033[0m, \033[92m{translated_text}\033[0m, \033[94m{suffix}\033[0m") # 打印译文,保留原始前、后缀不变,并完成合并 - # 打印格式化输出 - print(f"      🫐 🫐 🫐 🫐 🫐 🫐") - if prefix and suffix: - print(f"    {color_prefix}, {color_translate_part}, {color_suffix}") - elif prefix: - print(f"    {color_prefix}, {color_translate_part}") - elif suffix: - print(f"    {color_translate_part}, {color_suffix}") - else: - print(f"    {color_translate_part}") + if ollama_for_all_3_parts: + prompt_text = prompt_text.replace('!', '') # 去掉 `!` + try: + prompt_text = self.optimize_with_ollama(prompt_text, ollama_url, ollama_model) + match = re.search(r'#(.*?)#', prompt_text) + if match: + prompt_text = match.group(1) + prompt_text = re.sub(r'^[\s|,]+|[\s|,]+$', '', prompt_text.strip()) - if prefix and suffix: - print(f"    {color_prefix}, {color_translated_text}, {color_suffix}") - elif prefix: - print(f"    {color_prefix}, {color_translated_text}") - elif suffix: - print(f"    {color_translated_text}, {color_suffix}") - else: - print(f"    {color_translated_text}") + print(f"    \033[90;1m{prompt_text}\033[0m") # 打印 Ollama 对合并后的提示词(包括前、后缀和译文)整体的处理结果 + except Exception as e: + print(f"Failed to optimize prompt with Ollama in all 3 parts: {e}") + # 去掉前缀 prefix 为空时,开头会出现的`,` + prompt_text = prompt_text.lstrip(',') else: - prompt_text = text.strip() # 如果没有匹配项,使用原文本 + prompt_text = text.strip() + print(f"    \033[94m{prompt_text}\033[0m") # 打印无需翻译的原始提示词 - # 打印格式化输出 - print(f"    {prompt_text}") + if ollama_for_part_of_original: + try: + # 使用正则提取 `!!` 之间的部分,并移除 `!!` + match = re.search(r'^(.*?)!(.*?)!(.*?)$', text) + if match: + prefix, ollama_part, suffix = [re.sub(r'^[\s|,]+|[\s|,]+$', '', part.strip()) for part in match.groups()] +# prefix, ollama_part, suffix = match.groups() + ollama_part = self.optimize_with_ollama(ollama_part.strip(), ollama_url, ollama_model) + ollama_match = re.search(r'#(.*?)#', ollama_part) + if ollama_match: + ollama_part = ollama_match.group(1) + ollama_part = re.sub(r'^[\s|,]+|[\s|,]+$', '', ollama_part.strip()) + + prompt_text = f"{prefix.strip()}, {ollama_part.strip()}, {suffix.strip()}" + print(f"    \033[32m{prefix}\033[0m, \033[90;1m{ollama_part}\033[0m, \033[32m{suffix}\033[0m") # 打印 Ollama 对无需翻译的原始提示词的处理结果,并保留原始前、后缀 + except Exception as e: + print(f"Failed to optimize prompt with Ollama in part of original prompt: {e}") + + elif ollama_for_original: + prompt_text = text.replace('!', '') # 去掉 `!` + try: + prompt_text = self.optimize_with_ollama(prompt_text, ollama_url, ollama_model) + match = re.search(r'#(.*?)#', prompt_text) + if match: + prompt_text = match.group(1) + prompt_text = re.sub(r'^[\s|,]+|[\s|,]+$', '', prompt_text.strip()) + + print(f"    \033[90;1m{prompt_text}\033[0m") # 打印无需翻译但需要 Ollama 处理的提示词 + except Exception as e: + print(f"Failed to optimize prompt with Ollama in original prompt: {e}") + + # 去掉前缀 prefix 为空时,开头会出现的`,` + prompt_text = prompt_text.lstrip(',') + + # 最终输出 + print(f"    \033[32m{prompt_text}\033[0m") # 打印进入 CLIP 编码流程的提示词 tokens = clip.tokenize(prompt_text) cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True) return ([[cond, {"pooled_output": pooled}]], prompt_text) - RETURN_TYPES = ( - "CONDITIONING", - "STRING", - ) + RETURN_TYPES = ("CONDITIONING", "STRING",) FUNCTION = "mtencode" CATEGORY = "MTCLIPEncode" @@ -116,4 +203,4 @@ NODE_CLASS_MAPPINGS = { NODE_DISPLAY_NAME_MAPPINGS = { "MTCLIPEncode": "MTCLIPEncode", -} +} \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..285e4b7 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "comfyui_mtclipencode" +description = "MTCLIPEncode: An extension for ComfyUI's CLIPTextEncode node, offering multilingual translation (using MarianMT) and prompt enhancement (using Ollama). Seamlessly translate your native language prompts into English and further optimize them for generating your desired images with Stable Diffusion. Supports Krita AI Diffusion." # 节点描述 +version = "0.2.0" +license = {file = "LICENSE"} +dependencies = ["transformers", "requests", "ollama"] + +[project.urls] +Repository = "https://github.com/Marksusu/ComfyUI_MTCLIPEncode" +# Used by Comfy Registry https://comfyregistry.org + +[tool.comfy] +PublisherId = "marksusu" +DisplayName = "ComfyUI_MTCLIPEncode" +Icon = "" diff --git a/requirements.txt b/requirements.txt index 747b7aa..504e397 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,3 @@ -transformers \ No newline at end of file +transformers +requests +ollama \ No newline at end of file