For batch text watermarking:
# Add text watermark to all JPGs
for f in *.jpg; do
convert "$f" \
-font "Arial-Bold" -pointsize 36 -fill "#FFFFFF80" \
-gravity southeast -annotate +20+20 "© My Company" \
"watermarked/${f}"
done
Parameters:
-font: font family
-pointsize 36: font size
-fill "#FFFFFF80": white at 50% opacity
-gravity southeast: position (southeast = bottom-right)
-annotate +20+20: offset from gravity point
For logo watermark:
for f in *.jpg; do
convert "$f" watermark.png \
-gravity southeast -geometry +20+20 \
-composite "watermarked/${f}"
done
watermark.png should be a transparent PNG with the logo. The -composite operator blends it onto the source.
For batch processing patterns, see Batch Processing Files Guide.
For subtler protection:
# Watermark at 30% opacity
for f in *.jpg; do
convert "$f" \( watermark.png -alpha set -channel A -evaluate set 30% +channel \) \
-gravity southeast -geometry +20+20 -composite \
"watermarked/${f}"
done
The -channel A -evaluate set 30% reduces alpha channel to 30%. Watermark is visible but doesn't dominate.
For a tiled diagonal watermark:
# Create diagonal watermark
convert -size 800x600 xc:none -font "Arial-Bold" -pointsize 60 \
-fill "#FFFFFF20" -gravity center -annotate -45 "© My Company" \
diagonal_watermark.png
# Apply to images
for f in *.jpg; do
convert "$f" diagonal_watermark.png -gravity center -composite "watermarked/${f}"
done
Diagonal watermarks are harder to crop out. Useful for stock photo protection.
For Node.js workflows:
const sharp = require("sharp");
const path = require("path");
const fs = require("fs").promises;
async function watermark(inputPath, outputPath, watermarkPath) {
await sharp(inputPath)
.composite([
{
input: watermarkPath,
gravity: "southeast",
},
])
.toFile(outputPath);
}
const inputs = await fs.readdir("./input");
for (const file of inputs.filter((f) => f.endsWith(".jpg"))) {
await watermark(`./input/${file}`, `./output/${file}`, "./watermark.png");
}
Sharp is dramatically faster than ImageMagick for high-volume batch (5-10x faster).
For Sharp vs ImageMagick comparison, see ImageMagick vs GraphicsMagick.
| Position | Use case |
|---|
| Bottom-right | Standard signature/copyright |
| Top-right | Less common, draws attention |
| Bottom-center | Stock photo style |
| Center diagonal | Stock photo, hard to crop |
| All four corners | Maximum protection |
| Tiled | Maximum protection |
For most casual use: bottom-right at 30-50% opacity.
For invisible identifying marks (forensic protection):
from PIL import Image
def embed_watermark(image_path, message, output_path):
img = Image.open(image_path)
pixels = img.load()
binary_message = ''.join(format(ord(c), '08b') for c in message)
pixel_index = 0
for bit in binary_message:
x = pixel_index % img.width
y = pixel_index // img.width
if y >= img.height:
break
r, g, b = pixels[x, y][:3]
# Modify least significant bit of red
r = (r & 0xFE) | int(bit)
pixels[x, y] = (r, g, b)
pixel_index += 1
img.save(output_path)
# Usage
embed_watermark("photo.jpg", "JohnDoe-2026-05-08", "marked.png")
The watermark is invisible to humans but readable with the extraction code. Useful for forensic identification of leaked images.
Note: JPG re-encoding can destroy LSB watermarks. Use PNG for invisible watermarks. Use stronger algorithms (DCT-based) for JPG.
For non-visible identification:
# Add copyright to EXIF
exiftool -overwrite_original \
-copyright="© My Company 2026" \
-artist="Photographer Name" \
*.jpg
EXIF metadata is part of the file. Survives most JPG re-saves but stripped by some social platforms.
Watermark too large: pointsize too big. Scale based on image dimensions:
# Watermark scales with image size
convert input.jpg \
\( -size 30%x -gravity southeast -font "Arial" \
-pointsize $(( $(identify -format '%h' input.jpg) / 20 )) \
-fill "#FFFFFF80" -annotate +20+20 "© Company" \) \
-composite output.jpg
Logo watermark pixelated: source PNG too small. Use high-resolution watermark (2000x2000 minimum), scale down per image.
Color of watermark wrong on certain images: dark image + dark watermark = invisible. Use semi-transparent white or black depending on image.
Batch script slow: ImageMagick is slower than Sharp. Switch to Sharp for high-volume.
Watermark crops off: gravity position incorrect. Check ImageMagick gravity reference.
| Method | Removal difficulty |
|---|
| Visible text/logo | Easy (crop or content-aware fill) |
| Diagonal across image | Hard (covers significant area) |
| Tiled across image | Very hard (destroys image) |
| Invisible steganography | Very hard (destroys image quality) |
| Combined approach | Hardest |
For high-value content: combined approach (visible + invisible).
For broader batch image work, see ImageMagick vs GraphicsMagick.
Watermarks deter casual copying. Determined infringers can remove visible watermarks. They serve as evidence in legal disputes.
For commercial photographers: yes, before sharing. For casual photographers: depends on context (social media: usually not).
Yes. Library Module > Export > Watermarking. Apply per-export. Useful but per-export rather than batch.
For visible but unobtrusive: 25-50% opacity. For aggressive protection: 60-80%.
Visible watermarks: never (part of image). Invisible watermarks (LSB): yes, often. EXIF metadata: usually stripped.
For your own watermarks: re-export from source. For others' watermarks: typically illegal. Don't.
For batch image watermarking in 2026: ImageMagick for command-line scripting, Sharp for Node.js high-volume. Visible watermarks for attribution, transparent overlays for branding, invisible steganography for forensic protection. Combine for high-value content. Our image converter handles related image processing.