Non-semantic Options (NOVA)
A Perceptual Attack as a Settings Blob
- NS_OPT
Most of this pack is traditional image processing with a camera-pipeline theme. Non-semantic Options (NOVA) is the exception: it's the one node doing actual machine learning, and it's the reason the pack's README cites the UnMarker paper. You don't need to understand the math to use it - but you do need to know it's heavy, because it's the slowest thing here by a mile.
What it is. This is a settings-bundle node, same pattern as CameraOptionsNode: it takes a pile of numbers and outputs a single NS_OPT JSON string (custom type NONSEMANTICOP) that plugs into NovaNodes' NS_Opt input. In NovaNodes, those settings drive the pack's attack_non_semantic() function, which implements the "non-semantic attack" from the UnMarker paper (Kassis & Hengartner): it optimizes a tiny additive perturbation so the image pushes back against detection-style classifiers while staying perceptually indistinguishable. Think of it as the AI Normalizer the README mentions - the term makes more sense than "non-semantic attack" if you're not coming from the watermark-evasion literature.
How it works. The code is readable even if you're not a PyTorch person: it initializes a random perturbation delta (scaled to 1e-5), then runs an Adam optimizer over it. At each step it computes a spectral loss from the FFT difference (encouraging the perturbed image to match the original's spectrum), an LPIPS perceptual loss using an AlexNet-based model, and an L2 norm on the perturbation. It penalizes each loss only past its threshold (ns_t_lpips, ns_t_l2), scales the penalties (ns_c_lpips, ns_c_l2), and clips gradients. LPIPS keeps it invisible to a human; the L2 term keeps the perturbation physically tiny; the spectral term is what makes it meaningfully different to a detector. It runs first in the NovaNodes pipeline, before any of the camera effects.
Inputs that matter.
- non_semantic - the master switch. False by default, and if it's false the whole optimizer never runs. Most users never turn it on.
- ns_iterations - 500 default. More iterations = stronger effect, linearly slower. This is the first thing to drop when you're iterating on speed.
- ns_learning_rate - 0.0003 default. Leave it unless you know what you're doing; the other knobs matter more.
- ns_t_lpips / ns_t_l2 - the perceptual and geometric thresholds. Crank the LPIPS one up if you want a stronger nudge and can tolerate a hair more visible change.
- ns_grad_clip - gradient clipping, 0.05 default. This is what keeps the optimization from exploding into visible garbage.
The one output: NS_OPT, wired into NovaNodes.NS_Opt. Standalone, it does nothing - no image out, same as its camera sibling.
Gotchas. First run with non_semantic on downloads the LPIPS weights, so don't panic if it stalls on a fresh install. It also wants a GPU: 500 Adam iterations of a PyTorch optimizer on a CPU over a full-res image is a patience test. And remember NovaNodes has built-in NS_DEFAULTS with non_semantic: false, so if you never wire this node in, nothing about the attack changes - it's off, cleanly.
Install is the pack-wide one - ComfyUI Manager → ComfyUI-NovaNoiser, or clone into custom_nodes and pip install -r requirements.txt. That file pulls in lpips, which is the one dependency this node genuinely can't live without (plus the pinned torch build the pack wants).
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| non_semantic | BOOLEAN | false | — |
| ns_iterations | INT | 5001–10000 | — |
| ns_learning_rate | FLOAT | 0.00030.000001–1 | — |
| ns_t_lpips | FLOAT | 0.04000–1 | — |
| ns_t_l2 | FLOAT | 0.00000–1 | — |
| ns_c_lpips | FLOAT | 0.01000–1 | — |
| ns_c_l2 | FLOAT | 0.6000–10 | — |
| ns_grad_clip | FLOAT | 0.05000–1 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| NS_OPT | NONSEMANTICOP | — |