Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bc4c9a3c8 | ||
|
|
7e092c7777 | ||
|
|
140b32bcfb | ||
|
|
d81a3a2701 | ||
|
|
9305ca6a7a | ||
|
|
9a8f689fae | ||
|
|
b1cb565255 | ||
|
|
a4ed0d9f09 | ||
|
|
3ff6fbe5e7 | ||
|
|
8b66a51bc5 | ||
|
|
7ece136a60 |
@@ -0,0 +1,33 @@
|
||||
name: Close inactive issues
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "17 3 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
close-inactive-issues:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
repo-token: ${{ github.token }}
|
||||
days-before-issue-stale: 23
|
||||
days-before-issue-close: 7
|
||||
stale-issue-label: stale
|
||||
stale-issue-message: >
|
||||
This issue has had no activity for 23 days. It will be
|
||||
automatically closed in 7 days unless there is new activity.
|
||||
close-issue-message: >
|
||||
This issue was automatically closed after 30 days without
|
||||
activity. If the problem still exists, please reopen it or
|
||||
create a new issue with updated information.
|
||||
close-issue-reason: not_planned
|
||||
exempt-issue-labels: pinned,security,confirmed-bug
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
Vendored
-7
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"git.showActionButton": {
|
||||
"sync": false,
|
||||
"commit": true,
|
||||
"publish": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# AGENTS.md
|
||||
|
||||
Operational Rules for AI Coding Agents
|
||||
|
||||
- Scope control: Make minimal, localized changes. Do not rewrite entire files unless explicitly instructed.
|
||||
- Edit safety: Never leave files in a broken or incomplete state. Do not stop edits mid-file. Warn before any risky or large change.
|
||||
- Style consistency: Preserve existing coding style, structure, naming, and architecture. Do not reformat code unnecessarily.
|
||||
- No cosmetic refactors: Do not perform stylistic rewrites or "cleanup" changes disguised as refactors.
|
||||
- Imports/layout/formatting: Do not change imports, formatting, or code layout unless required for correctness.
|
||||
- File protection: Do not modify the following unless explicitly requested:
|
||||
- README.md
|
||||
- LICENSE
|
||||
- .git/
|
||||
- .claude/
|
||||
- nodes/
|
||||
- core/
|
||||
- No hallucination: Do not invent APIs, features, config options, or behaviors not present in the codebase.
|
||||
- Explicit intent: If a request is ambiguous or underspecified, ask for clarification before editing.
|
||||
- Refactors: Keep refactors incremental, justified, and tightly scoped. No sweeping refactors.
|
||||
- Testing & validation: When applicable, suggest how to validate changes. Do not claim tests were run unless you ran them.
|
||||
- Communication: Briefly explain what changed and why.
|
||||
- Failure handling: Do not abort silently. If you must continue later, say so clearly and indicate what remains.
|
||||
|
||||
## Version Tagging
|
||||
|
||||
- When a commit bumps the version in `pyproject.toml`, create and push the matching Git tag targeting that exact commit before doing anything else.
|
||||
- The version-bump commit must have a user-facing release message that summarizes the actual changes in that version. Do not use a generic subject such as `Bump version to 1.0.7` by itself. Use a descriptive subject such as `Prepare 1.0.7 release with user-defined style indicators`.
|
||||
- New version tags must include a concise description of the changes shipped in that version. Create release tags as annotated tags with a title like `Release 1.0.7` and bullet-point notes summarizing the user-visible changes and important fixes. Users should be able to understand what changed from the tag page alone.
|
||||
- The tag name must exactly match the version string in `pyproject.toml` (e.g., `1.0.0`, `1.0.1`, `1.2.3`). **No `v` prefix, no other format.** This repo uses plain numeric tags only (e.g., `1.2.0`, never `v1.2.0`).
|
||||
- Before creating a release tag, inspect the existing tags in the repo (`git tag --list`) to confirm the naming convention in use. For this repo the required format is always plain numeric (e.g., `1.2.0`).
|
||||
- The README version badge (`shields.io github/v/tag`) may render the tag with a `v` prefix (e.g., display `v1.2.0`) even when the actual Git tag is `1.2.0`. This is normal shields.io display behaviour — do **not** create a `v`-prefixed tag to match the badge display.
|
||||
- Do not create GitHub releases unless explicitly requested.
|
||||
- Never move, overwrite, recreate, or retag an existing version tag to point to a different commit.
|
||||
- Each version tag is immutable: it must permanently point to the commit where that version was published.
|
||||
- If a version is pushed without the matching tag, the README version badge will become outdated or inconsistent.
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 2.6 MiB |
@@ -0,0 +1,20 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400" role="img" aria-labelledby="title desc">
|
||||
<title id="title">LoRA Pipeline icon</title>
|
||||
<desc id="desc">LP monogram in violet and yellow.</desc>
|
||||
<defs>
|
||||
<linearGradient id="panel" x1="42" y1="32" x2="360" y2="370" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#292632"/>
|
||||
<stop offset="1" stop-color="#17171d"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="accent" x1="82" y1="94" x2="318" y2="304" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#b49aff"/>
|
||||
<stop offset="1" stop-color="#ffd400"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="400" height="400" rx="88" fill="#11131a"/>
|
||||
<rect x="28" y="28" width="344" height="344" rx="72" fill="url(#panel)" stroke="url(#accent)" stroke-width="10"/>
|
||||
<text x="200" y="252" fill="url(#accent)" font-family="Arial, Helvetica, sans-serif" font-size="150" font-weight="800" letter-spacing="-8" text-anchor="middle">LP</text>
|
||||
<circle cx="86" cy="314" r="9" fill="#b49aff"/>
|
||||
<circle cx="116" cy="314" r="9" fill="#ffd400"/>
|
||||
<path d="M146 314h168" stroke="url(#accent)" stroke-width="10" stroke-linecap="round" opacity=".55"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
+22
-1
@@ -29,6 +29,7 @@ Bereichsbasierte LoRA Konditionierungs-Wrapper und LoRA Planungsknoten für Comf
|
||||
- 🧩 [Optionale Abhängigkeiten](#optional-dependencies)
|
||||
- 🧭 [Mehrbereich-Beispielworkflow](#example-workflow-multi-area-conditioning-pipeline)
|
||||
- 🖼️ [Galerie](#gallery)
|
||||
- 🚀 [Änderungsprotokoll](#changelog)
|
||||
- 💙 [Finanzierung & Unterstützung](#funding--support)
|
||||
- 📄 [Lizenz](#license)
|
||||
|
||||
@@ -120,17 +121,27 @@ Kombinieren Sie globale positive/negative Konditionierung mit der Bereichspipeli
|
||||
- `global_negative` (`CONDITIONING`, erforderlich)
|
||||
- `pipeline` (`CONDITIONING_PIPELINE`, erforderlich)
|
||||
- `global_strength` (`FLOAT`, Standard `0.3`)
|
||||
- `fast_mode` (`BOOLEAN`, Standard `false`)
|
||||
|
||||
**Ausgaben:**
|
||||
- `positive_out` (`CONDITIONING`)
|
||||
- `negative_out` (`CONDITIONING`)
|
||||
- `areas_out` (`CONDITIONING_AREAS`)
|
||||
|
||||
**Details zu `areas_out`:**
|
||||
|
||||
`areas_out` gibt die Liste der konfigurierten Bereichsregionen als strukturierte Datenausgabe aus. Jeder Eintrag enthält die normalisierten Koordinaten (`x`, `y`, `width`, `height`) und `strength`, die über `Conditioning Pipeline (Set Area)` in der Pipeline definiert wurden. Verbinden Sie `areas_out` mit [ComfyUI-OpenPose-Studio](https://github.com/andreszs/comfyui-openpose-studio), um Ihre Konditionierungsbereiche automatisch in den OpenPose-Editor zu spiegeln — die Pose-Platzierung wird sich an den genau konditionierten Regionen ausrichten. Dieser Ausgang kann auch von anderen Erweiterungen oder Knoten verwendet werden, die Bereichsmetadaten für die Maskengenerierung oder bereichsbewusste nachgelagerte Verarbeitung akzeptieren.
|
||||
|
||||
**Hinweise zum Verhalten:**
|
||||
- Wenn die Pipeline leer/ungültig ist, greifen die Ausgaben auf die globalen Eingaben zurück.
|
||||
- Wenn die Pipeline leer/ungültig ist, greifen die Ausgaben auf die globalen Eingaben zurück, und `areas_out` ist eine leere Liste.
|
||||
- Wendet regionale Einträge an und anschließend einen Standard-Kombinationsdurchgang für nicht abgedeckte Regionen.
|
||||
- `global_strength` steuert, wie stark der globale Kontext mit lokalen Bereichen konkurriert.
|
||||
- Wenn Sie global_strength zu hoch einstellen, kann dies den Einfluss der bereichsspezifischen Konditionierung verringern und sich negativ auf die Bildqualität auswirken.
|
||||
- Gute Ergebnisse werden im Allgemeinen dadurch erzielt, dass die globale Stärke mit der Stärke pro Bereich und der LoRA-Stärke in Einklang gebracht wird, anstatt alle Werte zu maximieren.
|
||||
- `fast_mode` ist optional und standardmäßig deaktiviert, sodass bestehende Workflows ihr bisheriges Verhalten beibehalten.
|
||||
- Wenn Fast Mode aktiviert ist, wird die globale positive Konditionierung an jede regionale positive Konditionierung angehängt. `global_strength` wird nicht ignoriert, sondern skaliert nur den angehängten globalen Anteil.
|
||||
- Wenn die konfigurierten Regionen die gesamte Leinwand abdecken, vermeidet Fast Mode einen separaten globalen positiven Durchgang. Bei unvollständiger Abdeckung bleibt der globale Fallback erhalten, daher fällt die Beschleunigung geringer aus.
|
||||
- Fast Mode ist mathematisch nicht identisch mit dem Standardpfad und kann Prompt-Gewichtung, Komposition oder Subjekttreue verändern. Vergleichen Sie die Ergebnisse vor dem produktiven Einsatz.
|
||||
|
||||
**Häufige Fehler:**
|
||||
- Es wird nur ein Konditionierungsstrom zugeführt, statt sowohl positiv als auch negativ.
|
||||
@@ -249,6 +260,16 @@ In [diesem Beitrag](https://www.andreszsogon.com/building-a-multi-character-comf
|
||||
|
||||
---
|
||||
|
||||
## <a id="changelog"></a>Änderungsprotokoll
|
||||
|
||||
### 1.1.4
|
||||
|
||||
- Die optimierte regionale Konditionierung reduzierte die gemessene Generierungszeit in einem getesteten SDXL-Workflow mit zwei Regionen von etwa 144 auf 70 Sekunden — rund 51 % weniger Renderzeit.
|
||||
- Der optionale Fast Mode benötigte in einem vergleichbaren Test mit vollständiger Abdeckung etwa 54 Sekunden — bis zu rund 63 % weniger Renderzeit als die vorherige Implementierung.
|
||||
- Die Leistung hängt von GPU, Modell, Auflösung, Sampler, ControlNet-Konfiguration und regionaler Abdeckung ab. Fast Mode kann auch sichtbare Unterschiede erzeugen und ist daher standardmäßig deaktiviert.
|
||||
|
||||
---
|
||||
|
||||
## <a id="funding--support"></a>Finanzierung und Unterstützung
|
||||
|
||||
### Warum Ihre Unterstützung wichtig ist
|
||||
|
||||
+22
-1
@@ -29,6 +29,7 @@ Wrappers de condicionamiento LoRA basados en áreas y nodos de planificación Lo
|
||||
- 🧩 [Dependencias opcionales](#optional-dependencies)
|
||||
- 🧭 [Workflow de ejemplo con múltiples áreas](#example-workflow-multi-area-conditioning-pipeline)
|
||||
- 🖼️ [Galería](#gallery)
|
||||
- 🚀 [Registro de cambios](#changelog)
|
||||
- 💙 [Financiamiento y apoyo](#funding--support)
|
||||
- 📄 [Licencia](#license)
|
||||
|
||||
@@ -120,17 +121,27 @@ Combinar el condicionamiento global positivo/negativo con el pipeline del área
|
||||
- `global_negative` (`CONDITIONING`, requerido)
|
||||
- `pipeline` (`CONDITIONING_PIPELINE`, requerido)
|
||||
- `global_strength` (`FLOAT`, predeterminado `0.3`)
|
||||
- `fast_mode` (`BOOLEAN`, predeterminado `false`)
|
||||
|
||||
**Salidas:**
|
||||
- `positive_out` (`CONDITIONING`)
|
||||
- `negative_out` (`CONDITIONING`)
|
||||
- `areas_out` (`CONDITIONING_AREAS`)
|
||||
|
||||
**Detalles de `areas_out`:**
|
||||
|
||||
`areas_out` expone la lista de regiones de área configuradas como una salida de datos estructurada. Cada entrada contiene las coordenadas normalizadas (`x`, `y`, `width`, `height`) y `strength` que se definieron en la canalización mediante `Conditioning Pipeline (Set Area)`. Conecte `areas_out` a [ComfyUI-OpenPose-Studio](https://github.com/andreszs/comfyui-openpose-studio) para reflejar automáticamente sus áreas de condicionamiento en el editor de OpenPose — la colocación de poses se alineará con las regiones exactas que condicionó. Esta salida también puede ser consumida por cualquier otra extensión o nodo que acepte metadatos de área para generación de máscaras o procesamiento posterior consciente de regiones.
|
||||
|
||||
**Notas de comportamiento:**
|
||||
- Si la canalización está vacía o no es válida, las salidas vuelven a las entradas globales.
|
||||
- Si la canalización está vacía o no es válida, las salidas vuelven a las entradas globales y `areas_out` es una lista vacía.
|
||||
- Aplica entradas regionales, luego un pase combinado predeterminado para regiones no cubiertas.
|
||||
- `global_strength` controla la fuerza con la que el contexto global compite con las áreas locales.
|
||||
- Presionar global_strength demasiado alto puede reducir la influencia del acondicionamiento por área y afectar negativamente la calidad de la imagen.
|
||||
- Los buenos resultados generalmente provienen de equilibrar la fuerza global con la fuerza por área y la fuerza LoRA en lugar de maximizar todos los valores.
|
||||
- `fast_mode` es opcional y viene desactivado, por lo que los workflows existentes conservan su comportamiento anterior.
|
||||
- Al activarlo, Fast Mode concatena el condicionamiento positivo global dentro de cada condicionamiento positivo regional. `global_strength` no se ignora: escala únicamente la parte global agregada.
|
||||
- Cuando las regiones configuradas cubren todo el canvas, Fast Mode evita un pase positivo global independiente. Si la cobertura es incompleta, conserva el fallback global para mantener un resultado correcto, por lo que la mejora de velocidad será menor.
|
||||
- Fast Mode no es matemáticamente idéntico a la ruta estándar y puede cambiar el equilibrio del prompt, la composición o la fidelidad de los sujetos. Compare los resultados antes de adoptarlo en workflows de producción.
|
||||
|
||||
**Errores comunes:**
|
||||
- Alimentar solo una corriente condicionante en lugar de positiva y negativa.
|
||||
@@ -249,6 +260,16 @@ En [este post](https://www.andreszsogon.com/building-a-multi-character-comfyui-w
|
||||
|
||||
---
|
||||
|
||||
## <a id="changelog"></a>Registro de cambios
|
||||
|
||||
### 1.1.4
|
||||
|
||||
- La optimización del condicionamiento regional redujo el tiempo medido de generación de aproximadamente 144 a 70 segundos en un workflow SDXL de dos regiones: cerca de un 51% menos tiempo de renderizado.
|
||||
- Se agregó el Fast Mode opcional. En una prueba equivalente con cobertura completa terminó en aproximadamente 54 segundos: hasta cerca de un 63% menos tiempo que la implementación anterior.
|
||||
- El rendimiento varía según GPU, modelo, resolución, sampler, configuración de ControlNet y cobertura regional. Fast Mode también puede producir diferencias visuales, por lo que permanece desactivado por defecto.
|
||||
|
||||
---
|
||||
|
||||
## <a id="funding--support"></a>Financiamiento y apoyo
|
||||
|
||||
### Por qué es importante su apoyo
|
||||
|
||||
+23
-2
@@ -29,6 +29,7 @@ Wrappers de conditionnement LoRA basés sur la zone et nœuds de planification L
|
||||
- 🧩 [Dépendances facultatives](#optional-dependencies)
|
||||
- 🧭 [Exemple de workflow multi-zones](#example-workflow-multi-area-conditioning-pipeline)
|
||||
- 🖼️ [Galerie](#gallery)
|
||||
- 🚀 [Journal des modifications](#changelog)
|
||||
- 💙 [Financement et soutien](#funding--support)
|
||||
- 📄 [Licence](#license)
|
||||
|
||||
@@ -120,17 +121,27 @@ Combinez le conditionnement global positif/négatif avec le pipeline régional p
|
||||
- `global_negative` (`CONDITIONING`, obligatoire)
|
||||
- `pipeline` (`CONDITIONING_PIPELINE`, obligatoire)
|
||||
- `global_strength` (`FLOAT`, par défaut `0.3`)
|
||||
- `fast_mode` (`BOOLEAN`, par défaut `false`)
|
||||
|
||||
**Sorties :**
|
||||
- `positive_out` (`CONDITIONING`)
|
||||
- `negative_out` (`CONDITIONING`)
|
||||
- `areas_out` (`CONDITIONING_AREAS`)
|
||||
|
||||
**Remarques sur le comportement :**
|
||||
- Si le pipeline est vide/invalide, les sorties reviennent aux entrées globales.
|
||||
**Détails sur `areas_out` :**
|
||||
|
||||
`areas_out` expose la liste des régions de zone configurées sous forme de sortie de données structurées. Chaque entrée contient les coordonnées normalisées (`x`, `y`, `width`, `height`) et `strength` définies dans le pipeline via `Conditioning Pipeline (Set Area)`. Connectez `areas_out` à [ComfyUI-OpenPose-Studio](https://github.com/andreszs/comfyui-openpose-studio) pour refléter automatiquement vos zones de conditionnement dans l'éditeur OpenPose — le placement des poses s'alignera sur les régions exactes que vous avez conditionnées. Cette sortie peut également être consommée par toute autre extension ou nœud acceptant des métadonnées de zone pour la génération de masques ou le traitement en aval sensible aux régions.
|
||||
|
||||
**Remarques sur le comportement :**
|
||||
- Si le pipeline est vide/invalide, les sorties reviennent aux entrées globales et `areas_out` est une liste vide.
|
||||
- Applique les entrées régionales, puis une passe de combinaison par défaut pour les régions non couvertes.
|
||||
- `global_strength` contrôle la mesure dans laquelle le contexte mondial entre en concurrence avec les zones locales.
|
||||
- Pousser global_strength trop haut peut réduire l'influence du conditionnement par zone et avoir un impact négatif sur la qualité de l'image.
|
||||
- Les bons résultats proviennent généralement d'un équilibre entre la force globale, la force par zone et la force LoRA plutôt que de maximiser toutes les valeurs.
|
||||
- `fast_mode` est facultatif et désactivé par défaut, afin que les workflows existants conservent leur comportement précédent.
|
||||
- Lorsqu'il est activé, Fast Mode concatène le conditionnement positif global à chaque conditionnement positif régional. `global_strength` n'est pas ignoré : il met uniquement à l'échelle la partie globale ajoutée.
|
||||
- Lorsque les régions configurées couvrent tout le canevas, Fast Mode évite une passe positive globale séparée. Si la couverture est incomplète, le fallback global est conservé et l'accélération sera donc moindre.
|
||||
- Fast Mode n'est pas mathématiquement identique au chemin standard et peut modifier l'équilibre du prompt, la composition ou la fidélité des sujets. Comparez les résultats avant de l'utiliser en production.
|
||||
|
||||
**Erreurs courantes :**
|
||||
- Nourrir un seul flux de conditionnement au lieu d’un flux positif et négatif.
|
||||
@@ -249,6 +260,16 @@ Consultez [cet article](https://www.andreszsogon.com/building-a-multi-character-
|
||||
|
||||
---
|
||||
|
||||
## <a id="changelog"></a>Journal des modifications
|
||||
|
||||
### 1.1.4
|
||||
|
||||
- L'optimisation du conditionnement régional a réduit le temps de génération mesuré d'environ 144 à 70 secondes dans un workflow SDXL testé avec deux régions, soit environ 51 % de temps de rendu en moins.
|
||||
- Le Fast Mode facultatif a terminé un test équivalent avec couverture complète en environ 54 secondes, soit jusqu'à environ 63 % de temps en moins que l'implémentation précédente.
|
||||
- Les performances varient selon le GPU, le modèle, la résolution, le sampler, la configuration ControlNet et la couverture régionale. Fast Mode peut également produire des différences visuelles et reste donc désactivé par défaut.
|
||||
|
||||
---
|
||||
|
||||
## <a id="funding--support"></a>Financement et soutien
|
||||
|
||||
### Pourquoi votre soutien est important
|
||||
|
||||
+22
-1
@@ -29,6 +29,7 @@
|
||||
- 🧩 [オプションの依存関係](#optional-dependencies)
|
||||
- 🧭 [マルチエリアのワークフロー例](#example-workflow-multi-area-conditioning-pipeline)
|
||||
- 🖼️ [ギャラリー](#gallery)
|
||||
- 🚀 [変更履歴](#changelog)
|
||||
- 💙 [資金とサポート](#funding--support)
|
||||
- 📄 [ライセンス](#license)
|
||||
|
||||
@@ -120,17 +121,27 @@
|
||||
- `global_negative` (`CONDITIONING`、必須)
|
||||
- `pipeline` (`CONDITIONING_PIPELINE`、必須)
|
||||
- `global_strength` (`FLOAT`、デフォルトは `0.3`)
|
||||
- `fast_mode` (`BOOLEAN`、デフォルトは `false`)
|
||||
|
||||
**出力:**
|
||||
- `positive_out` (`CONDITIONING`)
|
||||
- `negative_out` (`CONDITIONING`)
|
||||
- `areas_out` (`CONDITIONING_AREAS`)
|
||||
|
||||
**`areas_out` の詳細:**
|
||||
|
||||
`areas_out` は、設定されたエリア領域のリストを構造化データ出力として公開します。各エントリには、`Conditioning Pipeline (Set Area)` を通じてパイプラインで定義された正規化座標(`x`、`y`、`width`、`height`)と `strength` が含まれています。`areas_out` を [ComfyUI-OpenPose-Studio](https://github.com/andreszs/comfyui-openpose-studio) に接続すると、コンディショニングエリアを OpenPose エディターに自動的にミラーリングできます。ポーズの配置は、コンディショニングした正確な領域に合わせて整列されます。この出力は、マスク生成や領域認識型のダウンストリーム処理のためにエリアメタデータを受け入れる他の拡張機能やノードでも使用できます。
|
||||
|
||||
**行動メモ:**
|
||||
- パイプラインが空または無効な場合、出力はグローバル入力にフォールバックします。
|
||||
- パイプラインが空または無効な場合、出力はグローバル入力にフォールバックし、`areas_out` は空のリストになります。
|
||||
- 地域エントリを適用してから、カバーされていない地域のデフォルトの結合パスを適用します。
|
||||
- `global_strength` は、グローバル コンテキストがローカル エリアとどの程度競合するかを制御します。
|
||||
- global_strength を高くしすぎると、エリアごとのコンディショニングの影響が減少し、画質に悪影響を及ぼす可能性があります。
|
||||
- 通常、良い結果は、すべての値を最大化するのではなく、全体的な強度とエリアごとの強度および LoRA の強度のバランスを取ることで得られます。
|
||||
- `fast_mode` は任意で、デフォルトでは無効です。そのため、既存のワークフローは以前の動作を維持します。
|
||||
- Fast Mode を有効にすると、グローバルなポジティブコンディショニングが各地域のポジティブコンディショニングに連結されます。`global_strength` は無視されず、追加されたグローバル部分だけをスケーリングします。
|
||||
- 設定された地域がキャンバス全体を覆う場合、Fast Mode は独立したグローバルポジティブパスを省略します。カバーが不完全な場合はグローバルフォールバックを維持するため、速度向上は小さくなります。
|
||||
- Fast Mode は標準パスと数学的に同一ではなく、プロンプトのバランス、構図、被写体の再現性が変わる可能性があります。本番ワークフローに採用する前に結果を比較してください。
|
||||
|
||||
**よくある間違い:**
|
||||
- ポジティブとネガティブの両方ではなく、1 つのコンディショニング ストリームのみを供給します。
|
||||
@@ -249,6 +260,16 @@ ComfyUI が使用するのと同じ Python 環境に、必要なものだけを
|
||||
|
||||
---
|
||||
|
||||
## <a id="changelog"></a>変更履歴
|
||||
|
||||
### 1.1.4
|
||||
|
||||
- 地域コンディショニングの最適化により、2 地域の SDXL テストワークフローで、測定された生成時間が約 144 秒から 70 秒に短縮されました。レンダリング時間は約 51% 減少しました。
|
||||
- 任意の Fast Mode は、全体をカバーする同等のテストを約 54 秒で完了しました。以前の実装と比べて最大約 63% の時間短縮です。
|
||||
- 性能は GPU、モデル、解像度、sampler、ControlNet 設定、地域のカバー範囲によって変わります。Fast Mode は視覚的な差も生じる可能性があるため、デフォルトでは無効です。
|
||||
|
||||
---
|
||||
|
||||
## <a id="funding--support"></a>資金提供とサポート
|
||||
|
||||
### あなたのサポートが重要な理由
|
||||
|
||||
+22
-1
@@ -29,6 +29,7 @@ ComfyUI에 대한 영역 기반 LoRA 조절 래퍼 및 LoRA 예약 노드.
|
||||
- 🧩 [선택적 종속성](#optional-dependencies)
|
||||
- 🧭 [다중 영역 예시 워크플로](#example-workflow-multi-area-conditioning-pipeline)
|
||||
- 🖼️ [갤러리](#gallery)
|
||||
- 🚀 [변경 내역](#changelog)
|
||||
- 💙 [자금 및 지원](#funding--support)
|
||||
- 📄 [라이센스](#license)
|
||||
|
||||
@@ -120,17 +121,27 @@ ComfyUI에 대한 영역 기반 LoRA 조절 래퍼 및 LoRA 예약 노드.
|
||||
- `global_negative`(`CONDITIONING`, 필수)
|
||||
- `pipeline`(`CONDITIONING_PIPELINE`, 필수)
|
||||
- `global_strength`(`FLOAT`, 기본값 `0.3`)
|
||||
- `fast_mode`(`BOOLEAN`, 기본값 `false`)
|
||||
|
||||
**출력:**
|
||||
- `positive_out` (`CONDITIONING`)
|
||||
- `negative_out` (`CONDITIONING`)
|
||||
- `areas_out` (`CONDITIONING_AREAS`)
|
||||
|
||||
**`areas_out` 세부사항:**
|
||||
|
||||
`areas_out`은 구성된 영역 목록을 구조화된 데이터 출력으로 노출합니다. 각 항목에는 `Conditioning Pipeline (Set Area)`를 통해 파이프라인에서 정의된 정규화된 좌표(`x`, `y`, `width`, `height`)와 `strength`가 포함됩니다. `areas_out`을 [ComfyUI-OpenPose-Studio](https://github.com/andreszs/comfyui-openpose-studio)에 연결하면 조건화 영역을 OpenPose 편집기에 자동으로 미러링할 수 있습니다. 포즈 배치는 조건화한 정확한 영역에 맞게 정렬됩니다. 이 출력은 마스크 생성 또는 영역 인식 다운스트림 처리를 위한 영역 메타데이터를 허용하는 다른 확장 또는 노드에서도 사용할 수 있습니다.
|
||||
|
||||
**행동 참고사항:**
|
||||
- 파이프라인이 비어 있거나 유효하지 않은 경우 출력은 전역 입력으로 대체됩니다.
|
||||
- 파이프라인이 비어 있거나 유효하지 않은 경우 출력은 전역 입력으로 대체되고 `areas_out`은 빈 목록이 됩니다.
|
||||
- 지역 항목을 적용한 다음, 발견되지 않은 지역에 대한 기본 통합 패스를 적용합니다.
|
||||
- `global_strength`은 글로벌 컨텍스트가 로컬 영역과 얼마나 강력하게 경쟁하는지 제어합니다.
|
||||
- global_strength을(를) 너무 높이면 영역별 조정 영향이 줄어들고 이미지 품질에 부정적인 영향을 미칠 수 있습니다.
|
||||
- 좋은 결과는 일반적으로 모든 값을 최대화하기보다는 글로벌 강점과 지역별 강점 및 LoRA 강점의 균형을 맞추는 것에서 나옵니다.
|
||||
- `fast_mode`는 선택 사항이며 기본적으로 비활성화되어 기존 워크플로의 이전 동작을 유지합니다.
|
||||
- Fast Mode를 활성화하면 전역 포지티브 컨디셔닝이 각 지역 포지티브 컨디셔닝에 연결됩니다. `global_strength`는 무시되지 않으며 추가된 전역 부분만 스케일링합니다.
|
||||
- 구성된 영역이 전체 캔버스를 덮으면 Fast Mode는 별도의 전역 포지티브 패스를 생략합니다. 커버리지가 불완전하면 전역 fallback을 유지하므로 속도 향상은 더 작습니다.
|
||||
- Fast Mode는 표준 경로와 수학적으로 동일하지 않으며 프롬프트 균형, 구도 또는 피사체 충실도를 변경할 수 있습니다. 프로덕션 워크플로에 적용하기 전에 결과를 비교하십시오.
|
||||
|
||||
**일반적인 실수:**
|
||||
- 양성 및 음성 둘 다 대신 하나의 조건화 스트림만 공급합니다.
|
||||
@@ -249,6 +260,16 @@ ComfyUI에서 사용하는 것과 동일한 Python 환경에 필요한 것만
|
||||
|
||||
---
|
||||
|
||||
## <a id="changelog"></a>변경 내역
|
||||
|
||||
### 1.1.4
|
||||
|
||||
- 지역 컨디셔닝 최적화로 테스트한 2개 영역 SDXL 워크플로의 측정 생성 시간이 약 144초에서 70초로 줄어 렌더링 시간이 약 51% 감소했습니다.
|
||||
- 선택적 Fast Mode는 전체 커버리지의 동등한 테스트를 약 54초에 완료해 이전 구현보다 최대 약 63% 적은 시간이 걸렸습니다.
|
||||
- 성능은 GPU, 모델, 해상도, sampler, ControlNet 구성 및 지역 커버리지에 따라 달라집니다. Fast Mode는 시각적 차이를 만들 수도 있으므로 기본적으로 비활성화되어 있습니다.
|
||||
|
||||
---
|
||||
|
||||
## <a id="funding--support"></a>자금 및 지원
|
||||
|
||||
### 귀하의 지원이 중요한 이유
|
||||
|
||||
+22
-1
@@ -29,6 +29,7 @@ Area-based LoRA conditioning wrappers and LoRA Scheduling nodes for ComfyUI.
|
||||
- 🧩 [Optional Dependencies](#optional-dependencies)
|
||||
- 🧭 [Multi-area example workflow](#example-workflow-multi-area-conditioning-pipeline)
|
||||
- 🖼️ [Gallery](#gallery)
|
||||
- 🚀 [Changelog](#changelog)
|
||||
- 💙 [Funding & Support](#funding--support)
|
||||
- 📄 [License](#license)
|
||||
|
||||
@@ -120,17 +121,27 @@ Combine global positive/negative conditioning with the area pipeline for region-
|
||||
- `global_negative` (`CONDITIONING`, required)
|
||||
- `pipeline` (`CONDITIONING_PIPELINE`, required)
|
||||
- `global_strength` (`FLOAT`, default `0.3`)
|
||||
- `fast_mode` (`BOOLEAN`, default `false`)
|
||||
|
||||
**Outputs:**
|
||||
- `positive_out` (`CONDITIONING`)
|
||||
- `negative_out` (`CONDITIONING`)
|
||||
- `areas_out` (`CONDITIONING_AREAS`)
|
||||
|
||||
**`areas_out` details:**
|
||||
|
||||
`areas_out` exposes the list of configured area regions as a structured data output. Each entry contains the normalized coordinates (`x`, `y`, `width`, `height`) and `strength` that were defined in the pipeline via `Conditioning Pipeline (Set Area)`. Connect `areas_out` to [ComfyUI-OpenPose-Studio](https://github.com/andreszs/comfyui-openpose-studio) to automatically mirror your conditioning areas into the OpenPose editor — pose placement will align with the exact regions you conditioned. This output can also be consumed by any other extension or node that accepts area metadata for mask generation or region-aware downstream processing.
|
||||
|
||||
**Behavior notes:**
|
||||
- If the pipeline is empty/invalid, outputs fall back to the global inputs.
|
||||
- If the pipeline is empty/invalid, outputs fall back to the global inputs and `areas_out` is an empty list.
|
||||
- Applies regional entries, then a default combine pass for uncovered regions.
|
||||
- `global_strength` controls how strongly global context competes with local areas.
|
||||
- Pushing global_strength too high can reduce per-area conditioning influence and negatively impact image quality.
|
||||
- Good results generally come from balancing global strength with per-area strength and LoRA strength rather than maximizing all values.
|
||||
- `fast_mode` is opt-in and disabled by default, so existing workflows keep their previous behavior.
|
||||
- When enabled, Fast Mode concatenates the global positive conditioning into every regional positive conditioning. `global_strength` is not ignored: it scales only the appended global portion.
|
||||
- When the configured regions cover the full canvas, Fast Mode avoids a separate global positive pass. If coverage is incomplete, the global fallback is retained for correctness, so the speedup will be smaller.
|
||||
- Fast Mode is not mathematically identical to the standard path and may change prompt balance, composition, or subject fidelity. Compare results before adopting it for production workflows.
|
||||
|
||||
**Common mistakes:**
|
||||
- Feeding only one conditioning stream instead of both positive and negative.
|
||||
@@ -249,6 +260,16 @@ See [this post](https://www.andreszsogon.com/building-a-multi-character-comfyui-
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
### 1.1.4
|
||||
|
||||
- Optimized regional conditioning reduced measured generation time from about 144 seconds to 70 seconds in a tested two-region SDXL workflow—approximately 51% less render time.
|
||||
- Added the optional Fast Mode. In an equivalent full-coverage test it completed in about 54 seconds—up to approximately 63% less render time than the previous implementation.
|
||||
- Performance varies by GPU, model, resolution, sampler, ControlNet setup, and regional coverage. Fast Mode can also produce visual differences, so it remains disabled by default.
|
||||
|
||||
---
|
||||
|
||||
## Funding & Support
|
||||
|
||||
### Why Your Support Matters
|
||||
|
||||
+22
-1
@@ -29,6 +29,7 @@ Wrappers de condicionamento LoRA baseados em área e nós de agendamento LoRA pa
|
||||
- 🧩 [Dependências opcionais](#optional-dependencies)
|
||||
- 🧭 [Workflow de exemplo multiárea](#example-workflow-multi-area-conditioning-pipeline)
|
||||
- 🖼️ [Galeria](#gallery)
|
||||
- 🚀 [Registro de alterações](#changelog)
|
||||
- 💙 [Financiamento e Apoio](#funding--support)
|
||||
- 📄 [Licença](#license)
|
||||
|
||||
@@ -120,17 +121,27 @@ Combine o condicionamento positivo/negativo global com o pipeline de área para
|
||||
- `global_negative` (`CONDITIONING`, obrigatório)
|
||||
- `pipeline` (`CONDITIONING_PIPELINE`, obrigatório)
|
||||
- `global_strength` (`FLOAT`, padrão `0.3`)
|
||||
- `fast_mode` (`BOOLEAN`, padrão `false`)
|
||||
|
||||
**Saídas:**
|
||||
- `positive_out` (`CONDITIONING`)
|
||||
- `negative_out` (`CONDITIONING`)
|
||||
- `areas_out` (`CONDITIONING_AREAS`)
|
||||
|
||||
**Detalhes sobre `areas_out`:**
|
||||
|
||||
`areas_out` expõe a lista de regiões de área configuradas como uma saída de dados estruturada. Cada entrada contém as coordenadas normalizadas (`x`, `y`, `width`, `height`) e `strength` definidas no pipeline via `Conditioning Pipeline (Set Area)`. Conecte `areas_out` ao [ComfyUI-OpenPose-Studio](https://github.com/andreszs/comfyui-openpose-studio) para espelhar automaticamente suas áreas de condicionamento no editor OpenPose — o posicionamento das poses se alinhará com as regiões exatas que você condicionou. Esta saída também pode ser consumida por qualquer outra extensão ou nó que aceite metadados de área para geração de máscaras ou processamento posterior com reconhecimento de região.
|
||||
|
||||
**Notas de comportamento:**
|
||||
- Se o pipeline estiver vazio/inválido, as saídas voltam para as entradas globais.
|
||||
- Se o pipeline estiver vazio/inválido, as saídas voltam para as entradas globais e `areas_out` é uma lista vazia.
|
||||
- Aplica entradas regionais e, em seguida, um passe combinado padrão para regiões descobertas.
|
||||
- `global_strength` controla a intensidade com que o contexto global compete com as áreas locais.
|
||||
- Pressionar global_strength muito alto pode reduzir a influência do condicionamento por área e impactar negativamente a qualidade da imagem.
|
||||
- Bons resultados geralmente vêm do equilíbrio da força global com a força por área e da força LoRA, em vez de maximizar todos os valores.
|
||||
- `fast_mode` é opcional e vem desativado, portanto os workflows existentes mantêm o comportamento anterior.
|
||||
- Quando ativado, Fast Mode concatena o condicionamento positivo global em cada condicionamento positivo regional. `global_strength` não é ignorado: ele dimensiona apenas a parte global adicionada.
|
||||
- Quando as regiões configuradas cobrem todo o canvas, Fast Mode evita um passe positivo global separado. Se a cobertura estiver incompleta, o fallback global será mantido e o ganho de velocidade será menor.
|
||||
- Fast Mode não é matematicamente idêntico ao caminho padrão e pode alterar o equilíbrio do prompt, a composição ou a fidelidade dos temas. Compare os resultados antes de usá-lo em produção.
|
||||
|
||||
**Erros comuns:**
|
||||
- Alimentando apenas um fluxo de condicionamento em vez de positivo e negativo.
|
||||
@@ -249,6 +260,16 @@ Veja [este post](https://www.andreszsogon.com/building-a-multi-character-comfyui
|
||||
|
||||
---
|
||||
|
||||
## <a id="changelog"></a>Registro de alterações
|
||||
|
||||
### 1.1.4
|
||||
|
||||
- A otimização do condicionamento regional reduziu o tempo medido de geração de aproximadamente 144 para 70 segundos em um workflow SDXL testado com duas regiões — cerca de 51% menos tempo de renderização.
|
||||
- O Fast Mode opcional concluiu um teste equivalente com cobertura completa em aproximadamente 54 segundos — até cerca de 63% menos tempo que a implementação anterior.
|
||||
- O desempenho varia conforme GPU, modelo, resolução, sampler, configuração do ControlNet e cobertura regional. Fast Mode também pode produzir diferenças visuais e, por isso, permanece desativado por padrão.
|
||||
|
||||
---
|
||||
|
||||
## <a id="funding--support"></a>Financiamento e Apoio
|
||||
|
||||
### Por que seu apoio é importante
|
||||
|
||||
+22
-1
@@ -29,6 +29,7 @@
|
||||
- 🧩 [Необязательные зависимости](#optional-dependencies)
|
||||
- 🧭 [Пример workflow с несколькими зонами](#example-workflow-multi-area-conditioning-pipeline)
|
||||
- 🖼️ [Галерея](#gallery)
|
||||
- 🚀 [История изменений](#changelog)
|
||||
- 💙 [Финансирование и поддержка](#funding--support)
|
||||
- 📄 [Лицензия](#license)
|
||||
|
||||
@@ -120,17 +121,27 @@
|
||||
- `global_negative` (`CONDITIONING`, обязательно)
|
||||
- `pipeline` (`CONDITIONING_PIPELINE`, обязательно)
|
||||
- `global_strength` (`FLOAT`, по умолчанию `0.3`)
|
||||
- `fast_mode` (`BOOLEAN`, по умолчанию `false`)
|
||||
|
||||
**Выходы:**
|
||||
- `positive_out` (`CONDITIONING`)
|
||||
- `negative_out` (`CONDITIONING`)
|
||||
- `areas_out` (`CONDITIONING_AREAS`)
|
||||
|
||||
**Подробности об `areas_out`:**
|
||||
|
||||
`areas_out` предоставляет список настроенных областных регионов в виде структурированных выходных данных. Каждая запись содержит нормализованные координаты (`x`, `y`, `width`, `height`) и `strength`, определённые в конвейере через `Conditioning Pipeline (Set Area)`. Подключите `areas_out` к [ComfyUI-OpenPose-Studio](https://github.com/andreszs/comfyui-openpose-studio), чтобы автоматически отобразить ваши области обусловливания в редакторе OpenPose — расположение поз будет совпадать с теми самыми регионами, которые вы обусловили. Этот выход также может быть использован любым другим расширением или узлом, принимающим метаданные областей для генерации масок или регионально-ориентированной нисходящей обработки.
|
||||
|
||||
**Примечания к поведению:**
|
||||
- Если конвейер пуст/недействителен, выходные данные возвращаются к глобальным входам.
|
||||
- Если конвейер пуст/недействителен, выходные данные возвращаются к глобальным входам, а `areas_out` является пустым списком.
|
||||
- Применяет региональные записи, а затем комбинированный проход по умолчанию для непокрытых регионов.
|
||||
- `global_strength` контролирует, насколько сильно глобальный контекст конкурирует с локальными областями.
|
||||
- Слишком высокое значение global_strength может уменьшить влияние обработки каждой области и отрицательно повлиять на качество изображения.
|
||||
- Хорошие результаты обычно достигаются за счет баланса глобальной силы с силой по регионам и силы LoRA, а не за счет максимизации всех значений.
|
||||
- `fast_mode` является необязательным и по умолчанию отключён, поэтому существующие workflows сохраняют прежнее поведение.
|
||||
- При включении Fast Mode глобальное положительное кондиционирование добавляется к каждому региональному положительному кондиционированию. `global_strength` не игнорируется: он масштабирует только добавленную глобальную часть.
|
||||
- Если настроенные регионы покрывают весь холст, Fast Mode исключает отдельный глобальный положительный проход. При неполном покрытии сохраняется глобальный fallback, поэтому ускорение будет меньше.
|
||||
- Fast Mode математически не идентичен стандартному пути и может изменить баланс prompt, композицию или точность субъектов. Сравните результаты перед использованием в production workflows.
|
||||
|
||||
**Распространённые ошибки:**
|
||||
- Подача только одного обуславливающего потока вместо одновременно положительного и отрицательного.
|
||||
@@ -249,6 +260,16 @@
|
||||
|
||||
---
|
||||
|
||||
## <a id="changelog"></a>История изменений
|
||||
|
||||
### 1.1.4
|
||||
|
||||
- Оптимизация регионального кондиционирования сократила измеренное время генерации примерно со 144 до 70 секунд в протестированном SDXL workflow с двумя регионами — примерно на 51% меньше времени рендеринга.
|
||||
- Необязательный Fast Mode завершил эквивалентный тест с полным покрытием примерно за 54 секунды — до 63% меньше времени по сравнению с предыдущей реализацией.
|
||||
- Производительность зависит от GPU, модели, разрешения, sampler, конфигурации ControlNet и покрытия регионов. Fast Mode также может давать визуальные отличия, поэтому по умолчанию он отключён.
|
||||
|
||||
---
|
||||
|
||||
## <a id="funding--support"></a>Финансирование и поддержка
|
||||
|
||||
### Почему ваша поддержка важна
|
||||
|
||||
+22
-1
@@ -30,6 +30,7 @@
|
||||
- 🧩 [可選依賴項](#optional-dependencies)
|
||||
- 🧭 [多區域範例工作流程](#example-workflow-multi-area-conditioning-pipeline)
|
||||
- 🖼️ [圖庫](#gallery)
|
||||
- 🚀 [更新日誌](#changelog)
|
||||
- 💙 [資金與支持](#funding--support)
|
||||
- 📄 [許可證](#license)
|
||||
|
||||
@@ -121,17 +122,27 @@
|
||||
- `global_negative`(`CONDITIONING`,必填)
|
||||
- `pipeline`(`CONDITIONING_PIPELINE`,必填)
|
||||
- `global_strength`(`FLOAT`,預設 `0.3`)
|
||||
- `fast_mode`(`BOOLEAN`,預設 `false`)
|
||||
|
||||
**輸出:**
|
||||
- `positive_out` (`CONDITIONING`)
|
||||
- `negative_out` (`CONDITIONING`)
|
||||
- `areas_out` (`CONDITIONING_AREAS`)
|
||||
|
||||
**`areas_out` 詳情:**
|
||||
|
||||
`areas_out` 將已配置的區域清單作為結構化資料輸出公開。每個條目包含透過 `Conditioning Pipeline (Set Area)` 在管道中定義的歸一化座標(`x`、`y`、`width`、`height`)和 `strength`。將 `areas_out` 連接到 [ComfyUI-OpenPose-Studio](https://github.com/andreszs/comfyui-openpose-studio),即可將您的調節區域自動鏡像到 OpenPose 編輯器中——姿態放置將與您調節的確切區域對齊。此輸出也可被接受區域元資料的任何其他擴充功能或節點使用,用於遮罩生成或區域感知的下游處理。
|
||||
|
||||
**行為注意事項:**
|
||||
- 如果管道為空/無效,輸出將回落到全域輸入。
|
||||
- 如果管道為空/無效,輸出將回落到全域輸入,且 `areas_out` 為空清單。
|
||||
- 套用區域條目,然後對未覆蓋區域套用預設組合通道。
|
||||
- `global_strength` 控制全球背景與局部區域競爭的強度。
|
||||
- 將 global_strength 推得太高會減少每個區域的調節效果並對影像品質產生負面影響。
|
||||
- 好的結果通常來自於平衡全局強度與單位面積強度和 LoRA 強度,而不是最大化所有值。
|
||||
- `fast_mode` 是可選功能,預設關閉,因此現有工作流程會維持原有行為。
|
||||
- 啟用 Fast Mode 後,全域正向調節會連接到每個區域正向調節中。`global_strength` 不會被忽略;它只縮放新增的全域部分。
|
||||
- 當設定的區域覆蓋整個畫布時,Fast Mode 會省略獨立的全域正向計算。若覆蓋不完整,則會保留全域 fallback,因此加速幅度較小。
|
||||
- Fast Mode 與標準路徑在數學上並不完全相同,可能改變提示詞平衡、構圖或主體保真度。在正式工作流程中採用前請比較結果。
|
||||
|
||||
**常見錯誤:**
|
||||
- 僅饋送一種調節流,而不是同時饋送正流和負流。
|
||||
@@ -250,6 +261,16 @@
|
||||
|
||||
---
|
||||
|
||||
## <a id="changelog"></a>更新日誌
|
||||
|
||||
### 1.1.4
|
||||
|
||||
- 區域調節最佳化將一個雙區域 SDXL 測試工作流程的實測生成時間從約 144 秒降至 70 秒,渲染時間減少約 51%。
|
||||
- 可選的 Fast Mode 在完整覆蓋的等效測試中約 54 秒完成,與先前的實作相比最多減少約 63% 的時間。
|
||||
- 效能會依 GPU、模型、解析度、sampler、ControlNet 設定及區域覆蓋情況而異。Fast Mode 也可能產生視覺差異,因此預設關閉。
|
||||
|
||||
---
|
||||
|
||||
## <a id="funding--support"></a>資金與支持
|
||||
|
||||
### 為什麼您的支持很重要
|
||||
|
||||
+22
-1
@@ -29,6 +29,7 @@
|
||||
- 🧩 [可选依赖项](#optional-dependencies)
|
||||
- 🧭 [多区域示例工作流程](#example-workflow-multi-area-conditioning-pipeline)
|
||||
- 🖼️ [图库](#gallery)
|
||||
- 🚀 [更新日志](#changelog)
|
||||
- 💙 [资金与支持](#funding--support)
|
||||
- 📄 [许可证](#license)
|
||||
|
||||
@@ -120,17 +121,27 @@
|
||||
- `global_negative`(`CONDITIONING`,必需)
|
||||
- `pipeline`(`CONDITIONING_PIPELINE`,必需)
|
||||
- `global_strength`(`FLOAT`,默认 `0.3`)
|
||||
- `fast_mode`(`BOOLEAN`,默认 `false`)
|
||||
|
||||
**输出:**
|
||||
- `positive_out` (`CONDITIONING`)
|
||||
- `negative_out` (`CONDITIONING`)
|
||||
- `areas_out` (`CONDITIONING_AREAS`)
|
||||
|
||||
**`areas_out` 详情:**
|
||||
|
||||
`areas_out` 将已配置的区域列表作为结构化数据输出公开。每个条目包含通过 `Conditioning Pipeline (Set Area)` 在管道中定义的归一化坐标(`x`、`y`、`width`、`height`)和 `strength`。将 `areas_out` 连接到 [ComfyUI-OpenPose-Studio](https://github.com/andreszs/comfyui-openpose-studio),即可将您的调节区域自动镜像到 OpenPose 编辑器中——姿态放置将与您调节的确切区域对齐。此输出也可被接受区域元数据的任何其他扩展或节点使用,用于遮罩生成或区域感知的下游处理。
|
||||
|
||||
**行为注意事项:**
|
||||
- 如果管道为空/无效,输出将回落到全局输入。
|
||||
- 如果管道为空/无效,输出将回落到全局输入,且 `areas_out` 为空列表。
|
||||
- 应用区域条目,然后对未覆盖区域应用默认组合通道。
|
||||
- `global_strength` 控制全球背景与局部区域竞争的强度。
|
||||
- 将 global_strength 推得太高会减少每个区域的调节影响并对图像质量产生负面影响。
|
||||
- 好的结果通常来自于平衡全局强度与单位面积强度和 LoRA 强度,而不是最大化所有值。
|
||||
- `fast_mode` 是可选功能,默认关闭,因此现有工作流会保持原有行为。
|
||||
- 启用 Fast Mode 后,全局正向条件会连接到每个区域正向条件中。`global_strength` 不会被忽略;它只缩放新增的全局部分。
|
||||
- 当配置的区域覆盖整个画布时,Fast Mode 会省略单独的全局正向计算。若覆盖不完整,则会保留全局 fallback,因此加速幅度会较小。
|
||||
- Fast Mode 与标准路径并非数学上完全相同,可能改变提示词平衡、构图或主体保真度。在用于正式工作流之前请比较结果。
|
||||
|
||||
**常见错误:**
|
||||
- 仅馈送一种调节流,而不是同时馈送正流和负流。
|
||||
@@ -249,6 +260,16 @@
|
||||
|
||||
---
|
||||
|
||||
## <a id="changelog"></a>更新日志
|
||||
|
||||
### 1.1.4
|
||||
|
||||
- 区域条件优化将一个双区域 SDXL 测试工作流的实测生成时间从约 144 秒降至 70 秒,渲染时间减少约 51%。
|
||||
- 可选的 Fast Mode 在完整覆盖的等效测试中约 54 秒完成,与之前的实现相比最多减少约 63% 的时间。
|
||||
- 性能会随 GPU、模型、分辨率、sampler、ControlNet 配置及区域覆盖情况而变化。Fast Mode 也可能产生视觉差异,因此默认关闭。
|
||||
|
||||
---
|
||||
|
||||
## <a id="funding--support"></a>资金与支持
|
||||
|
||||
### 为什么您的支持很重要
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+129
-30
@@ -1,3 +1,5 @@
|
||||
import math
|
||||
|
||||
import torch
|
||||
|
||||
|
||||
@@ -24,7 +26,7 @@ class ConditioningPipelineSetArea:
|
||||
|
||||
NODE_ID = "ConditioningPipelineSetArea"
|
||||
NODE_NAME = "Conditioning Pipeline (Set Area)"
|
||||
CATEGORY = "LoRA Pipeline/Conditioning"
|
||||
CATEGORY = "LoRA Pipeline/Conditioning"
|
||||
RETURN_TYPES = ("CONDITIONING_PIPELINE",)
|
||||
RETURN_NAMES = ("pipeline_out",)
|
||||
FUNCTION = "run"
|
||||
@@ -78,14 +80,25 @@ class ConditioningPipelineCombine:
|
||||
},
|
||||
"optional": {
|
||||
"global_strength": ("FLOAT", {"default": 0.3, "min": 0.0, "max": 10.0, "step": 0.05}),
|
||||
"fast_mode": (
|
||||
"BOOLEAN",
|
||||
{
|
||||
"default": False,
|
||||
"tooltip": (
|
||||
"Faster full-canvas mode. Concatenates the global positive "
|
||||
"into every regional positive and avoids a separate global "
|
||||
"UNet pass. This changes image generation semantics."
|
||||
),
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
NODE_ID = "ConditioningPipelineCombine"
|
||||
NODE_NAME = "Conditioning Pipeline (Combine)"
|
||||
CATEGORY = "LoRA Pipeline/Conditioning"
|
||||
RETURN_TYPES = ("CONDITIONING", "CONDITIONING")
|
||||
RETURN_NAMES = ("positive_out", "negative_out")
|
||||
CATEGORY = "LoRA Pipeline/Conditioning"
|
||||
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "CONDITIONING_AREAS")
|
||||
RETURN_NAMES = ("positive_out", "negative_out", "areas_out")
|
||||
FUNCTION = "run"
|
||||
|
||||
BASE_RES = 64
|
||||
@@ -100,7 +113,45 @@ class ConditioningPipelineCombine:
|
||||
mask[y_px : y_px + h_px, x_px : x_px + w_px] = 1.0
|
||||
return mask.unsqueeze(0)
|
||||
|
||||
def run(self, global_positive, global_negative, pipeline, global_strength=0.3):
|
||||
@staticmethod
|
||||
def _make_fast_mask(x, y, width, height, res):
|
||||
mask = torch.zeros(res, res)
|
||||
x_start = max(0, min(res, math.floor(x * res)))
|
||||
y_start = max(0, min(res, math.floor(y * res)))
|
||||
x_end = max(x_start, min(res, math.ceil((x + width) * res)))
|
||||
y_end = max(y_start, min(res, math.ceil((y + height) * res)))
|
||||
mask[y_start:y_end, x_start:x_end] = 1.0
|
||||
return mask.unsqueeze(0)
|
||||
|
||||
@staticmethod
|
||||
def _concat_global(conditioning, global_positive, global_strength):
|
||||
if global_strength <= 0.0 or not conditioning or not global_positive:
|
||||
return conditioning
|
||||
|
||||
global_tokens = global_positive[0][0] * global_strength
|
||||
combined = []
|
||||
for entry in conditioning:
|
||||
tokens = torch.cat((entry[0], global_tokens), dim=1)
|
||||
combined.append([tokens, entry[1].copy()])
|
||||
return combined
|
||||
|
||||
@staticmethod
|
||||
def _masks_cover_canvas(masks):
|
||||
if not masks:
|
||||
return False
|
||||
coverage = torch.zeros_like(masks[0], dtype=torch.bool)
|
||||
for mask in masks:
|
||||
coverage |= mask > 0
|
||||
return bool(torch.all(coverage).item())
|
||||
|
||||
def run(
|
||||
self,
|
||||
global_positive,
|
||||
global_negative,
|
||||
pipeline,
|
||||
global_strength=0.3,
|
||||
fast_mode=False,
|
||||
):
|
||||
from comfy_execution.graph_utils import GraphBuilder
|
||||
|
||||
global_strength = round(float(global_strength), 2)
|
||||
@@ -114,71 +165,119 @@ class ConditioningPipelineCombine:
|
||||
if cond is not None:
|
||||
valid.append(item)
|
||||
|
||||
areas_list = [
|
||||
{
|
||||
"x": float(item["x"]),
|
||||
"y": float(item["y"]),
|
||||
"width": float(item["width"]),
|
||||
"height": float(item["height"]),
|
||||
"strength": float(item.get("strength", 1.0)),
|
||||
}
|
||||
for item in valid
|
||||
]
|
||||
|
||||
if not valid:
|
||||
return (global_positive, global_negative)
|
||||
return (global_positive, global_negative, [])
|
||||
|
||||
graph = GraphBuilder()
|
||||
|
||||
# First entry: PairConditioningSetProperties (no previous combined state yet)
|
||||
if fast_mode:
|
||||
masks = []
|
||||
acc_pos = None
|
||||
|
||||
for item in valid:
|
||||
mask = self._make_fast_mask(
|
||||
item["x"], item["y"], item["width"], item["height"], self.BASE_RES,
|
||||
)
|
||||
masks.append(mask)
|
||||
regional_positive = self._concat_global(
|
||||
item["conditioning"], global_positive, global_strength,
|
||||
)
|
||||
|
||||
if acc_pos is None:
|
||||
acc = graph.node(
|
||||
"ConditioningSetProperties",
|
||||
cond_NEW=regional_positive,
|
||||
strength=item.get("strength", 1.0),
|
||||
set_cond_area="default",
|
||||
mask=mask,
|
||||
)
|
||||
else:
|
||||
acc = graph.node(
|
||||
"ConditioningSetPropertiesAndCombine",
|
||||
cond=acc_pos,
|
||||
cond_NEW=regional_positive,
|
||||
strength=item.get("strength", 1.0),
|
||||
set_cond_area="default",
|
||||
mask=mask,
|
||||
)
|
||||
acc_pos = acc.out(0)
|
||||
|
||||
if not self._masks_cover_canvas(masks):
|
||||
fallback = graph.node(
|
||||
"ConditioningSetDefaultCombine",
|
||||
cond=acc_pos,
|
||||
cond_DEFAULT=global_positive,
|
||||
)
|
||||
acc_pos = fallback.out(0)
|
||||
|
||||
return {
|
||||
"result": (acc_pos, global_negative, areas_list),
|
||||
"expand": graph.finalize(),
|
||||
}
|
||||
|
||||
# First entry: set properties on the regional positive conditioning.
|
||||
# The negative conditioning is global and identical for every region, so
|
||||
# evaluating masked copies of it would repeat the same UNet prediction.
|
||||
first = valid[0]
|
||||
mask_0 = self._make_mask(
|
||||
first["x"], first["y"], first["width"], first["height"], self.BASE_RES,
|
||||
)
|
||||
acc = graph.node(
|
||||
"PairConditioningSetProperties",
|
||||
positive_NEW=first["conditioning"],
|
||||
negative_NEW=global_negative,
|
||||
"ConditioningSetProperties",
|
||||
cond_NEW=first["conditioning"],
|
||||
strength=first.get("strength", 1.0),
|
||||
set_cond_area="default",
|
||||
mask=mask_0,
|
||||
)
|
||||
acc_pos = acc.out(0)
|
||||
acc_neg = acc.out(1)
|
||||
|
||||
# Entries siguientes: PairConditioningSetPropertiesAndCombine
|
||||
# Following entries: combine regional positive conditionings only.
|
||||
for item in valid[1:]:
|
||||
mask_i = self._make_mask(
|
||||
item["x"], item["y"], item["width"], item["height"], self.BASE_RES,
|
||||
)
|
||||
acc = graph.node(
|
||||
"PairConditioningSetPropertiesAndCombine",
|
||||
positive=acc_pos,
|
||||
negative=acc_neg,
|
||||
positive_NEW=item["conditioning"],
|
||||
negative_NEW=global_negative,
|
||||
"ConditioningSetPropertiesAndCombine",
|
||||
cond=acc_pos,
|
||||
cond_NEW=item["conditioning"],
|
||||
strength=item.get("strength", 1.0),
|
||||
set_cond_area="default",
|
||||
mask=mask_i,
|
||||
)
|
||||
acc_pos = acc.out(0)
|
||||
acc_neg = acc.out(1)
|
||||
|
||||
# Global como entrada full-image con strength controlable
|
||||
if global_strength > 0.0:
|
||||
global_mask = self._make_mask(0.0, 0.0, 1.0, 1.0, self.BASE_RES)
|
||||
acc = graph.node(
|
||||
"PairConditioningSetPropertiesAndCombine",
|
||||
positive=acc_pos,
|
||||
negative=acc_neg,
|
||||
positive_NEW=global_positive,
|
||||
negative_NEW=global_negative,
|
||||
"ConditioningSetPropertiesAndCombine",
|
||||
cond=acc_pos,
|
||||
cond_NEW=global_positive,
|
||||
strength=global_strength,
|
||||
set_cond_area="default",
|
||||
mask=global_mask,
|
||||
)
|
||||
acc_pos = acc.out(0)
|
||||
acc_neg = acc.out(1)
|
||||
|
||||
# Default fallback (safety net para pixeles sin cubrir)
|
||||
final = graph.node(
|
||||
"PairConditioningSetDefaultCombine",
|
||||
positive=acc_pos,
|
||||
negative=acc_neg,
|
||||
positive_DEFAULT=global_positive,
|
||||
negative_DEFAULT=global_negative,
|
||||
"ConditioningSetDefaultCombine",
|
||||
cond=acc_pos,
|
||||
cond_DEFAULT=global_positive,
|
||||
)
|
||||
|
||||
return {
|
||||
"result": (final.out(0), final.out(1)),
|
||||
"result": (final.out(0), global_negative, areas_list),
|
||||
"expand": graph.finalize(),
|
||||
}
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
|
||||
# Palette: distinct colors readable on dark and light backgrounds
|
||||
_AREA_COLORS = [
|
||||
(41, 121, 255), # Blue
|
||||
(255, 140, 0), # Orange
|
||||
(0, 200, 80), # Green
|
||||
(220, 50, 220), # Magenta
|
||||
(0, 200, 230), # Cyan
|
||||
(210, 180, 0), # Gold
|
||||
]
|
||||
|
||||
# Grid visual constants (match the OpenPose Studio canvas style)
|
||||
_GRID_SPACING = 64
|
||||
_BG_COLOR = (26, 26, 26) # #1a1a1a
|
||||
_GRID_COLOR = (220, 220, 220) # light gray
|
||||
_GRID_ALPHA = 0.50
|
||||
_AXIS_X_COLOR = (0, 255, 0) # green
|
||||
_AXIS_Y_COLOR = (255, 0, 0) # red
|
||||
_AXIS_ALPHA = 0.30
|
||||
_BORDER_COLOR = (160, 160, 160)
|
||||
_BORDER_ALPHA = 0.70
|
||||
|
||||
_FILL_ALPHA = 0.18
|
||||
_BORDER_LINE_ALPHA = 0.85
|
||||
_LABEL_BG_ALPHA = 0.68
|
||||
_LABEL_TEXT_ALPHA = 0.92
|
||||
|
||||
_MIN_DIM_FOR_FULL_LABEL = 80
|
||||
_FONT_SIZE = 11
|
||||
_PAD_H = 4
|
||||
_PAD_V = 2
|
||||
_LABEL_INSET = 6
|
||||
_BORDER_RADIUS = 3
|
||||
|
||||
|
||||
def _load_font(size):
|
||||
from PIL import ImageFont
|
||||
try:
|
||||
return ImageFont.load_default(size=size)
|
||||
except TypeError:
|
||||
return ImageFont.load_default()
|
||||
|
||||
|
||||
def _alpha_blend(base_rgba, overlay_rgba):
|
||||
"""Blend overlay onto base; both (R,G,B,A) tuples with A in 0-255."""
|
||||
ba = base_rgba[3] / 255.0
|
||||
oa = overlay_rgba[3] / 255.0
|
||||
out_a = oa + ba * (1.0 - oa)
|
||||
if out_a < 1e-6:
|
||||
return (0, 0, 0, 0)
|
||||
out_rgb = tuple(
|
||||
int((overlay_rgba[i] * oa + base_rgba[i] * ba * (1.0 - oa)) / out_a)
|
||||
for i in range(3)
|
||||
)
|
||||
return out_rgb + (int(out_a * 255),)
|
||||
|
||||
|
||||
def _render_area_preview(areas, width, height):
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
w, h = int(width), int(height)
|
||||
if w < 1 or h < 1:
|
||||
w, h = 64, 64
|
||||
|
||||
# ---- background ----
|
||||
img = Image.new("RGBA", (w, h), _BG_COLOR + (255,))
|
||||
|
||||
# ---- grid ----
|
||||
grid_layer = Image.new("RGBA", (w, h), (0, 0, 0, 0))
|
||||
gd = ImageDraw.Draw(grid_layer)
|
||||
ga = int(_GRID_ALPHA * 255)
|
||||
cx, cy = w // 2, h // 2
|
||||
|
||||
for x in range(_GRID_SPACING, w, _GRID_SPACING):
|
||||
if abs(x - cx) < 1:
|
||||
continue
|
||||
gd.line([(x, 0), (x, h - 1)], fill=_GRID_COLOR + (ga,), width=1)
|
||||
for y in range(_GRID_SPACING, h, _GRID_SPACING):
|
||||
if abs(y - cy) < 1:
|
||||
continue
|
||||
gd.line([(0, y), (w - 1, y)], fill=_GRID_COLOR + (ga,), width=1)
|
||||
|
||||
aa = int(_AXIS_ALPHA * 255)
|
||||
gd.line([(cx, 0), (cx, h - 1)], fill=_AXIS_Y_COLOR + (aa,), width=1)
|
||||
gd.line([(0, cy), (w - 1, cy)], fill=_AXIS_X_COLOR + (aa,), width=1)
|
||||
|
||||
img = Image.alpha_composite(img, grid_layer)
|
||||
|
||||
# ---- fills ----
|
||||
fills_layer = Image.new("RGBA", (w, h), (0, 0, 0, 0))
|
||||
fd = ImageDraw.Draw(fills_layer)
|
||||
for i, area in enumerate(areas):
|
||||
r, g, b = _AREA_COLORS[i % len(_AREA_COLORS)]
|
||||
rx = int(area["x"] * w)
|
||||
ry = int(area["y"] * h)
|
||||
rw = max(1, int(area["width"] * w))
|
||||
rh = max(1, int(area["height"] * h))
|
||||
fill_a = int(_FILL_ALPHA * 255)
|
||||
try:
|
||||
fd.rounded_rectangle(
|
||||
[rx, ry, rx + rw, ry + rh],
|
||||
radius=_BORDER_RADIUS,
|
||||
fill=(r, g, b, fill_a),
|
||||
)
|
||||
except AttributeError:
|
||||
fd.rectangle([rx, ry, rx + rw, ry + rh], fill=(r, g, b, fill_a))
|
||||
|
||||
img = Image.alpha_composite(img, fills_layer)
|
||||
|
||||
# ---- borders ----
|
||||
borders_layer = Image.new("RGBA", (w, h), (0, 0, 0, 0))
|
||||
bd = ImageDraw.Draw(borders_layer)
|
||||
for i, area in enumerate(areas):
|
||||
r, g, b = _AREA_COLORS[i % len(_AREA_COLORS)]
|
||||
rx = int(area["x"] * w)
|
||||
ry = int(area["y"] * h)
|
||||
rw = max(1, int(area["width"] * w))
|
||||
rh = max(1, int(area["height"] * h))
|
||||
border_a = int(_BORDER_LINE_ALPHA * 255)
|
||||
try:
|
||||
bd.rounded_rectangle(
|
||||
[rx, ry, rx + rw, ry + rh],
|
||||
radius=_BORDER_RADIUS,
|
||||
outline=(r, g, b, border_a),
|
||||
width=2,
|
||||
)
|
||||
except AttributeError:
|
||||
bd.rectangle([rx, ry, rx + rw, ry + rh], outline=(r, g, b, border_a), width=2)
|
||||
|
||||
img = Image.alpha_composite(img, borders_layer)
|
||||
|
||||
# ---- labels ----
|
||||
labels_layer = Image.new("RGBA", (w, h), (0, 0, 0, 0))
|
||||
ld = ImageDraw.Draw(labels_layer)
|
||||
font = _load_font(_FONT_SIZE)
|
||||
|
||||
for i, area in enumerate(areas):
|
||||
rx = int(area["x"] * w)
|
||||
ry = int(area["y"] * h)
|
||||
rw = max(1, int(area["width"] * w))
|
||||
rh = max(1, int(area["height"] * h))
|
||||
|
||||
idx = i + 1
|
||||
short_label = f"Area {idx}"
|
||||
full_label = (
|
||||
f"Area {idx} · x={area['x']:.2f} y={area['y']:.2f}"
|
||||
f" w={area['width']:.2f} h={area['height']:.2f}"
|
||||
)
|
||||
label = full_label if (rw >= _MIN_DIM_FOR_FULL_LABEL and rh >= _MIN_DIM_FOR_FULL_LABEL) else short_label
|
||||
|
||||
# Measure text
|
||||
try:
|
||||
bbox = ld.textbbox((0, 0), label, font=font)
|
||||
text_w = bbox[2] - bbox[0]
|
||||
text_h = bbox[3] - bbox[1]
|
||||
except AttributeError:
|
||||
# Pillow < 8.0.0
|
||||
text_w, text_h = ld.textsize(label, font=font) # type: ignore[attr-defined]
|
||||
|
||||
badge_w = text_w + _PAD_H * 2
|
||||
badge_h = text_h + _PAD_V * 2
|
||||
bx = rx + _LABEL_INSET
|
||||
by = ry + _LABEL_INSET
|
||||
|
||||
# Ensure label fits within the rectangle
|
||||
if bx + badge_w > rx + rw or by + badge_h > ry + rh:
|
||||
label = short_label
|
||||
try:
|
||||
bbox = ld.textbbox((0, 0), label, font=font)
|
||||
text_w = bbox[2] - bbox[0]
|
||||
text_h = bbox[3] - bbox[1]
|
||||
except AttributeError:
|
||||
text_w, text_h = ld.textsize(label, font=font) # type: ignore[attr-defined]
|
||||
badge_w = text_w + _PAD_H * 2
|
||||
badge_h = text_h + _PAD_V * 2
|
||||
|
||||
bg_a = int(_LABEL_BG_ALPHA * 255)
|
||||
try:
|
||||
ld.rounded_rectangle(
|
||||
[bx, by, bx + badge_w, by + badge_h],
|
||||
radius=3,
|
||||
fill=(0, 0, 0, bg_a),
|
||||
)
|
||||
except AttributeError:
|
||||
ld.rectangle([bx, by, bx + badge_w, by + badge_h], fill=(0, 0, 0, bg_a))
|
||||
|
||||
txt_a = int(_LABEL_TEXT_ALPHA * 255)
|
||||
ld.text((bx + _PAD_H, by + _PAD_V), label, font=font, fill=(255, 255, 255, txt_a))
|
||||
|
||||
img = Image.alpha_composite(img, labels_layer)
|
||||
|
||||
# ---- outer border ----
|
||||
outer = ImageDraw.Draw(img)
|
||||
ob_a = int(_BORDER_ALPHA * 255)
|
||||
outer.rectangle([0, 0, w - 1, h - 1], outline=_BORDER_COLOR + (ob_a,), width=1)
|
||||
|
||||
# ---- convert to float32 RGB tensor [1, H, W, 3] ----
|
||||
arr = np.array(img.convert("RGB")).astype(np.float32) / 255.0
|
||||
return torch.from_numpy(arr).unsqueeze(0)
|
||||
|
||||
|
||||
class ConditioningAreaPreview:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"areas": ("CONDITIONING_AREAS",),
|
||||
"width": ("INT", {"default": 512, "min": 64, "max": 4096, "step": 8}),
|
||||
"height": ("INT", {"default": 512, "min": 64, "max": 4096, "step": 8}),
|
||||
}
|
||||
}
|
||||
|
||||
NODE_ID = "ConditioningAreaPreview"
|
||||
NODE_NAME = "Conditioning Area Preview"
|
||||
CATEGORY = "LoRA Pipeline/Conditioning"
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("preview",)
|
||||
FUNCTION = "run"
|
||||
|
||||
def run(self, areas, width, height):
|
||||
if not isinstance(areas, list) or len(areas) == 0:
|
||||
# Return an empty dark canvas
|
||||
empty = torch.zeros((1, int(height), int(width), 3), dtype=torch.float32)
|
||||
return (empty,)
|
||||
|
||||
tensor = _render_area_preview(areas, width, height)
|
||||
return (tensor,)
|
||||
+3
-2
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "comfyui-lora-pipeline"
|
||||
version = "1.0.0"
|
||||
version = "1.1.4"
|
||||
description = "Area-based LoRA scheduling and area conditioning wrappers for ComfyUI."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
@@ -19,4 +19,5 @@ Documentation = "https://github.com/andreszs/comfyui-lora-pipeline#readme"
|
||||
[tool.comfy]
|
||||
PublisherId = "andreszs"
|
||||
DisplayName = "LoRA Pipeline"
|
||||
Icon = ""
|
||||
Icon = "https://raw.githubusercontent.com/andreszs/comfyui-lora-pipeline/main/assets/icon.svg"
|
||||
Banner = "https://raw.githubusercontent.com/andreszs/comfyui-lora-pipeline/refs/heads/main/assets/banner_112.gif"
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
import importlib.util
|
||||
import sys
|
||||
import types
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
import torch
|
||||
|
||||
|
||||
_MODULE_PATH = Path(__file__).parents[1] / "nodes" / "cond_area_pipeline.py"
|
||||
_SPEC = importlib.util.spec_from_file_location("lora_pipeline_cond_area", _MODULE_PATH)
|
||||
_MODULE = importlib.util.module_from_spec(_SPEC)
|
||||
_SPEC.loader.exec_module(_MODULE)
|
||||
ConditioningPipelineCombine = _MODULE.ConditioningPipelineCombine
|
||||
|
||||
|
||||
class _Output:
|
||||
def __init__(self, node_type, index):
|
||||
self.node_type = node_type
|
||||
self.index = index
|
||||
|
||||
|
||||
class _Node:
|
||||
def __init__(self, node_type):
|
||||
self.node_type = node_type
|
||||
|
||||
def out(self, index):
|
||||
return _Output(self.node_type, index)
|
||||
|
||||
|
||||
class _GraphBuilder:
|
||||
instance = None
|
||||
|
||||
def __init__(self):
|
||||
self.calls = []
|
||||
_GraphBuilder.instance = self
|
||||
|
||||
def node(self, node_type, **inputs):
|
||||
self.calls.append((node_type, inputs))
|
||||
return _Node(node_type)
|
||||
|
||||
def finalize(self):
|
||||
return self.calls
|
||||
|
||||
|
||||
class ConditioningPipelineCombineTests(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
graph_utils = types.ModuleType("comfy_execution.graph_utils")
|
||||
graph_utils.GraphBuilder = _GraphBuilder
|
||||
comfy_execution = types.ModuleType("comfy_execution")
|
||||
comfy_execution.graph_utils = graph_utils
|
||||
sys.modules["comfy_execution"] = comfy_execution
|
||||
sys.modules["comfy_execution.graph_utils"] = graph_utils
|
||||
|
||||
def test_negative_conditioning_is_not_duplicated_per_area(self):
|
||||
global_positive = object()
|
||||
global_negative = object()
|
||||
pipeline = [
|
||||
{
|
||||
"conditioning": object(),
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"width": 0.5,
|
||||
"height": 1.0,
|
||||
"strength": 0.8,
|
||||
},
|
||||
{
|
||||
"conditioning": object(),
|
||||
"x": 0.5,
|
||||
"y": 0.0,
|
||||
"width": 0.5,
|
||||
"height": 1.0,
|
||||
"strength": 0.8,
|
||||
},
|
||||
]
|
||||
|
||||
result = ConditioningPipelineCombine().run(
|
||||
global_positive,
|
||||
global_negative,
|
||||
pipeline,
|
||||
global_strength=0.3,
|
||||
)
|
||||
|
||||
positive, negative, areas = result["result"]
|
||||
calls = _GraphBuilder.instance.calls
|
||||
|
||||
self.assertIs(negative, global_negative)
|
||||
self.assertEqual(len(areas), 2)
|
||||
self.assertEqual(
|
||||
[node_type for node_type, _ in calls],
|
||||
[
|
||||
"ConditioningSetProperties",
|
||||
"ConditioningSetPropertiesAndCombine",
|
||||
"ConditioningSetPropertiesAndCombine",
|
||||
"ConditioningSetDefaultCombine",
|
||||
],
|
||||
)
|
||||
self.assertEqual(positive.node_type, "ConditioningSetDefaultCombine")
|
||||
self.assertFalse(any(node_type.startswith("PairConditioning") for node_type, _ in calls))
|
||||
|
||||
def test_fast_mode_concatenates_global_positive_and_skips_full_coverage_fallback(self):
|
||||
global_positive = [[torch.full((1, 1, 2), 2.0), {}]]
|
||||
global_negative = object()
|
||||
pipeline = [
|
||||
{
|
||||
"conditioning": [[torch.full((1, 1, 2), 1.0), {}]],
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"width": 0.5,
|
||||
"height": 1.0,
|
||||
"strength": 0.8,
|
||||
},
|
||||
{
|
||||
"conditioning": [[torch.full((1, 1, 2), 3.0), {}]],
|
||||
"x": 0.5,
|
||||
"y": 0.0,
|
||||
"width": 0.5,
|
||||
"height": 1.0,
|
||||
"strength": 0.8,
|
||||
},
|
||||
]
|
||||
|
||||
result = ConditioningPipelineCombine().run(
|
||||
global_positive,
|
||||
global_negative,
|
||||
pipeline,
|
||||
global_strength=0.25,
|
||||
fast_mode=True,
|
||||
)
|
||||
|
||||
positive, negative, _ = result["result"]
|
||||
calls = _GraphBuilder.instance.calls
|
||||
first_tokens = calls[0][1]["cond_NEW"][0][0]
|
||||
|
||||
self.assertIs(negative, global_negative)
|
||||
self.assertEqual(
|
||||
[node_type for node_type, _ in calls],
|
||||
[
|
||||
"ConditioningSetProperties",
|
||||
"ConditioningSetPropertiesAndCombine",
|
||||
],
|
||||
)
|
||||
self.assertEqual(positive.node_type, "ConditioningSetPropertiesAndCombine")
|
||||
self.assertEqual(first_tokens.shape[1], 2)
|
||||
torch.testing.assert_close(first_tokens[:, 1:, :], torch.full((1, 1, 2), 0.5))
|
||||
|
||||
def test_fast_mode_keeps_global_fallback_for_incomplete_coverage(self):
|
||||
global_positive = [[torch.ones((1, 1, 2)), {}]]
|
||||
pipeline = [
|
||||
{
|
||||
"conditioning": [[torch.ones((1, 1, 2)), {}]],
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"width": 0.5,
|
||||
"height": 1.0,
|
||||
"strength": 1.0,
|
||||
},
|
||||
]
|
||||
|
||||
result = ConditioningPipelineCombine().run(
|
||||
global_positive,
|
||||
object(),
|
||||
pipeline,
|
||||
fast_mode=True,
|
||||
)
|
||||
|
||||
positive = result["result"][0]
|
||||
calls = _GraphBuilder.instance.calls
|
||||
|
||||
self.assertEqual(calls[-1][0], "ConditioningSetDefaultCombine")
|
||||
self.assertEqual(positive.node_type, "ConditioningSetDefaultCombine")
|
||||
|
||||
def test_fast_mode_overlapping_fractional_regions_cover_canvas(self):
|
||||
conditioning = [[torch.ones((1, 1, 2)), {}]]
|
||||
pipeline = [
|
||||
{
|
||||
"conditioning": conditioning,
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"width": 0.52,
|
||||
"height": 1.0,
|
||||
"strength": 1.0,
|
||||
},
|
||||
{
|
||||
"conditioning": conditioning,
|
||||
"x": 0.48,
|
||||
"y": 0.0,
|
||||
"width": 0.52,
|
||||
"height": 1.0,
|
||||
"strength": 1.0,
|
||||
},
|
||||
]
|
||||
|
||||
ConditioningPipelineCombine().run(
|
||||
conditioning,
|
||||
object(),
|
||||
pipeline,
|
||||
fast_mode=True,
|
||||
)
|
||||
|
||||
calls = _GraphBuilder.instance.calls
|
||||
self.assertFalse(
|
||||
any(node_type == "ConditioningSetDefaultCombine" for node_type, _ in calls),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user