Remove Watermark from Video - LTX 2.3 LoRA
Upload an input video with watermarks and remove them with one click.
Inside the workflow
A read-only preview of the source template. Open it in Cloud to explore the nodes and make changes.
Loading the graph
Open full-size workflow preview- Output
- Video
- Author
- Rob
Call this model from your code
The request below uses the same prepared graph and settings as the Playground. Each submission can start a paid run; do not automatically retry a submission whose outcome is unknown.
What you need
- Runs on
- Comfy Cloud
- Key
- COMFY_API_KEY
- Your files
- Uploaded by the code from their urls
Needs a paid Cloud plan and credits.
# Python 3.10+: pip install comfy-sdk==0.4.0
import os
from comfy_sdk import Comfy
api_key = os.environ.get("COMFY_API_KEY", "").strip()
if not api_key: raise RuntimeError("Set COMFY_API_KEY")
os.environ.setdefault("COMFY_BASE_URL", "https://cloud.comfy.org")
client = Comfy(api_key=api_key)
workflow = client.workflows.from_json({
"5159": {
"class_type": "SaveVideo",
"inputs": {
"filename_prefix": "video",
"format": "auto",
"format.codec": "auto",
"video": [
"5161:5062",
0
]
},
"_meta": {
"title": "Save Video"
}
},
"5160": {
"class_type": "LoadVideo",
"inputs": {
"file": "UPLOADED_video_FILENAME"
},
"_meta": {
"title": "Load Video"
}
},
"5162": {
"class_type": "GetVideoComponents",
"inputs": {
"video": [
"5160",
0
]
},
"_meta": {
"title": "Get Video Components"
}
},
"5175": {
"class_type": "SaveVideo",
"inputs": {
"filename_prefix": "video_compare",
"format": "auto",
"format.codec": "auto",
"video": [
"5174:80",
0
]
},
"_meta": {
"title": "Save Comparison Video"
}
},
"5161:5057": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"5161:5084",
0
],
"text": "pc game, console game, video game, cartoon, childish, ugly"
},
"_meta": {
"title": "CLIP Text Encode (Negative Prompt)"
}
},
"5161:5058": {
"class_type": "RandomNoise",
"inputs": {
"noise_seed": 1
},
"_meta": {
"title": "RandomNoise"
}
},
"5161:5059": {
"class_type": "LatentUpscaleModelLoader",
"inputs": {
"model_name": "ltx-2.3-spatial-upscaler-x2-1.1.safetensors"
},
"_meta": {
"title": "Load Latent Upscale Model"
}
},
"5161:5060": {
"class_type": "LTXVConditioning",
"inputs": {
"frame_rate": [
"5161:5088",
2
],
"negative": [
"5161:5057",
0
],
"positive": [
"5161:5091",
0
]
},
"_meta": {
"title": "LTXVConditioning"
}
},
"5161:5061": {
"class_type": "CFGGuider",
"inputs": {
"cfg": 1,
"model": [
"5161:5087",
0
],
"negative": [
"5161:5098",
1
],
"positive": [
"5161:5098",
0
]
},
"_meta": {
"title": "CFG Guider"
}
},
"5161:5062": {
"class_type": "CreateVideo",
"inputs": {
"audio": [
"5162",
1
],
"bit_depth": "auto",
"codec": "none",
"color_space": "sRGB",
"fps": [
"5161:5088",
2
],
"images": [
"5161:5090",
0
]
},
"_meta": {
"title": "Create Video"
}
},
"5161:5063": {
"class_type": "KSamplerSelect",
"inputs": {
"sampler_name": "euler"
},
"_meta": {
"title": "KSamplerSelect"
}
},
"5161:5064": {
"class_type": "ManualSigmas",
"inputs": {
"sigmas": "1.0, 0.99375, 0.9875, 0.98125, 0.975, 0.909375, 0.725, 0.421875, 0.0"
},
"_meta": {
"title": "ManualSigmas"
}
},
"5161:5065": {
"class_type": "LTXVEmptyLatentAudio",
"inputs": {
"audio_vae": [
"5161:5086",
0
],
"batch_size": 1,
"frame_rate": [
"5161:5095",
0
],
"frames_number": [
"5161:5097",
1
]
},
"_meta": {
"title": "Empty LTXV Latent Audio"
}
},
"5161:5066": {
"class_type": "LTXVConcatAVLatent",
"inputs": {
"audio_latent": [
"5161:5065",
0
],
"video_latent": [
"5161:5094",
2
]
},
"_meta": {
"title": "Concat AV Latent"
}
},
"5161:5067": {
"class_type": "SamplerCustomAdvanced",
"inputs": {
"guider": [
"5161:5061",
0
],
"latent_image": [
"5161:5066",
0
],
"noise": [
"5161:5058",
0
],
"sampler": [
"5161:5063",
0
],
"sigmas": [
"5161:5064",
0
]
},
"_meta": {
"title": "SamplerCustomAdvanced"
}
},
"5161:5068": {
"class_type": "ResizeImageMaskNode",
"inputs": {
"input": [
"5161:5088",
0
],
"resize_type": "scale dimensions",
"resize_type.crop": "disabled",
"resize_type.height": 1088,
"resize_type.width": 1920,
"scale_method": "area"
},
"_meta": {
"title": "Resize Image/Mask (s2 size)"
}
},
"5161:5069": {
"class_type": "LTXVLatentUpsampler",
"inputs": {
"samples": [
"5161:5082",
2
],
"upscale_model": [
"5161:5059",
0
],
"vae": [
"5161:5085",
2
]
},
"_meta": {
"title": "LTXVLatentUpsampler"
}
},
"5161:5070": {
"class_type": "LTXVSeparateAVLatent",
"inputs": {
"av_latent": [
"5161:5067",
0
]
},
"_meta": {
"title": "Separate AV Latent"
}
},
"5161:5071": {
"class_type": "LTXAddVideoICLoRAGuide",
"inputs": {
"crop": "disabled",
"frame_idx": 0,
"image": [
"5161:5068",
0
],
"latent": [
"5161:5069",
0
],
"latent_downscale_factor": 1,
"negative": [
"5161:5099",
1
],
"positive": [
"5161:5099",
0
],
"strength": 1,
"tile_frames": 97,
"tile_overlap": 64,
"tile_size": 256,
"use_streaming": False,
"use_tiled_encode": True,
"vae": [
"5161:5085",
2
]
},
"_meta": {
"title": "🅛🅣🅧 Add Video IC-LoRA Guide"
}
},
"5161:5072": {
"class_type": "KSamplerSelect",
"inputs": {
"sampler_name": "euler"
},
"_meta": {
"title": "KSamplerSelect"
}
},
"5161:5073": {
"class_type": "SamplerCustomAdvanced",
"inputs": {
"guider": [
"5161:5076",
0
],
"latent_image": [
"5161:5077",
0
],
"noise": [
"5161:5078",
0
],
"sampler": [
"5161:5072",
0
],
"sigmas": [
"5161:5075",
0
]
},
"_meta": {
"title": "SamplerCustomAdvanced"
}
},
"5161:5074": {
"class_type": "LTXVCropGuides",
"inputs": {
"latent": [
"5161:5080",
0
],
"negative": [
"5161:5071",
1
],
"positive": [
"5161:5071",
0
]
},
"_meta": {
"title": "LTXVCropGuides"
}
},
"5161:5075": {
"class_type": "ManualSigmas",
"inputs": {
"sigmas": "0.909375, 0.725, 0.421875, 0.0"
},
"_meta": {
"title": "ManualSigmas"
}
},
"5161:5076": {
"class_type": "CFGGuider",
"inputs": {
"cfg": 1,
"model": [
"5161:5087",
0
],
"negative": [
"5161:5071",
1
],
"positive": [
"5161:5071",
0
]
},
"_meta": {
"title": "CFG Guider"
}
},
"5161:5077": {
"class_type": "LTXVConcatAVLatent",
"inputs": {
"audio_latent": [
"5161:5099",
2
],
"video_latent": [
"5161:5071",
2
]
},
"_meta": {
"title": "Concat AV Latent"
}
},
"5161:5078": {
"class_type": "RandomNoise",
"inputs": {
"noise_seed": 29
},
"_meta": {
"title": "RandomNoise"
}
},
"5161:5079": {
"class_type": "LTXVAudioVAEDecode",
"inputs": {
"audio_vae": [
"5161:5086",
0
],
"samples": [
"5161:5080",
1
]
},
"_meta": {
"title": "LTXV Audio VAE Decode"
}
},
"5161:5080": {
"class_type": "LTXVSeparateAVLatent",
"inputs": {
"av_latent": [
"5161:5073",
0
]
},
"_meta": {
"title": "Separate AV Latent"
}
},
"5161:5082": {
"class_type": "LTXVCropGuides",
"inputs": {
"latent": [
"5161:5070",
0
],
"negative": [
"5161:5094",
1
],
"positive": [
"5161:5094",
0
]
},
"_meta": {
"title": "LTXVCropGuides"
}
},
"5161:5083": {
"class_type": "LTXVAudioVAEEncode",
"inputs": {
"audio": [
"5161:5088",
1
],
"audio_vae": [
"5161:5086",
0
]
},
"_meta": {
"title": "LTXV Audio VAE Encode"
}
},
"5161:5084": {
"class_type": "LTXAVTextEncoderLoader",
"inputs": {
"ckpt_name": "ltx-2.3-22b-dev.safetensors",
"device": "default",
"text_encoder": "gemma_3_12B_it.safetensors"
},
"_meta": {
"title": "Load LTXV Audio Text Encoder"
}
},
"5161:5085": {
"class_type": "CheckpointLoaderSimple",
"inputs": {
"ckpt_name": "ltx-2.3-22b-dev.safetensors"
},
"_meta": {
"title": "Load Checkpoint"
}
},
"5161:5086": {
"class_type": "LTXVAudioVAELoader",
"inputs": {
"ckpt_name": "ltx-2.3-22b-dev.safetensors"
},
"_meta": {
"title": "Load LTXV Audio VAE"
}
},
"5161:5087": {
"class_type": "LTXICLoRALoaderModelOnly",
"inputs": {
"lora_name": "ltx2.3-ic-watermark-remove-general.safetensors",
"model": [
"5161:5085",
0
],
"strength_model": 1.5
},
"_meta": {
"title": "🅛🅣🅧 IC-LoRA Loader Model Only"
}
},
"5161:5088": {
"class_type": "GetVideoComponents",
"inputs": {
"video": [
"5160",
0
]
},
"_meta": {
"title": "Get Video Components"
}
},
"5161:5089": {
"class_type": "ResizeImageMaskNode",
"inputs": {
"input": [
"5161:5088",
0
],
"resize_type": "scale dimensions",
"resize_type.crop": "disabled",
"resize_type.height": 544,
"resize_type.width": 960,
"scale_method": "area"
},
"_meta": {
"title": "Resize Image/Mask (s1 size)"
}
},
"5161:5090": {
"class_type": "LTXVTiledVAEDecode",
"inputs": {
"horizontal_tiles": 2,
"last_frame_fix": False,
"latents": [
"5161:5074",
2
],
"overlap": 6,
"vae": [
"5161:5085",
2
],
"vertical_tiles": 2,
"working_device": "auto",
"working_dtype": "auto"
},
"_meta": {
"title": "🅛🅣🅧 LTXV Tiled VAE Decode"
}
},
"5161:5091": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"5161:5084",
0
],
"text": "Remove the short-video platform watermarks and every related occlusion from this clip: the semi-transparent account handle that drifts across the frame and jumps to a new position halfway through, and the small rounded logo badge with its name in the top-left corner. Restore what was hidden underneath them so the picture looks untouched: the wet cobblestones, the ornate green streetlamp, the limestone facades with their shutters and balconies, the swirling golden leaves and the dancing man in his mustard trousers and striped sweater. Keep the original motion, framing, colour and golden-hour light exactly as they are, with clean, sharp, natural detail and no blur, smudges or ghosting where the overlays used to be."
},
"_meta": {
"title": "CLIP Text Encode (Positive Prompt)"
}
},
"5161:5093": {
"class_type": "EmptyLTXVLatentVideo",
"inputs": {
"batch_size": 1,
"height": 544,
"length": [
"5161:5097",
1
],
"width": 960
},
"_meta": {
"title": "Empty LTXV Latent Video"
}
},
"5161:5094": {
"class_type": "LTXAddVideoICLoRAGuide",
"inputs": {
"crop": "disabled",
"frame_idx": 0,
"image": [
"5161:5089",
0
],
"latent": [
"5161:5093",
0
],
"latent_downscale_factor": 1,
"negative": [
"5161:5060",
1
],
"positive": [
"5161:5060",
0
],
"strength": 1,
"tile_frames": 97,
"tile_overlap": 64,
"tile_size": 256,
"use_streaming": False,
"use_tiled_encode": True,
"vae": [
"5161:5085",
2
]
},
"_meta": {
"title": "🅛🅣🅧 Add Video IC-LoRA Guide"
}
},
"5161:5095": {
"class_type": "LTXFloatToInt",
"inputs": {
"a": [
"5161:5088",
2
]
},
"_meta": {
"title": "🅛🅣🅧 Float To Int"
}
},
"5161:5096": {
"class_type": "GetImageSizeAndCount",
"inputs": {
"image": [
"5161:5089",
0
]
},
"_meta": {
"title": "Get Image Size & Count"
}
},
"5161:5097": {
"class_type": "ComfyMathExpression",
"inputs": {
"expression": "(int((a - 1) / 8)) * 8 + 1",
"values.a": [
"5161:5096",
3
]
},
"_meta": {
"title": "Math Expression"
}
},
"5161:5098": {
"class_type": "LTXVSetAudioRefTokens",
"inputs": {
"audio_latent": [
"5161:5083",
0
],
"negative": [
"5161:5094",
1
],
"positive": [
"5161:5094",
0
]
},
"_meta": {
"title": "🅛🅣🅧 Set Audio Ref Tokens"
}
},
"5161:5099": {
"class_type": "LTXVSetAudioRefTokens",
"inputs": {
"audio_latent": [
"5161:5070",
1
],
"negative": [
"5161:5082",
1
],
"positive": [
"5161:5082",
0
]
},
"_meta": {
"title": "🅛🅣🅧 Set Audio Ref Tokens"
}
},
"5174:77": {
"class_type": "GetVideoComponents",
"inputs": {
"video": [
"5160",
0
]
},
"_meta": {
"title": "Get Video Components"
}
},
"5174:78": {
"class_type": "GetVideoComponents",
"inputs": {
"video": [
"5161:5062",
0
]
},
"_meta": {
"title": "Get Video Components"
}
},
"5174:79": {
"class_type": "ImageStitch",
"inputs": {
"direction": "down",
"image1": [
"5174:77",
0
],
"image2": [
"5174:78",
0
],
"match_image_size": False,
"spacing_color": "white",
"spacing_width": 0
},
"_meta": {
"title": "Stitch Images"
}
},
"5174:80": {
"class_type": "CreateVideo",
"inputs": {
"audio": [
"5174:77",
1
],
"bit_depth": "auto",
"codec": "none",
"color_space": "sRGB",
"fps": [
"5174:77",
2
],
"images": [
"5174:79",
0
]
},
"_meta": {
"title": "Create Video"
}
}
})
workflow.set_input("5160", "file", client.assets.from_url("https://comfy.org/catalog-media/2f731481-9bd0-4266-8d74-16ae4ccceded/b328bcd8-cf71-44aa-8ba2-b6581db74676"))
# run() also passes api_key to the partner nodes in this workflow.
job = client.run(workflow, api_key=api_key)
for output in job.get_outputs("5159"):
output.to_file(output.name)
for output in job.get_outputs("5175"):
output.to_file(output.name)Try an example
About this workflow
This ComfyUI workflow removes persistent watermarks from a video by inpainting over them frame by frame with a LoRA-enhanced diffusion pass. It starts with LoadVideo to ingest your clip and GetVideoComponents to extract frames, dimensions, and timing info. The core processing is handled by two custom nodes (UUIDs 3b019976-c705-40af-8f45-081975d72105 and 83cfc570-fff1-4869-8c43-d39ef189634b), which apply an LTX 2.3 LoRA-guided inpainting step across the sequence to reconstruct clean background detail where the watermark sits.
Finally, SaveVideo reassembles the processed frames into a new video file.
Technically, the pipeline performs video-to-video inpainting using a Stable Diffusion-style model augmented by the LTX 2.3 LoRA adapter to bias reconstruction toward plausible content while suppressing the watermark. The custom processing stage maintains per-frame consistency and uses mask-driven edits so only the marked area is regenerated, preserving the rest of the frame. Because the work happens at the frame level and is driven by inpainting, the result avoids typical blur or smudge artifacts of traditional filters and holds up better on semi-transparent, corner logos.
It’s a practical, one-click solution for cleaning owned or licensed footage without manual rotoscoping.
Questions & answers
It uses an inpainting pipeline guided by the LTX 2.3 LoRA. Place the LTX 2.3 LoRA in models/loras and ensure a compatible base checkpoint is available in models/checkpoints. The custom processing node (UUIDs 3b019976-c705-40af-8f45-081975d72105 and 83cfc570-fff1-4869-8c43-d39ef189634b) handles applying the LoRA across video frames.
It’s optimized for persistent, static watermarks (e.g., corner logos or date stamps). Moving, animated, or large-area overlays are harder to inpaint consistently and may require parameter tuning, pre-masking, or manual cleanup.
SaveVideo typically writes the processed video stream only. If you need the original audio, remux it afterward using a video editor or a command like: ffmpeg -i clean.mp4 -i source.mp4 -map 0:v -map 1:a -c copy final.mp4.
Try moderately increasing the LoRA weight and denoise strength, downscaling slightly to reduce processing noise, and ensuring the watermark region is fully covered by the inpainting mask settings in the custom node. For very stubborn cases, process in segments around scene cuts.