
Media Processing
- 18 installs
- 95 repo stars
- Updated July 7, 2026
- binjuhor/shadcn-lar
media-processing is a Claude skill for processing video, audio, and images using the FFmpeg, ImageMagick, and RMBG command-line tools.
About
This skill processes multimedia files using FFmpeg, ImageMagick, and RMBG command-line tools. A developer uses it to convert media formats, encode video with specific codecs, resize or crop images, extract audio, remove backgrounds, generate thumbnails, or build media processing pipelines. It includes a tool-selection table and ready-to-run command examples.
- Processes video, audio, and images with FFmpeg, ImageMagick, and RMBG
- Covers encoding, conversion, resizing, and AI background removal
- Supports 100+ formats and hardware acceleration
Media Processing by the numbers
- 18 all-time installs (skills.sh)
- Ranked #1,007 of 1,337 Generative Media skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
media-processing capabilities & compatibility
- Capabilities
- video generation · image generation
- Use cases
- video generation · image generation
- Platforms
- macOS · Linux
What media-processing says it does
Process video, audio, and images using FFmpeg, ImageMagick, and RMBG CLI tools.
Background removal | RMBG | AI-powered, local processing
Supports 100+ formats, hardware acceleration (NVENC, QSV), and complex filtergraphs.
npx skills add https://github.com/binjuhor/shadcn-lar --skill media-processingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 18 |
|---|---|
| repo stars | ★ 95 |
| Last updated | July 7, 2026 |
| Repository | binjuhor/shadcn-lar ↗ |
What it does
Convert, encode, resize, and process video, audio, and images with FFmpeg, ImageMagick, and RMBG.
Who is it for?
Developers converting media formats, encoding video, resizing or editing images, or removing backgrounds via CLI.
Skip if: Generating new media from prompts; this skill processes existing files rather than creating content.
When should I use this skill?
Converting media formats, encoding video with a specific codec, resizing images, removing a background, extracting audio, or building a media pipeline.
What you get
Media is converted, encoded, resized, or background-removed correctly using the appropriate CLI tool and flags.
By the numbers
- Three CLI tools: FFmpeg, ImageMagick, RMBG
- Supports 100+ formats
Files
Media Processing Skill
Process video, audio, and images using FFmpeg, ImageMagick, and RMBG CLI tools.
Tool Selection
| Task | Tool | Reason |
|---|---|---|
| Video encoding/conversion | FFmpeg | Native codec support, streaming |
| Audio extraction/conversion | FFmpeg | Direct stream manipulation |
| Image resize/effects | ImageMagick | Optimized for still images |
| Background removal | RMBG | AI-powered, local processing |
| Batch images | ImageMagick | mogrify for in-place edits |
| Video thumbnails | FFmpeg | Frame extraction built-in |
| GIF creation | FFmpeg/ImageMagick | FFmpeg for video, ImageMagick for images |
Installation
# macOS
brew install ffmpeg imagemagick
npm install -g rmbg-cli
# Ubuntu/Debian
sudo apt-get install ffmpeg imagemagick
npm install -g rmbg-cli
# Verify
ffmpeg -version && magick -version && rmbg --versionEssential Commands
# Video: Convert/re-encode
ffmpeg -i input.mkv -c copy output.mp4
ffmpeg -i input.avi -c:v libx264 -crf 22 -c:a aac output.mp4
# Video: Extract audio
ffmpeg -i video.mp4 -vn -c:a copy audio.m4a
# Image: Convert/resize
magick input.png output.jpg
magick input.jpg -resize 800x600 output.jpg
# Image: Batch resize
mogrify -resize 800x -quality 85 *.jpg
# Background removal
rmbg input.jpg # Basic (modnet)
rmbg input.jpg -m briaai -o output.png # High quality
rmbg input.jpg -m u2netp -o output.png # FastKey Parameters
FFmpeg:
-c:v libx264- H.264 codec-crf 22- Quality (0-51, lower=better)-preset slow- Speed/compression balance-c:a aac- Audio codec
ImageMagick:
800x600- Fit within (maintains aspect)800x600^- Fill (may crop)-quality 85- JPEG quality-strip- Remove metadata
RMBG:
-m briaai- High quality model-m u2netp- Fast model-r 4096- Max resolution
References
Detailed guides in references/:
ffmpeg-encoding.md- Codecs, quality, hardware accelerationffmpeg-streaming.md- HLS/DASH, live streamingffmpeg-filters.md- Filters, complex filtergraphsimagemagick-editing.md- Effects, transformationsimagemagick-batch.md- Batch processing, parallel opsrmbg-background-removal.md- AI models, CLI usagecommon-workflows.md- Video optimization, responsive images, GIF creationtroubleshooting.md- Error fixes, performance tipsformat-compatibility.md- Format support, codec recommendations
Common Media Processing Workflows
Video Optimization
Optimize for Web
# H.264 with good compression
ffmpeg -i input.mp4 \
-c:v libx264 -preset slow -crf 23 \
-c:a aac -b:a 128k \
-movflags +faststart \
output.mp4Multi-Pass Encoding
# Pass 1 (analysis)
ffmpeg -y -i input.mkv -c:v libx264 -b:v 2600k -pass 1 -an -f null /dev/null
# Pass 2 (encoding)
ffmpeg -i input.mkv -c:v libx264 -b:v 2600k -pass 2 -c:a aac output.mp4Hardware-Accelerated Encoding
# NVIDIA NVENC
ffmpeg -hwaccel cuda -i input.mp4 -c:v h264_nvenc -preset fast -crf 22 output.mp4
# Intel QuickSync
ffmpeg -hwaccel qsv -c:v h264_qsv -i input.mp4 -c:v h264_qsv output.mp4Extract Video Segment
# From 1:30 to 3:00 (re-encode for precision)
ffmpeg -i input.mp4 -ss 00:01:30 -to 00:03:00 \
-c:v libx264 -c:a aac output.mp4Image Workflows
Create Responsive Images
# Generate multiple sizes
for size in 320 640 1024 1920; do
magick input.jpg -resize ${size}x -quality 85 "output-${size}w.jpg"
doneBatch Image Optimization
# Convert PNG to optimized JPEG
mogrify -path ./optimized -format jpg -quality 85 -strip *.pngComplex Image Pipeline
# Resize, crop, border, adjust
magick input.jpg \
-resize 1000x1000^ \
-gravity center \
-crop 1000x1000+0+0 +repage \
-bordercolor black -border 5x5 \
-brightness-contrast 5x10 \
-quality 90 \
output.jpgGIF Creation
Video to GIF
# High quality GIF with palette
ffmpeg -i input.mp4 -vf "fps=15,scale=640:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" output.gifAnimated GIF from Images
# Create with delay
magick -delay 100 -loop 0 frame*.png animated.gif
# Optimize size
magick animated.gif -fuzz 5% -layers Optimize optimized.gifBackground Removal Workflows
Batch Background Removal
# Process all images in directory
for img in *.jpg; do
rmbg "$img" -m modnet -o "${img%.jpg}-no-bg.png"
doneProduct Photography
# 1. Remove background
rmbg product.jpg -m u2net-cloth -o product-no-bg.png
# 2. Resize to multiple sizes
magick product-no-bg.png -resize 800x800 product-800.png
magick product-no-bg.png -resize 400x400 product-400.png
# 3. Add white background if needed
magick product-no-bg.png -background white -flatten product-white-bg.jpgMedia Analysis
Inspect Video Properties
# Detailed JSON output
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4
# Get resolution
ffprobe -v error -select_streams v:0 \
-show_entries stream=width,height \
-of csv=s=x:p=0 input.mp4Image Information
# Basic info
identify image.jpg
# Detailed format
identify -verbose image.jpg
# Custom format
identify -format "%f: %wx%h %b\n" image.jpgFFmpeg Video & Audio Encoding
Complete guide to codec selection, quality optimization, and hardware acceleration.
Video Codecs
H.264 (libx264)
Most widely supported codec, excellent compression/quality balance.
Best for: Universal compatibility, streaming, web video
Quality range: CRF 17-28 (lower = better)
# High quality
ffmpeg -i input.mkv -c:v libx264 -preset slow -crf 18 -c:a copy output.mp4
# Standard quality (recommended)
ffmpeg -i input.mkv -c:v libx264 -preset medium -crf 23 -c:a copy output.mp4
# Fast encoding
ffmpeg -i input.mkv -c:v libx264 -preset fast -crf 23 -c:a copy output.mp4H.265/HEVC (libx265)
25-50% better compression than H.264, slower encoding.
Best for: 4K video, file size reduction, archival
# High quality 4K
ffmpeg -i input.mkv -c:v libx265 -preset medium -crf 24 -c:a copy output.mp4
# Balanced quality
ffmpeg -i input.mkv -c:v libx265 -preset fast -crf 26 -c:a copy output.mp4VP9 (libvpx-vp9)
Royalty-free, WebM format, good for YouTube and open-source projects.
Best for: YouTube, Chrome/Firefox, open platforms
# Quality-based (recommended)
ffmpeg -i input.mkv -c:v libvpx-vp9 -crf 30 -b:v 0 -c:a libopus output.webm
# Two-pass for better quality
ffmpeg -i input.mkv -c:v libvpx-vp9 -b:v 2M -pass 1 -an -f null /dev/null
ffmpeg -i input.mkv -c:v libvpx-vp9 -b:v 2M -pass 2 -c:a libopus output.webmAV1 (libaom-av1, libsvtav1)
Next-generation codec, best compression, very slow encoding.
Best for: Future-proofing, maximum compression, low bandwidth
# Using libaom (slow, highest quality)
ffmpeg -i input.mkv -c:v libaom-av1 -crf 30 -b:v 0 -strict experimental output.mp4
# Using SVT-AV1 (faster)
ffmpeg -i input.mkv -c:v libsvtav1 -crf 30 -preset 5 output.mp4Audio Codecs
AAC (Industry Standard)
Best quality for streaming, universal support.
# High quality
ffmpeg -i input.mp4 -c:a aac -b:a 192k output.mp4
# Standard quality
ffmpeg -i input.mp4 -c:a aac -b:a 128k output.mp4
# Low bitrate
ffmpeg -i input.mp4 -c:a aac -b:a 96k output.mp4MP3 (libmp3lame)
Universal compatibility, good quality.
# Variable bitrate (best quality)
ffmpeg -i input.wav -c:a libmp3lame -q:a 0 output.mp3
# Constant bitrate
ffmpeg -i input.wav -c:a libmp3lame -b:a 192k output.mp3Opus (libopus)
Best quality at low bitrates, ideal for voice and streaming.
# Voice (mono)
ffmpeg -i input.mp4 -c:a libopus -b:a 32k -ac 1 output.webm
# Music (stereo)
ffmpeg -i input.mp4 -c:a libopus -b:a 128k output.webmFLAC (Lossless)
No quality loss, archival quality, larger files.
# Lossless audio
ffmpeg -i input.wav -c:a flac output.flac
# Extract audio losslessly
ffmpeg -i video.mp4 -c:a flac audio.flacQuality Optimization
CRF (Constant Rate Factor)
Best for quality-focused encoding. Single-pass, adjusts bitrate for complexity.
CRF Scale:
- 0 = Lossless (huge files)
- 17-18 = Visually lossless
- 20-23 = High quality (recommended)
- 24-28 = Medium quality
- 30+ = Low quality
- 51 = Worst quality
# Visually lossless
ffmpeg -i input.mp4 -c:v libx264 -crf 18 -preset slow output.mp4
# High quality (recommended)
ffmpeg -i input.mp4 -c:v libx264 -crf 22 -preset medium output.mp4
# Balanced quality/size
ffmpeg -i input.mp4 -c:v libx264 -crf 25 -preset fast output.mp4Bitrate-Based Encoding
Target specific file size or quality. Two-pass recommended.
# Calculate target bitrate
# bitrate = (target_size_MB * 8192) / duration_seconds - audio_bitrate
# Two-pass encoding (2600k video, 128k audio)
ffmpeg -y -i input.mkv -c:v libx264 -b:v 2600k -pass 1 -an -f null /dev/null
ffmpeg -i input.mkv -c:v libx264 -b:v 2600k -pass 2 -c:a aac -b:a 128k output.mp4Presets (Speed vs Compression)
Trade-off between encoding speed and file size.
Available presets:
ultrafast- Fastest, largest filessuperfastveryfastfasterfastmedium- Default balanceslow- Better compressionslowerveryslow- Best compressionplacebo- Not recommended (minimal gains)
# Fast encoding (real-time)
ffmpeg -i input.mp4 -c:v libx264 -preset ultrafast -crf 23 output.mp4
# Balanced
ffmpeg -i input.mp4 -c:v libx264 -preset medium -crf 22 output.mp4
# Best compression (slow)
ffmpeg -i input.mp4 -c:v libx264 -preset veryslow -crf 20 output.mp4Hardware Acceleration
NVIDIA NVENC
5-10x faster encoding, slightly larger files than software encoding.
Requirements: NVIDIA GPU (GTX 10xx or newer)
# H.264 with NVENC
ffmpeg -hwaccel cuda -i input.mp4 -c:v h264_nvenc -preset fast -crf 22 output.mp4
# H.265 with NVENC
ffmpeg -hwaccel cuda -i input.mp4 -c:v hevc_nvenc -preset slow -crf 24 output.mp4
# Quality levels (instead of CRF)
ffmpeg -hwaccel cuda -i input.mp4 -c:v h264_nvenc -preset slow -rc vbr -cq 22 output.mp4NVENC Presets:
default- Balancedslow- Better qualitymediumfasthp- High performancehq- High qualitybd- Bluray diskll- Low latencyllhq- Low latency high qualityllhp- Low latency high performance
Intel QuickSync (QSV)
Fast hardware encoding on Intel CPUs with integrated graphics.
Requirements: Intel CPU with Quick Sync Video support
# H.264 with QSV
ffmpeg -hwaccel qsv -c:v h264_qsv -i input.mp4 \
-c:v h264_qsv -preset fast -global_quality 22 output.mp4
# H.265 with QSV
ffmpeg -hwaccel qsv -c:v hevc_qsv -i input.mp4 \
-c:v hevc_qsv -preset medium -global_quality 24 output.mp4
# Quality levels
ffmpeg -hwaccel qsv -i input.mp4 -c:v h264_qsv -global_quality 20 output.mp4AMD VCE/VCN
Hardware encoding on AMD GPUs.
Requirements: AMD GPU with VCE/VCN support
# H.264 with AMF
ffmpeg -hwaccel auto -i input.mp4 \
-c:v h264_amf -quality balanced -rc cqp -qp 22 output.mp4
# H.265 with AMF
ffmpeg -hwaccel auto -i input.mp4 \
-c:v hevc_amf -quality quality -rc cqp -qp 24 output.mp4Apple VideoToolbox (macOS)
Hardware encoding on macOS devices.
# H.264 with VideoToolbox
ffmpeg -i input.mp4 -c:v h264_videotoolbox -b:v 2M output.mp4
# H.265 with VideoToolbox
ffmpeg -i input.mp4 -c:v hevc_videotoolbox -b:v 1.5M output.mp4Performance Tuning
Multi-Threading
FFmpeg automatically uses multiple cores. Override if needed:
# Limit threads
ffmpeg -threads 4 -i input.mp4 -c:v libx264 output.mp4
# Auto (default)
ffmpeg -threads 0 -i input.mp4 -c:v libx264 output.mp4Tune Options
Optimize encoder for specific content types:
# Film content
ffmpeg -i input.mp4 -c:v libx264 -tune film -crf 22 output.mp4
# Animation
ffmpeg -i input.mp4 -c:v libx264 -tune animation -crf 22 output.mp4
# Grain (film with noise)
ffmpeg -i input.mp4 -c:v libx264 -tune grain -crf 22 output.mp4
# Low latency streaming
ffmpeg -i input.mp4 -c:v libx264 -tune zerolatency -crf 22 output.mp4
# Screen content (sharp edges)
ffmpeg -i input.mp4 -c:v libx264 -tune stillimage -crf 22 output.mp4Codec Selection Guide
Use Cases
| Use Case | Codec | Settings |
|---|---|---|
| Web video | H.264 | CRF 23, preset medium |
| 4K streaming | H.265 | CRF 24, preset fast |
| YouTube upload | VP9 or H.264 | CRF 23 |
| Archive | H.265 or H.264 | CRF 18, preset slow |
| Low bandwidth | AV1 or H.265 | CRF 30 |
| Fast encoding | H.264 NVENC | preset fast |
| Maximum compatibility | H.264 | profile main, level 4.0 |
Platform Compatibility
| Platform | Recommended | Supported |
|---|---|---|
| Web browsers | H.264 | H.264, VP9, AV1 |
| Mobile devices | H.264 | H.264, H.265 |
| Smart TVs | H.264 | H.264, H.265 |
| YouTube | VP9, H.264 | All |
| Social media | H.264 | H.264 |
Best Practices
1. Use CRF for most tasks - Better than bitrate for variable content 2. Start with CRF 23 - Good balance, adjust based on results 3. Use slow preset - For archival and final delivery 4. Use fast preset - For previews and testing 5. Hardware acceleration - When speed is critical 6. Two-pass encoding - When file size is fixed 7. Match source frame rate - Don't increase FPS 8. Don't upscale resolution - Keep original or downscale 9. Test on short clips - Verify settings before full encode 10. Keep source files - Original quality for re-encoding
Troubleshooting
Poor Quality Output
# Lower CRF value
ffmpeg -i input.mp4 -c:v libx264 -crf 18 -preset slow output.mp4
# Use slower preset
ffmpeg -i input.mp4 -c:v libx264 -crf 22 -preset veryslow output.mp4
# Increase bitrate (two-pass)
ffmpeg -y -i input.mp4 -c:v libx264 -b:v 5M -pass 1 -an -f null /dev/null
ffmpeg -i input.mp4 -c:v libx264 -b:v 5M -pass 2 -c:a aac output.mp4Slow Encoding
# Use faster preset
ffmpeg -i input.mp4 -c:v libx264 -preset ultrafast output.mp4
# Use hardware acceleration
ffmpeg -hwaccel cuda -i input.mp4 -c:v h264_nvenc output.mp4
# Reduce resolution
ffmpeg -i input.mp4 -vf scale=1280:-1 -c:v libx264 output.mp4Large File Size
# Increase CRF
ffmpeg -i input.mp4 -c:v libx264 -crf 26 output.mp4
# Use better codec
ffmpeg -i input.mp4 -c:v libx265 -crf 26 output.mp4
# Two-pass with target bitrate
ffmpeg -y -i input.mp4 -c:v libx264 -b:v 1M -pass 1 -an -f null /dev/null
ffmpeg -i input.mp4 -c:v libx264 -b:v 1M -pass 2 -c:a aac output.mp4FFmpeg Filters & Effects
Complete guide to video and audio filters, complex filtergraphs, and effect chains.
Filter Basics
Filter Syntax
Filters are applied with -vf (video) or -af (audio).
# Single filter
ffmpeg -i input.mp4 -vf scale=1280:720 output.mp4
# Chain filters with comma
ffmpeg -i input.mp4 -vf "scale=1280:720,hqdn3d" output.mp4
# Complex filtergraph with -filter_complex
ffmpeg -i input.mp4 -i logo.png \
-filter_complex "[0:v][1:v]overlay=10:10" \
output.mp4Video Filters
Scale (Resize)
Change video dimensions.
# Specific dimensions
ffmpeg -i input.mp4 -vf scale=1280:720 output.mp4
# Maintain aspect ratio (auto height)
ffmpeg -i input.mp4 -vf scale=1280:-1 output.mp4
# Maintain aspect ratio (auto width)
ffmpeg -i input.mp4 -vf scale=-1:720 output.mp4
# Scale to half
ffmpeg -i input.mp4 -vf scale=iw/2:ih/2 output.mp4
# Scale with algorithm
ffmpeg -i input.mp4 -vf scale=1280:-1:flags=lanczos output.mp4Scaling algorithms:
bilinear- Fast, defaultbicubic- Better qualitylanczos- Best quality, slower
Crop
Extract portion of video.
# Crop width:height:x:y
ffmpeg -i input.mp4 -vf crop=1280:720:0:0 output.mp4
# Crop from center
ffmpeg -i input.mp4 -vf crop=1280:720:(iw-1280)/2:(ih-720)/2 output.mp4
# Auto-detect black borders
ffmpeg -i input.mp4 -vf cropdetect -f null -
# Apply detected crop
ffmpeg -i input.mp4 -vf crop=1920:800:0:140 output.mp4Rotate & Flip
Change video orientation.
# Rotate 90° clockwise
ffmpeg -i input.mp4 -vf transpose=1 output.mp4
# Rotate 90° counter-clockwise
ffmpeg -i input.mp4 -vf transpose=2 output.mp4
# Rotate 180°
ffmpeg -i input.mp4 -vf transpose=1,transpose=1 output.mp4
# Flip horizontal
ffmpeg -i input.mp4 -vf hflip output.mp4
# Flip vertical
ffmpeg -i input.mp4 -vf vflip output.mp4
# Rotate arbitrary angle
ffmpeg -i input.mp4 -vf rotate=45*PI/180 output.mp4Overlay (Watermark)
Composite images over video.
# Top-left corner
ffmpeg -i video.mp4 -i logo.png \
-filter_complex overlay=10:10 output.mp4
# Top-right corner
ffmpeg -i video.mp4 -i logo.png \
-filter_complex "overlay=W-w-10:10" output.mp4
# Bottom-right corner
ffmpeg -i video.mp4 -i logo.png \
-filter_complex "overlay=W-w-10:H-h-10" output.mp4
# Center
ffmpeg -i video.mp4 -i logo.png \
-filter_complex "overlay=(W-w)/2:(H-h)/2" output.mp4
# With transparency
ffmpeg -i video.mp4 -i logo.png \
-filter_complex "[1:v]format=rgba,colorchannelmixer=aa=0.5[logo];[0:v][logo]overlay=10:10" \
output.mp4Denoise
Reduce video noise.
# High-quality denoise (hqdn3d)
ffmpeg -i input.mp4 -vf hqdn3d output.mp4
# Stronger denoise
ffmpeg -i input.mp4 -vf hqdn3d=4:3:6:4.5 output.mp4
# Temporal denoise (nlmeans - slow but best)
ffmpeg -i input.mp4 -vf nlmeans output.mp4
# Fast denoise
ffmpeg -i input.mp4 -vf dctdnoiz output.mp4Deinterlace
Remove interlacing artifacts.
# YADIF (fast, good quality)
ffmpeg -i input.mp4 -vf yadif output.mp4
# YADIF with frame doubling
ffmpeg -i input.mp4 -vf yadif=1 output.mp4
# Bwdif (better quality)
ffmpeg -i input.mp4 -vf bwdif output.mp4Speed & Slow Motion
Change playback speed.
# 2x speed (video + audio)
ffmpeg -i input.mp4 -vf setpts=0.5*PTS -af atempo=2.0 output.mp4
# 0.5x speed (slow motion)
ffmpeg -i input.mp4 -vf setpts=2.0*PTS -af atempo=0.5 output.mp4
# 4x speed (chain atempo)
ffmpeg -i input.mp4 -vf setpts=0.25*PTS -af atempo=2.0,atempo=2.0 output.mp4Pad (Add Borders)
Add borders or letterbox.
# Add black borders to make 16:9
ffmpeg -i input.mp4 -vf "pad=1920:1080:(ow-iw)/2:(oh-ih)/2" output.mp4
# Add colored borders
ffmpeg -i input.mp4 -vf "pad=1920:1080:(ow-iw)/2:(oh-ih)/2:color=white" output.mp4
# Letterbox for Instagram (1:1)
ffmpeg -i input.mp4 -vf "scale=1080:-1,pad=1080:1080:(ow-iw)/2:(oh-ih)/2:color=black" output.mp4Sharpen & Blur
Adjust image sharpness.
# Sharpen (unsharp mask)
ffmpeg -i input.mp4 -vf unsharp=5:5:1.0 output.mp4
# Stronger sharpen
ffmpeg -i input.mp4 -vf unsharp=7:7:2.5 output.mp4
# Gaussian blur
ffmpeg -i input.mp4 -vf gblur=sigma=8 output.mp4
# Box blur
ffmpeg -i input.mp4 -vf boxblur=5:1 output.mp4Color Adjustments
Modify colors and exposure.
# Brightness (+/- 1.0)
ffmpeg -i input.mp4 -vf eq=brightness=0.1 output.mp4
# Contrast (+/- 2.0)
ffmpeg -i input.mp4 -vf eq=contrast=1.2 output.mp4
# Saturation (0-3)
ffmpeg -i input.mp4 -vf eq=saturation=1.5 output.mp4
# Gamma (0.1-10)
ffmpeg -i input.mp4 -vf eq=gamma=1.2 output.mp4
# Combined adjustments
ffmpeg -i input.mp4 -vf eq=brightness=0.05:contrast=1.1:saturation=1.2 output.mp4
# Curves (color grading)
ffmpeg -i input.mp4 -vf curves=vintage output.mp4
# Hue shift
ffmpeg -i input.mp4 -vf hue=h=90 output.mp4Grayscale & Effects
Convert to monochrome or apply effects.
# Grayscale
ffmpeg -i input.mp4 -vf hue=s=0 output.mp4
# Sepia tone
ffmpeg -i input.mp4 -vf colorchannelmixer=.393:.769:.189:0:.349:.686:.168:0:.272:.534:.131 output.mp4
# Negative
ffmpeg -i input.mp4 -vf negate output.mp4
# Edge detection
ffmpeg -i input.mp4 -vf edgedetect output.mp4
# Vignette
ffmpeg -i input.mp4 -vf vignette output.mp4Fade In/Out
Smooth transitions.
# Fade in from black (2 seconds)
ffmpeg -i input.mp4 -vf fade=in:0:60 output.mp4
# Fade out to black (last 2 seconds)
ffmpeg -i input.mp4 -vf fade=out:st=28:d=2 output.mp4
# Both fade in and out
ffmpeg -i input.mp4 -vf "fade=in:0:30,fade=out:st=28:d=2" output.mp4Stabilization
Reduce camera shake.
# Two-pass stabilization
# Pass 1: detect motion
ffmpeg -i input.mp4 -vf vidstabdetect=shakiness=10:accuracy=15 -f null -
# Pass 2: stabilize
ffmpeg -i input.mp4 -vf vidstabtransform=smoothing=30:input="transforms.trf" output.mp4Text Overlay
Add text to video.
# Simple text
ffmpeg -i input.mp4 -vf "drawtext=text='Hello World':fontsize=24:x=10:y=10" output.mp4
# With styling
ffmpeg -i input.mp4 -vf "drawtext=text='Title':fontsize=48:fontcolor=white:x=(w-text_w)/2:y=50:box=1:boxcolor=black@0.5:boxborderw=5" output.mp4
# Timestamp
ffmpeg -i input.mp4 -vf "drawtext=text='%{pts\:hms}':fontsize=20:x=10:y=10:fontcolor=white" output.mp4Audio Filters
Volume
Adjust audio level.
# Increase by 10dB
ffmpeg -i input.mp4 -af volume=10dB output.mp4
# Decrease to 50%
ffmpeg -i input.mp4 -af volume=0.5 output.mp4
# Double volume
ffmpeg -i input.mp4 -af volume=2.0 output.mp4Normalize
Balance audio levels.
# Loudness normalization (EBU R128)
ffmpeg -i input.mp4 -af loudnorm output.mp4
# With specific target
ffmpeg -i input.mp4 -af loudnorm=I=-16:TP=-1.5:LRA=11 output.mp4
# Two-pass normalization (better quality)
# Pass 1: analyze
ffmpeg -i input.mp4 -af loudnorm=print_format=json -f null -
# Pass 2: normalize with measured values
ffmpeg -i input.mp4 -af loudnorm=measured_I=-23:measured_LRA=7:measured_TP=-2:measured_thresh=-33 output.mp4Equalizer
Adjust frequency bands.
# Bass boost
ffmpeg -i input.mp4 -af equalizer=f=100:width_type=h:width=200:g=10 output.mp4
# Treble boost
ffmpeg -i input.mp4 -af equalizer=f=10000:width_type=h:width=2000:g=5 output.mp4
# Multiple bands
ffmpeg -i input.mp4 -af "equalizer=f=100:g=5,equalizer=f=1000:g=-3" output.mp4Compressor
Dynamic range compression.
# Basic compression
ffmpeg -i input.mp4 -af acompressor output.mp4
# Custom settings
ffmpeg -i input.mp4 -af acompressor=threshold=-20dB:ratio=4:attack=200:release=1000 output.mp4Noise Reduction
Remove background noise.
# High-pass filter (remove low frequency noise)
ffmpeg -i input.mp4 -af highpass=f=200 output.mp4
# Low-pass filter (remove high frequency noise)
ffmpeg -i input.mp4 -af lowpass=f=3000 output.mp4
# Band-pass filter
ffmpeg -i input.mp4 -af "highpass=f=200,lowpass=f=3000" output.mp4Fade Audio
Smooth audio transitions.
# Fade in (2 seconds)
ffmpeg -i input.mp4 -af afade=t=in:st=0:d=2 output.mp4
# Fade out (last 3 seconds)
ffmpeg -i input.mp4 -af afade=t=out:st=27:d=3 output.mp4
# Both
ffmpeg -i input.mp4 -af "afade=t=in:st=0:d=2,afade=t=out:st=27:d=3" output.mp4Audio Mixing
Combine multiple audio tracks.
# Mix two audio files
ffmpeg -i audio1.mp3 -i audio2.mp3 \
-filter_complex amix=inputs=2:duration=longest output.mp3
# Mix with volume adjustment
ffmpeg -i audio1.mp3 -i audio2.mp3 \
-filter_complex "[0:a]volume=0.8[a1];[1:a]volume=0.5[a2];[a1][a2]amix=inputs=2" \
output.mp3Complex Filtergraphs
Multiple Outputs
Create multiple versions simultaneously.
# Generate 3 resolutions at once
ffmpeg -i input.mp4 \
-filter_complex "[0:v]split=3[v1][v2][v3]; \
[v1]scale=1920:1080[out1]; \
[v2]scale=1280:720[out2]; \
[v3]scale=640:360[out3]" \
-map "[out1]" -c:v libx264 -crf 22 output_1080p.mp4 \
-map "[out2]" -c:v libx264 -crf 23 output_720p.mp4 \
-map "[out3]" -c:v libx264 -crf 24 output_360p.mp4 \
-map 0:a -c:a copyPicture-in-Picture
Overlay small video on main video.
ffmpeg -i main.mp4 -i small.mp4 \
-filter_complex "[1:v]scale=320:180[pip]; \
[0:v][pip]overlay=W-w-10:H-h-10" \
output.mp4Side-by-Side Comparison
Compare two videos.
# Horizontal
ffmpeg -i left.mp4 -i right.mp4 \
-filter_complex "[0:v][1:v]hstack=inputs=2" \
output.mp4
# Vertical
ffmpeg -i top.mp4 -i bottom.mp4 \
-filter_complex "[0:v][1:v]vstack=inputs=2" \
output.mp4Crossfade Transition
Smooth transition between videos.
ffmpeg -i video1.mp4 -i video2.mp4 \
-filter_complex "[0:v][1:v]xfade=transition=fade:duration=2:offset=8" \
output.mp4Transition types: fade, wipeleft, wiperight, wipeup, wipedown, slideleft, slideright, slideup, slidedown, circlecrop, rectcrop, distance, fadeblack, fadewhite, radial, smoothleft, smoothright, smoothup, smoothdown
Color Correction Pipeline
Professional color grading.
ffmpeg -i input.mp4 \
-filter_complex "[0:v]eq=contrast=1.1:brightness=0.05:saturation=1.2[v1]; \
[v1]curves=vintage[v2]; \
[v2]vignette[v3]; \
[v3]unsharp=5:5:1.0[out]" \
-map "[out]" -c:v libx264 -crf 18 output.mp4Filter Performance
GPU Acceleration
Use hardware filters when available.
# NVIDIA CUDA scale
ffmpeg -hwaccel cuda -i input.mp4 \
-vf scale_cuda=1280:720 \
-c:v h264_nvenc output.mp4
# Multiple GPU filters
ffmpeg -hwaccel cuda -i input.mp4 \
-vf "scale_cuda=1280:720,hwdownload,format=nv12" \
-c:v h264_nvenc output.mp4Optimize Filter Order
More efficient filter chains.
# Bad: scale after complex operations
ffmpeg -i input.mp4 -vf "hqdn3d,unsharp=5:5:1.0,scale=1280:720" output.mp4
# Good: scale first (fewer pixels to process)
ffmpeg -i input.mp4 -vf "scale=1280:720,hqdn3d,unsharp=5:5:1.0" output.mp4Common Filter Recipes
YouTube Optimized
ffmpeg -i input.mp4 \
-vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2" \
-c:v libx264 -preset slow -crf 18 -c:a aac -b:a 192k \
output.mp4Instagram Portrait
ffmpeg -i input.mp4 \
-vf "scale=1080:1350:force_original_aspect_ratio=decrease,pad=1080:1350:(ow-iw)/2:(oh-ih)/2:color=white" \
-c:v libx264 -preset fast -crf 23 -c:a aac \
output.mp4Vintage Film Look
ffmpeg -i input.mp4 \
-vf "curves=vintage,vignette=angle=PI/4,eq=saturation=0.8,noise=alls=10:allf=t" \
-c:v libx264 -crf 20 output.mp4Clean & Enhance
ffmpeg -i input.mp4 \
-vf "hqdn3d=4:3:6:4.5,unsharp=5:5:1.0,eq=contrast=1.05:saturation=1.1" \
-c:v libx264 -crf 20 output.mp4FFmpeg Streaming & Live Video
Complete guide to HLS/DASH streaming, live streaming platforms, and adaptive bitrate encoding.
HLS (HTTP Live Streaming)
Basic HLS Stream
Generate playlist for on-demand streaming.
# Simple HLS with default settings
ffmpeg -i input.mp4 \
-c:v libx264 -c:a aac \
-f hls -hls_time 6 -hls_playlist_type vod \
-hls_segment_filename "segment_%03d.ts" \
playlist.m3u8Key parameters:
-hls_time- Segment duration (seconds, default 2)-hls_playlist_type-vod(on-demand) orevent(live)-hls_segment_filename- Naming pattern for segments
Optimized HLS
Better quality and compatibility.
ffmpeg -i input.mp4 \
-c:v libx264 -preset fast -crf 22 \
-g 48 -sc_threshold 0 \
-c:a aac -b:a 128k \
-f hls -hls_time 6 -hls_playlist_type vod \
-hls_segment_filename "segment_%03d.ts" \
playlist.m3u8Parameters explained:
-g 48- Keyframe every 48 frames (2s @ 24fps)-sc_threshold 0- Disable scene detection (consistent segments)
Multi-Bitrate HLS (Adaptive)
Create multiple quality levels for adaptive streaming.
ffmpeg -i input.mp4 \
-map 0:v -map 0:a -map 0:v -map 0:a -map 0:v -map 0:a \
-c:v libx264 -crf 22 -c:a aac -b:a 128k \
-b:v:0 800k -s:v:0 640x360 -maxrate:v:0 856k -bufsize:v:0 1200k \
-b:v:1 1400k -s:v:1 842x480 -maxrate:v:1 1498k -bufsize:v:1 2100k \
-b:v:2 2800k -s:v:2 1280x720 -maxrate:v:2 2996k -bufsize:v:2 4200k \
-var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2" \
-master_pl_name master.m3u8 \
-f hls -hls_time 6 -hls_list_size 0 \
-hls_segment_filename "stream_%v/segment_%03d.ts" \
stream_%v/playlist.m3u8Creates:
master.m3u8- Master playlist (entry point)stream_0/playlist.m3u8- 360p streamstream_1/playlist.m3u8- 480p streamstream_2/playlist.m3u8- 720p stream
HLS with Encryption
Protect content with AES-128 encryption.
# Generate encryption key
openssl rand 16 > enc.key
echo "enc.key" > enc.keyinfo
echo "enc.key" >> enc.keyinfo
openssl rand -hex 16 >> enc.keyinfo
# Encode with encryption
ffmpeg -i input.mp4 \
-c:v libx264 -c:a aac \
-f hls -hls_time 6 \
-hls_key_info_file enc.keyinfo \
-hls_segment_filename "segment_%03d.ts" \
playlist.m3u8DASH (Dynamic Adaptive Streaming)
Basic DASH
MPEG-DASH format for adaptive streaming.
ffmpeg -i input.mp4 \
-c:v libx264 -c:a aac \
-f dash -seg_duration 6 \
-use_template 1 -use_timeline 1 \
manifest.mpdMulti-Bitrate DASH
Multiple quality levels.
ffmpeg -i input.mp4 \
-map 0:v -map 0:a -map 0:v -map 0:a \
-c:v libx264 -c:a aac \
-b:v:0 800k -s:v:0 640x360 \
-b:v:1 1400k -s:v:1 1280x720 \
-b:a:0 128k -b:a:1 128k \
-f dash -seg_duration 6 \
-use_template 1 -use_timeline 1 \
manifest.mpdRTMP Live Streaming
Stream to Twitch
ffmpeg -re -i input.mp4 \
-c:v libx264 -preset veryfast -maxrate 3000k -bufsize 6000k \
-pix_fmt yuv420p -g 50 -c:a aac -b:a 128k -ar 44100 \
-f flv rtmp://live.twitch.tv/app/STREAM_KEYStream to YouTube
ffmpeg -re -i input.mp4 \
-c:v libx264 -preset veryfast -maxrate 2500k -bufsize 5000k \
-pix_fmt yuv420p -g 60 -c:a aac -b:a 128k \
-f flv rtmp://a.rtmp.youtube.com/live2/STREAM_KEYStream to Facebook
ffmpeg -re -i input.mp4 \
-c:v libx264 -preset veryfast -maxrate 4000k -bufsize 8000k \
-pix_fmt yuv420p -g 60 -c:a aac -b:a 128k \
-f flv rtmps://live-api-s.facebook.com:443/rtmp/STREAM_KEYCustom RTMP Server
ffmpeg -re -i input.mp4 \
-c:v libx264 -preset veryfast -tune zerolatency \
-maxrate 2500k -bufsize 5000k \
-pix_fmt yuv420p -g 50 \
-c:a aac -b:a 128k -ar 44100 \
-f flv rtmp://your-server.com/live/stream-keyScreen Capture + Stream
Linux (X11)
ffmpeg -f x11grab -s 1920x1080 -framerate 30 -i :0.0 \
-f pulse -ac 2 -i default \
-c:v libx264 -preset veryfast -tune zerolatency \
-maxrate 2500k -bufsize 5000k -pix_fmt yuv420p \
-c:a aac -b:a 128k -ar 44100 \
-f flv rtmp://live.twitch.tv/app/STREAM_KEYmacOS (AVFoundation)
# List devices
ffmpeg -f avfoundation -list_devices true -i ""
# Capture and stream
ffmpeg -f avfoundation -framerate 30 -i "1:0" \
-c:v libx264 -preset veryfast -tune zerolatency \
-maxrate 2500k -bufsize 5000k -pix_fmt yuv420p \
-c:a aac -b:a 128k \
-f flv rtmp://live.twitch.tv/app/STREAM_KEYWindows (DirectShow)
ffmpeg -f dshow -i video="screen-capture-recorder":audio="Stereo Mix" \
-c:v libx264 -preset ultrafast -tune zerolatency \
-maxrate 750k -bufsize 3000k \
-f flv rtmp://live.twitch.tv/app/STREAM_KEYThumbnail Generation
Single Thumbnail
Extract frame at specific time.
# At 5 seconds
ffmpeg -ss 00:00:05 -i input.mp4 -vframes 1 -vf scale=320:-1 thumb.jpg
# At 10% duration
ffmpeg -ss $(ffprobe -v error -show_entries format=duration \
-of default=noprint_wrappers=1:nokey=1 input.mp4 | \
awk '{print $1*0.1}') -i input.mp4 -vframes 1 thumb.jpgMultiple Thumbnails
Generate thumbnails at intervals.
# One per minute
ffmpeg -i input.mp4 -vf fps=1/60,scale=320:-1 thumb_%03d.jpg
# One per 10 seconds
ffmpeg -i input.mp4 -vf fps=1/10,scale=320:-1 thumb_%03d.jpg
# First 10 frames
ffmpeg -i input.mp4 -vframes 10 -vf scale=320:-1 thumb_%02d.jpgThumbnail Sprite Sheet
Create single image with multiple thumbnails.
# Generate frames
ffmpeg -i input.mp4 -vf fps=1/10,scale=160:90 frames/thumb_%03d.jpg
# Combine into sprite (requires ImageMagick)
montage frames/thumb_*.jpg -tile 5x -geometry +0+0 sprite.jpgPreview Generation
Video Preview (Trailer)
Extract multiple short clips.
# Extract 3 segments
ffmpeg -i input.mp4 \
-ss 00:00:30 -t 00:00:10 -c copy segment1.mp4
ffmpeg -i input.mp4 \
-ss 00:05:00 -t 00:00:10 -c copy segment2.mp4
ffmpeg -i input.mp4 \
-ss 00:10:00 -t 00:00:10 -c copy segment3.mp4
# Concatenate segments
echo "file 'segment1.mp4'" > concat.txt
echo "file 'segment2.mp4'" >> concat.txt
echo "file 'segment3.mp4'" >> concat.txt
ffmpeg -f concat -safe 0 -i concat.txt -c copy preview.mp4Fast Preview (Low Quality)
Quick preview for review.
ffmpeg -i input.mp4 \
-vf scale=640:-1 \
-c:v libx264 -preset ultrafast -crf 28 \
-c:a aac -b:a 64k \
preview.mp4Streaming Parameters
Important RTMP Parameters
Real-time reading:
-re- Read input at native frame rate
Low latency:
-tune zerolatency- Optimize for minimal latency-preset ultrafastorveryfast- Fast encoding
Keyframes:
-g 50- Keyframe interval (GOP size)- Recommended: 2 seconds (fps * 2)
Rate control:
-maxrate- Maximum bitrate (e.g., 3000k)-bufsize- Buffer size (typically 2x maxrate)
Compatibility:
-pix_fmt yuv420p- Compatible pixel format
Bitrate Recommendations
1080p 60fps:
- 4500-6000 kbps video
- 160 kbps audio
1080p 30fps:
- 3000-4500 kbps video
- 128 kbps audio
720p 60fps:
- 2500-4000 kbps video
- 128 kbps audio
720p 30fps:
- 1500-2500 kbps video
- 128 kbps audio
480p:
- 500-1000 kbps video
- 128 kbps audio
UDP/RTP Streaming
UDP Stream
Simple network streaming.
# Sender
ffmpeg -re -i input.mp4 -c copy -f mpegts udp://192.168.1.100:1234
# Receiver
ffplay udp://192.168.1.100:1234RTP Stream
Real-Time Protocol for low latency.
# Audio only
ffmpeg -re -i audio.mp3 -c:a libopus -f rtp rtp://192.168.1.100:5004
# Video + audio
ffmpeg -re -i input.mp4 \
-c:v libx264 -preset ultrafast \
-c:a aac -f rtp rtp://192.168.1.100:5004Multicast Stream
Stream to multiple receivers.
# Sender (multicast address)
ffmpeg -re -i input.mp4 -c copy -f mpegts udp://239.255.0.1:1234
# Receiver
ffplay udp://239.255.0.1:1234Advanced Streaming
Hardware-Accelerated Streaming
Use GPU for faster encoding.
# NVIDIA NVENC
ffmpeg -re -i input.mp4 \
-c:v h264_nvenc -preset fast -maxrate 3000k -bufsize 6000k \
-c:a aac -b:a 128k \
-f flv rtmp://live.twitch.tv/app/STREAM_KEY
# Intel QSV
ffmpeg -re -hwaccel qsv -i input.mp4 \
-c:v h264_qsv -preset fast -maxrate 3000k -bufsize 6000k \
-c:a aac -b:a 128k \
-f flv rtmp://live.twitch.tv/app/STREAM_KEYStream with Overlay
Add graphics during stream.
ffmpeg -re -i input.mp4 -i logo.png \
-filter_complex "[0:v][1:v]overlay=10:10" \
-c:v libx264 -preset veryfast -maxrate 3000k \
-c:a copy \
-f flv rtmp://live.twitch.tv/app/STREAM_KEYLoop Stream
Continuously loop video for 24/7 stream.
ffmpeg -stream_loop -1 -re -i input.mp4 \
-c:v libx264 -preset veryfast -maxrate 2500k \
-c:a aac -b:a 128k \
-f flv rtmp://live.twitch.tv/app/STREAM_KEYTroubleshooting
Buffering Issues
# Reduce buffer size
ffmpeg -re -i input.mp4 -maxrate 2000k -bufsize 2000k -c:v libx264 -f flv rtmp://...
# Use faster preset
ffmpeg -re -i input.mp4 -preset ultrafast -c:v libx264 -f flv rtmp://...Audio/Video Desync
# Force constant frame rate
ffmpeg -re -i input.mp4 -r 30 -c:v libx264 -f flv rtmp://...
# Use -vsync 1
ffmpeg -re -i input.mp4 -vsync 1 -c:v libx264 -f flv rtmp://...Connection Drops
# Increase timeout
ffmpeg -timeout 5000000 -re -i input.mp4 -c:v libx264 -f flv rtmp://...
# Reconnect on failure (use wrapper script)
while true; do
ffmpeg -re -i input.mp4 -c:v libx264 -f flv rtmp://...
sleep 5
doneFormat Compatibility & Conversion Guide
Complete guide to media format support, codec recommendations, and conversion best practices.
Image Format Support
ImageMagick Formats
Raster Formats (Full Support):
- JPEG (.jpg, .jpeg) - Lossy, universal
- PNG (.png) - Lossless, transparency
- WebP (.webp) - Modern, lossy/lossless
- GIF (.gif) - Animation, limited colors
- TIFF (.tif, .tiff) - Professional, lossless
- BMP (.bmp) - Uncompressed, legacy
- ICO (.ico) - Icons, multi-size
Raw Formats (Read Support):
- CR2, NEF, ARW, DNG (Canon, Nikon, Sony, Adobe RAW)
- Requires dcraw or ufraw-batch
Vector Formats (Limited):
- SVG (.svg) - Read only, converts to raster
- PDF (.pdf) - Read/write, may have policy restrictions
Other Formats:
- HEIC (.heic) - Apple format, requires libheif
- AVIF (.avif) - Next-gen, requires libavif
- PSD (.psd) - Photoshop, basic support
FFmpeg Image Support
Input Formats:
- JPEG, PNG, BMP, TIFF, WebP, GIF
- Image sequences (frame_%04d.png)
Output Formats:
- JPEG, PNG, BMP, TIFF
- Video from images
Video Format Support
Container Formats
Universal Containers:
- MP4 (.mp4) - Most compatible, streaming
- MKV (.mkv) - Feature-rich, flexible
- WebM (.webm) - Web-optimized, open
- AVI (.avi) - Legacy, broad support
- MOV (.mov) - Apple, professional
Streaming Containers:
- TS (.ts) - Transport stream, HLS segments
- M3U8 (.m3u8) - HLS playlist
- MPD (.mpd) - DASH manifest
- FLV (.flv) - Flash (legacy)
Professional Formats:
- ProRes (.mov) - Apple professional
- DNxHD/DNxHR (.mxf, .mov) - Avid professional
- MXF (.mxf) - Broadcast
Video Codecs
Modern Codecs:
- H.264/AVC (libx264) - Universal, excellent balance
- H.265/HEVC (libx265) - Better compression, 4K
- VP9 (libvpx-vp9) - Open, YouTube
- AV1 (libaom-av1, libsvtav1) - Next-gen, best compression
Legacy Codecs:
- MPEG-4 (mpeg4) - Older devices
- MPEG-2 (mpeg2video) - DVD, broadcast
- VP8 (libvpx) - WebM predecessor
Professional Codecs:
- ProRes (prores) - Apple post-production
- DNxHD (dnxhd) - Avid editing
- Uncompressed (rawvideo) - Maximum quality
Audio Codecs
Modern Codecs:
- AAC (aac) - Universal, streaming
- Opus (libopus) - Best low-bitrate
- MP3 (libmp3lame) - Universal compatibility
Lossless Codecs:
- FLAC (flac) - Open, archival
- ALAC (alac) - Apple lossless
- WAV (pcm_s16le) - Uncompressed
Other Codecs:
- Vorbis (libvorbis) - Open, WebM
- AC-3 (ac3) - Dolby Digital, surround
- DTS (dts) - Cinema surround
Format Recommendations
Use Case Matrix
| Use Case | Image Format | Video Container | Video Codec | Audio Codec |
|---|---|---|---|---|
| Web general | JPEG 85% | MP4 | H.264 | AAC 128k |
| Web transparency | PNG | - | - | - |
| Web modern | WebP | WebM | VP9 | Opus |
| Social media | JPEG 85% | MP4 | H.264 | AAC 128k |
| 4K streaming | - | MP4 | H.265 | AAC 192k |
| Archive | PNG/TIFF | MKV | H.265 CRF 18 | FLAC |
| JPEG 75% | - | - | - | |
| TIFF/PNG | - | - | - | |
| YouTube | - | MP4/WebM | H.264/VP9 | AAC/Opus |
| Live stream | - | FLV | H.264 | AAC |
| Editing | - | MOV/MXF | ProRes/DNxHD | PCM |
Platform Compatibility
Web Browsers (2025):
- Images: JPEG, PNG, WebP, GIF, SVG
- Video: MP4 (H.264), WebM (VP9), MP4 (AV1)
- Audio: AAC, MP3, Opus, Vorbis
Mobile Devices:
- iOS: JPEG, PNG, HEIC, MP4 (H.264/H.265), AAC
- Android: JPEG, PNG, WebP, MP4 (H.264/H.265), AAC
Smart TVs:
- Most: MP4 (H.264), AAC
- Modern: MP4 (H.265), AC-3
Social Media:
- All platforms: JPEG, MP4 (H.264), AAC
Quality vs Size Trade-offs
Image Quality Comparison
JPEG Quality Levels:
- 95-100: ~5-10 MB (large image), minimal artifacts
- 85-94: ~1-3 MB, imperceptible loss
- 75-84: ~500 KB-1 MB, slight artifacts
- 60-74: ~200-500 KB, visible artifacts
- Below 60: <200 KB, poor quality
Format Comparison (Same quality):
- WebP: 25-35% smaller than JPEG
- HEIC: 40-50% smaller than JPEG
- AVIF: 50-60% smaller than JPEG
- PNG: 2-5x larger than JPEG (lossless)
Video Quality Comparison
H.264 CRF Values:
- CRF 18: Visually lossless, ~8-15 Mbps (1080p)
- CRF 23: High quality, ~4-8 Mbps (1080p)
- CRF 28: Medium quality, ~2-4 Mbps (1080p)
Codec Comparison (Same quality):
- H.265: 40-50% smaller than H.264
- VP9: 30-40% smaller than H.264
- AV1: 50-60% smaller than H.264
Audio Quality Comparison
AAC Bitrates:
- 320 kbps: Transparent, archival
- 192 kbps: High quality, music
- 128 kbps: Good quality, streaming
- 96 kbps: Acceptable, low bandwidth
- 64 kbps: Poor, voice only
Codec Efficiency (Same quality):
- Opus: Best at low bitrates (<128k)
- AAC: Best overall balance
- MP3: Less efficient but universal
Conversion Best Practices
Image Conversions
PNG to JPEG:
# Standard conversion
magick input.png -quality 85 -strip output.jpg
# With transparency handling
magick input.png -background white -flatten -quality 85 output.jpgJPEG to WebP:
# FFmpeg
ffmpeg -i input.jpg -quality 80 output.webp
# ImageMagick
magick input.jpg -quality 80 output.webpRAW to JPEG:
# Requires dcraw
magick input.CR2 -quality 90 output.jpgHEIC to JPEG:
# Requires libheif
magick input.heic -quality 85 output.jpgVideo Conversions
MKV to MP4:
# Copy streams (fast)
ffmpeg -i input.mkv -c copy output.mp4
# Re-encode if needed
ffmpeg -i input.mkv -c:v libx264 -crf 23 -c:a aac output.mp4AVI to MP4:
# Modern codecs
ffmpeg -i input.avi -c:v libx264 -crf 23 -c:a aac output.mp4MOV to MP4:
# Copy if H.264 already
ffmpeg -i input.mov -c copy output.mp4
# Convert ProRes to H.264
ffmpeg -i input.mov -c:v libx264 -crf 18 -c:a aac output.mp4Any to WebM:
# VP9 encoding
ffmpeg -i input.mp4 -c:v libvpx-vp9 -crf 30 -b:v 0 -c:a libopus output.webmAudio Conversions
Extract Audio from Video:
# Keep original codec
ffmpeg -i video.mp4 -vn -c:a copy audio.m4a
# Convert to MP3
ffmpeg -i video.mp4 -vn -q:a 0 audio.mp3
# Convert to FLAC (lossless)
ffmpeg -i video.mp4 -vn -c:a flac audio.flacAudio Format Conversion:
# WAV to MP3
ffmpeg -i input.wav -c:a libmp3lame -b:a 192k output.mp3
# MP3 to AAC
ffmpeg -i input.mp3 -c:a aac -b:a 192k output.m4a
# Any to Opus
ffmpeg -i input.wav -c:a libopus -b:a 128k output.opusCodec Selection Guide
Choose H.264 When:
- Maximum compatibility needed
- Targeting older devices
- Streaming to unknown devices
- Social media upload
- Fast encoding required
Choose H.265 When:
- 4K video encoding
- Storage space limited
- Modern device targets
- Archival quality needed
- Bandwidth constrained
Choose VP9 When:
- YouTube upload
- Open-source requirement
- Chrome/Firefox primary
- Royalty-free needed
Choose AV1 When:
- Future-proofing content
- Maximum compression needed
- Encoding time not critical
- Modern platform targets
Format Migration Strategies
Archive to Web
# High-res archive -> Web-optimized
for img in archive/*.tif; do
base=$(basename "$img" .tif)
magick "$img" -resize 2000x2000\> -quality 85 -strip "web/${base}.jpg"
magick "$img" -resize 2000x2000\> -quality 85 "web/${base}.webp"
doneLegacy to Modern
# Convert old formats to modern codecs
for video in legacy/*.avi; do
base=$(basename "$video" .avi)
ffmpeg -i "$video" \
-c:v libx264 -crf 23 -preset slow \
-c:a aac -b:a 128k \
"modern/${base}.mp4"
doneMulti-Format Publishing
# Create multiple formats for compatibility
input="source.mp4"
# Modern browsers
ffmpeg -i "$input" -c:v libx264 -crf 23 -c:a aac output.mp4
ffmpeg -i "$input" -c:v libvpx-vp9 -crf 30 -c:a libopus output.webm
# Images
ffmpeg -ss 5 -i "$input" -vframes 1 poster.jpg
magick poster.jpg -quality 80 poster.webpTroubleshooting
Unsupported Format
# Check FFmpeg formats
ffmpeg -formats
# Check ImageMagick formats
magick identify -list format
# Install missing codec support
sudo apt-get install libx264-dev libx265-dev libvpx-devCompatibility Issues
# Force compatible encoding
ffmpeg -i input.mp4 \
-c:v libx264 -profile:v high -level 4.0 \
-pix_fmt yuv420p \
-c:a aac -b:a 128k \
output.mp4Quality Loss
# Avoid multiple conversions
# Bad: source -> edit -> web -> social
# Good: source -> final (single conversion)
# Use lossless intermediate
ffmpeg -i source.mp4 -c:v ffv1 intermediate.mkv
# Edit intermediate
ffmpeg -i intermediate.mkv -c:v libx264 final.mp4ImageMagick Batch Processing
Complete guide to batch operations, mogrify command, parallel processing, and automation.
Mogrify Command
Basic Mogrify
Modify files in-place (overwrites originals).
# Resize all JPEGs
mogrify -resize 800x600 *.jpg
# Convert format (creates new files)
mogrify -format png *.jpg
# Apply effect to all images
mogrify -quality 85 -strip *.jpgWarning: mogrify modifies files in-place. Always backup originals or use -path to output to different directory.
Output to Different Directory
Preserve originals.
# Create output directory first
mkdir output
# Process to output directory
mogrify -path ./output -resize 800x600 *.jpg
# With format conversion
mogrify -path ./optimized -format webp -quality 80 *.pngCommon Batch Operations
Resize All Images
# Resize to width 800
mogrify -resize 800x *.jpg
# Resize to height 600
mogrify -resize x600 *.jpg
# Fit within 800×600
mogrify -resize 800x600 *.jpg
# Resize to exact dimensions
mogrify -resize 800x600! *.jpg
# Only shrink, never enlarge
mogrify -resize 800x600\> *.jpgFormat Conversion
# PNG to JPEG
mogrify -path ./jpg -format jpg -quality 85 *.png
# JPEG to WebP
mogrify -path ./webp -format webp -quality 80 *.jpg
# Any format to PNG
mogrify -path ./png -format png *.{jpg,gif,bmp}Optimize Images
# Strip metadata from all JPEGs
mogrify -strip *.jpg
# Optimize JPEGs for web
mogrify -quality 85 -strip -interlace Plane *.jpg
# Compress PNGs
mogrify -quality 95 *.png
# Combined optimization
mogrify -quality 85 -strip -interlace Plane -sampling-factor 4:2:0 *.jpgApply Effects
# Add watermark to all images
mogrify -gravity southeast -draw "image over 10,10 0,0 'watermark.png'" *.jpg
# Convert all to grayscale
mogrify -colorspace Gray *.jpg
# Apply sepia tone
mogrify -sepia-tone 80% *.jpg
# Sharpen all images
mogrify -sharpen 0x1 *.jpgThumbnail Generation
# Create square thumbnails
mogrify -path ./thumbnails -resize 200x200^ -gravity center -extent 200x200 *.jpg
# Create thumbnails with max dimension
mogrify -path ./thumbs -thumbnail 300x300 *.jpg
# Thumbnails with quality control
mogrify -path ./thumbs -thumbnail 200x200 -quality 80 -strip *.jpgShell Loops
Basic For Loop
More control than mogrify.
# Resize with custom naming
for img in *.jpg; do
magick "$img" -resize 800x600 "resized_$img"
done
# Process to subdirectory
mkdir processed
for img in *.jpg; do
magick "$img" -resize 1920x1080 "processed/$img"
doneMultiple Operations
# Complex processing pipeline
for img in *.jpg; do
magick "$img" \
-resize 1920x1080^ \
-gravity center \
-crop 1920x1080+0+0 +repage \
-unsharp 0x1 \
-quality 85 -strip \
"processed_$img"
doneFormat Conversion with Rename
# Convert PNG to JPEG with new names
for img in *.png; do
magick "$img" -quality 90 "${img%.png}.jpg"
done
# Add prefix during conversion
for img in *.jpg; do
magick "$img" -resize 800x "web_${img}"
doneConditional Processing
# Only process large images
for img in *.jpg; do
width=$(identify -format "%w" "$img")
if [ $width -gt 2000 ]; then
magick "$img" -resize 2000x "resized_$img"
fi
done
# Skip existing output files
for img in *.jpg; do
output="output_$img"
if [ ! -f "$output" ]; then
magick "$img" -resize 800x "$output"
fi
doneParallel Processing
GNU Parallel
Process multiple images simultaneously.
# Install GNU Parallel
# Ubuntu/Debian: sudo apt-get install parallel
# macOS: brew install parallel
# Basic parallel resize
parallel magick {} -resize 800x600 resized_{} ::: *.jpg
# Parallel with function
resize_image() {
magick "$1" -resize 1920x1080 -quality 85 "processed_$1"
}
export -f resize_image
parallel resize_image ::: *.jpg
# Limit concurrent jobs
parallel -j 4 magick {} -resize 800x {} ::: *.jpg
# Progress indicator
parallel --progress magick {} -resize 800x {} ::: *.jpgXargs Parallel
# Using xargs for parallel processing
ls *.jpg | xargs -I {} -P 4 magick {} -resize 800x processed_{}
# With find
find . -name "*.jpg" -print0 | \
xargs -0 -I {} -P 4 magick {} -resize 800x {}Advanced Batch Patterns
Recursive Processing
# Process all JPEGs in subdirectories
find . -name "*.jpg" -exec magick {} -resize 800x {} \;
# With output directory structure
find . -name "*.jpg" -type f | while read img; do
outdir="output/$(dirname "$img")"
mkdir -p "$outdir"
magick "$img" -resize 800x "$outdir/$(basename "$img")"
doneBatch with Different Sizes
# Generate multiple sizes
for size in 320 640 1024 1920; do
mkdir -p "output/${size}w"
for img in *.jpg; do
magick "$img" -resize ${size}x -quality 85 "output/${size}w/$img"
done
done
# Parallel version
for size in 320 640 1024 1920; do
mkdir -p "output/${size}w"
parallel magick {} -resize ${size}x -quality 85 "output/${size}w/{}" ::: *.jpg
doneResponsive Image Set
# Create responsive image set with srcset
mkdir -p responsive
for img in *.jpg; do
base="${img%.jpg}"
for width in 320 640 1024 1920; do
magick "$img" -resize ${width}x -quality 85 \
"responsive/${base}-${width}w.jpg"
done
doneWatermark Batch
# Add watermark to all images
for img in *.jpg; do
magick "$img" watermark.png \
-gravity southeast -geometry +10+10 \
-composite "watermarked_$img"
done
# Different watermark positions for portrait vs landscape
for img in *.jpg; do
width=$(identify -format "%w" "$img")
height=$(identify -format "%h" "$img")
if [ $width -gt $height ]; then
# Landscape
magick "$img" watermark.png -gravity southeast -composite "marked_$img"
else
# Portrait
magick "$img" watermark.png -gravity south -composite "marked_$img"
fi
doneError Handling
Check Before Processing
# Verify image before processing
for img in *.jpg; do
if identify "$img" > /dev/null 2>&1; then
magick "$img" -resize 800x "processed_$img"
else
echo "Skipping corrupt image: $img"
fi
doneLog Processing
# Log successful and failed operations
log_file="batch_process.log"
error_log="errors.log"
for img in *.jpg; do
if magick "$img" -resize 800x "output/$img" 2>> "$error_log"; then
echo "$(date): Processed $img" >> "$log_file"
else
echo "$(date): Failed $img" >> "$error_log"
fi
doneDry Run Mode
# Test without modifying files
dry_run=true
for img in *.jpg; do
cmd="magick $img -resize 800x processed_$img"
if [ "$dry_run" = true ]; then
echo "Would run: $cmd"
else
eval $cmd
fi
doneOptimization Workflows
Web Publishing Pipeline
# Complete web optimization workflow
mkdir -p web/{original,optimized,thumbnails}
# Copy originals
cp *.jpg web/original/
# Create optimized versions
mogrify -path web/optimized \
-resize 1920x1080\> \
-quality 85 \
-strip \
-interlace Plane \
web/original/*.jpg
# Create thumbnails
mogrify -path web/thumbnails \
-thumbnail 300x300 \
-quality 80 \
-strip \
web/original/*.jpgArchive to Web Conversion
# Convert high-res archives to web formats
for img in archives/*.jpg; do
base=$(basename "$img" .jpg)
# Full size web version
magick "$img" -resize 2048x2048\> -quality 90 -strip "web/${base}.jpg"
# Thumbnail
magick "$img" -thumbnail 400x400 -quality 85 "web/${base}_thumb.jpg"
# WebP version
magick "$img" -resize 2048x2048\> -quality 85 "web/${base}.webp"
donePrint to Web Workflow
# Convert print-ready images to web
for img in print/*.tif; do
base=$(basename "$img" .tif)
# Convert colorspace and optimize
magick "$img" \
-colorspace sRGB \
-resize 2000x2000\> \
-quality 90 \
-strip \
-interlace Plane \
"web/${base}.jpg"
doneBatch Reporting
Generate Report
# Create processing report
report="batch_report.txt"
echo "Batch Processing Report - $(date)" > "$report"
echo "================================" >> "$report"
total=0
success=0
failed=0
for img in *.jpg; do
((total++))
if magick "$img" -resize 800x "output/$img" 2>/dev/null; then
((success++))
echo "✓ $img" >> "$report"
else
((failed++))
echo "✗ $img" >> "$report"
fi
done
echo "" >> "$report"
echo "Total: $total, Success: $success, Failed: $failed" >> "$report"Image Inventory
# Create inventory of images
inventory="image_inventory.csv"
echo "Filename,Width,Height,Format,Size,ColorSpace" > "$inventory"
for img in *.{jpg,png,gif}; do
[ -f "$img" ] || continue
info=$(identify -format "%f,%w,%h,%m,%b,%[colorspace]" "$img")
echo "$info" >> "$inventory"
donePerformance Tips
Optimize Loop Performance
# Bad: Launch mogrify for each file
for img in *.jpg; do
mogrify -resize 800x "$img"
done
# Good: Process all files in one mogrify call
mogrify -resize 800x *.jpg
# Best: Use parallel processing for complex operations
parallel magick {} -resize 800x -quality 85 processed_{} ::: *.jpgMemory Management
# Limit memory for batch processing
for img in *.jpg; do
magick -limit memory 2GB -limit map 4GB \
"$img" -resize 50% "output/$img"
doneProgress Tracking
# Show progress for long batch operations
total=$(ls *.jpg | wc -l)
current=0
for img in *.jpg; do
((current++))
echo "Processing $current/$total: $img"
magick "$img" -resize 800x "output/$img"
doneAutomation Scripts
Complete Bash Script
#!/bin/bash
# Configuration
INPUT_DIR="./input"
OUTPUT_DIR="./output"
QUALITY=85
MAX_WIDTH=1920
THUMBNAIL_SIZE=300
# Create output directories
mkdir -p "$OUTPUT_DIR"/{full,thumbnails}
# Process images
echo "Processing images..."
for img in "$INPUT_DIR"/*.{jpg,jpeg,png}; do
[ -f "$img" ] || continue
filename=$(basename "$img")
base="${filename%.*}"
# Full size
magick "$img" \
-resize ${MAX_WIDTH}x\> \
-quality $QUALITY \
-strip \
"$OUTPUT_DIR/full/${base}.jpg"
# Thumbnail
magick "$img" \
-thumbnail ${THUMBNAIL_SIZE}x${THUMBNAIL_SIZE} \
-quality 80 \
-strip \
"$OUTPUT_DIR/thumbnails/${base}_thumb.jpg"
echo "✓ $filename"
done
echo "Done!"Python Batch Script
#!/usr/bin/env python3
import os
import subprocess
from pathlib import Path
INPUT_DIR = Path("./input")
OUTPUT_DIR = Path("./output")
SIZES = [320, 640, 1024, 1920]
# Create output directories
for size in SIZES:
(OUTPUT_DIR / f"{size}w").mkdir(parents=True, exist_ok=True)
# Process images
for img in INPUT_DIR.glob("*.jpg"):
for size in SIZES:
output = OUTPUT_DIR / f"{size}w" / img.name
subprocess.run([
"magick", str(img),
"-resize", f"{size}x",
"-quality", "85",
"-strip",
str(output)
])
print(f"✓ {img.name} -> {size}w")Common Batch Recipes
Social Media Sizes
# Generate social media image sizes
for img in *.jpg; do
base="${img%.jpg}"
# Instagram square (1080×1080)
magick "$img" -resize 1080x1080^ -gravity center -extent 1080x1080 "${base}_ig_square.jpg"
# Instagram portrait (1080×1350)
magick "$img" -resize 1080x1350^ -gravity center -extent 1080x1350 "${base}_ig_portrait.jpg"
# Facebook post (1200×630)
magick "$img" -resize 1200x630^ -gravity center -extent 1200x630 "${base}_fb_post.jpg"
# Twitter post (1200×675)
magick "$img" -resize 1200x675^ -gravity center -extent 1200x675 "${base}_tw_post.jpg"
doneEmail Newsletter Images
# Optimize images for email
mogrify -path ./email \
-resize 600x\> \
-quality 75 \
-strip \
-interlace Plane \
*.jpgBackup and Archive
# Create web versions and keep originals
mkdir -p {originals,web}
# Move originals
mv *.jpg originals/
# Create optimized copies
for img in originals/*.jpg; do
base=$(basename "$img")
magick "$img" -resize 2000x2000\> -quality 85 -strip "web/$base"
doneImageMagick Image Editing
Complete guide to format conversion, resizing, effects, transformations, and composition.
Format Conversion
Basic Conversion
Convert between image formats.
# PNG to JPEG
magick input.png output.jpg
# JPEG to WebP
magick input.jpg output.webp
# Multiple outputs simultaneously
magick input.png output.jpg output.webp output.gif
# Convert with quality setting
magick input.png -quality 85 output.jpgQuality Settings
JPEG Quality (0-100):
- 95-100: Archival, minimal compression
- 85-94: High quality, web publishing
- 75-84: Medium quality, web optimized
- 60-74: Lower quality, smaller files
- Below 60: Visible artifacts
# High quality
magick input.png -quality 95 output.jpg
# Web optimized (recommended)
magick input.png -quality 85 -strip output.jpg
# Smaller file size
magick input.png -quality 75 -sampling-factor 4:2:0 -strip output.jpgPNG Quality (0-9 = compression level):
# Maximum compression (slower)
magick input.jpg -quality 95 output.png
# Faster compression
magick input.jpg -quality 75 output.pngWebP Quality:
# Lossy with quality
magick input.jpg -quality 80 output.webp
# Lossless
magick input.png -define webp:lossless=true output.webpProgressive & Optimization
# Progressive JPEG (better web loading)
magick input.png -quality 85 -interlace Plane output.jpg
# Strip metadata (reduce file size)
magick input.jpg -strip output.jpg
# Combined optimization
magick input.png -quality 85 -interlace Plane -strip output.jpgResizing Operations
Basic Resize
Maintain aspect ratio.
# Fit within 800×600
magick input.jpg -resize 800x600 output.jpg
# Resize to specific width (auto height)
magick input.jpg -resize 800x output.jpg
# Resize to specific height (auto width)
magick input.jpg -resize x600 output.jpg
# Scale by percentage
magick input.jpg -resize 50% output.jpgAdvanced Resize
# Resize only if larger (shrink only)
magick input.jpg -resize 800x600\> output.jpg
# Resize only if smaller (enlarge only)
magick input.jpg -resize 800x600\< output.jpg
# Force exact dimensions (ignore aspect ratio)
magick input.jpg -resize 800x600! output.jpg
# Fill dimensions (may crop)
magick input.jpg -resize 800x600^ output.jpg
# Minimum dimensions
magick input.jpg -resize 800x600^ output.jpgResize Algorithms
# High quality (Lanczos)
magick input.jpg -filter Lanczos -resize 50% output.jpg
# Fast resize (Box)
magick input.jpg -filter Box -resize 50% output.jpg
# Mitchel filter (good balance)
magick input.jpg -filter Mitchell -resize 50% output.jpgFilter comparison:
Lanczos- Highest quality, slowerMitchell- Good quality, fastCatrom- Sharp, good for downscalingBox- Fastest, acceptable qualityCubic- Smooth results
Cropping
Basic Crop
Extract region from image.
# Crop width×height+x+y
magick input.jpg -crop 400x400+100+100 output.jpg
# Remove virtual canvas after crop
magick input.jpg -crop 400x400+100+100 +repage output.jpg
# Crop from center
magick input.jpg -gravity center -crop 400x400+0+0 output.jpg
# Crop to aspect ratio
magick input.jpg -gravity center -crop 16:9 +repage output.jpgSmart Crop
Content-aware cropping.
# Trim transparent/same-color borders
magick input.png -trim +repage output.png
# Trim with fuzz tolerance
magick input.jpg -fuzz 10% -trim +repage output.jpgThumbnail Generation
Create square thumbnails from any aspect ratio.
# Resize and crop to square
magick input.jpg -resize 200x200^ -gravity center -extent 200x200 thumb.jpg
# Alternative method
magick input.jpg -thumbnail 200x200^ -gravity center -crop 200x200+0+0 +repage thumb.jpg
# With background (no crop)
magick input.jpg -resize 200x200 -background white -gravity center -extent 200x200 thumb.jpgEffects & Filters
Blur Effects
# Standard blur (radius 0 = auto)
magick input.jpg -blur 0x8 output.jpg
# Gaussian blur (radius×sigma)
magick input.jpg -gaussian-blur 5x3 output.jpg
# Motion blur (angle)
magick input.jpg -motion-blur 0x20+45 output.jpg
# Radial blur
magick input.jpg -radial-blur 10 output.jpgSharpen
# Basic sharpen
magick input.jpg -sharpen 0x1 output.jpg
# Stronger sharpen
magick input.jpg -sharpen 0x3 output.jpg
# Unsharp mask (advanced)
magick input.jpg -unsharp 0x1 output.jpgColor Effects
# Grayscale
magick input.jpg -colorspace Gray output.jpg
# Sepia tone
magick input.jpg -sepia-tone 80% output.jpg
# Negate (invert colors)
magick input.jpg -negate output.jpg
# Posterize (reduce colors)
magick input.jpg -posterize 8 output.jpg
# Solarize
magick input.jpg -solarize 50% output.jpgArtistic Effects
# Edge detection
magick input.jpg -edge 3 output.jpg
# Emboss
magick input.jpg -emboss 2 output.jpg
# Oil painting
magick input.jpg -paint 4 output.jpg
# Charcoal drawing
magick input.jpg -charcoal 2 output.jpg
# Sketch
magick input.jpg -sketch 0x20+120 output.jpg
# Swirl
magick input.jpg -swirl 90 output.jpgAdjustments
Brightness & Contrast
# Increase brightness
magick input.jpg -brightness-contrast 10x0 output.jpg
# Increase contrast
magick input.jpg -brightness-contrast 0x20 output.jpg
# Both
magick input.jpg -brightness-contrast 10x20 output.jpg
# Negative values to decrease
magick input.jpg -brightness-contrast -10x-10 output.jpgColor Adjustments
# Adjust saturation (HSL modulation)
# Format: brightness,saturation,hue
magick input.jpg -modulate 100,150,100 output.jpg
# Adjust hue
magick input.jpg -modulate 100,100,120 output.jpg
# Combined adjustments
magick input.jpg -modulate 105,120,100 output.jpg
# Adjust specific color channels
magick input.jpg -channel Red -evaluate multiply 1.2 output.jpgAuto Corrections
# Auto level (normalize contrast)
magick input.jpg -auto-level output.jpg
# Auto gamma correction
magick input.jpg -auto-gamma output.jpg
# Normalize (stretch histogram)
magick input.jpg -normalize output.jpg
# Enhance (digital enhancement)
magick input.jpg -enhance output.jpg
# Equalize (histogram equalization)
magick input.jpg -equalize output.jpgTransformations
Rotation
# Rotate 90° clockwise
magick input.jpg -rotate 90 output.jpg
# Rotate 180°
magick input.jpg -rotate 180 output.jpg
# Rotate counter-clockwise
magick input.jpg -rotate -90 output.jpg
# Rotate with background
magick input.jpg -background white -rotate 45 output.jpg
# Auto-orient based on EXIF
magick input.jpg -auto-orient output.jpgFlip & Mirror
# Flip vertically
magick input.jpg -flip output.jpg
# Flip horizontally (mirror)
magick input.jpg -flop output.jpg
# Both
magick input.jpg -flip -flop output.jpgBorders & Frames
Simple Borders
# Add 10px black border
magick input.jpg -border 10x10 output.jpg
# Colored border
magick input.jpg -bordercolor red -border 10x10 output.jpg
# Different width/height
magick input.jpg -bordercolor blue -border 20x10 output.jpgAdvanced Frames
# Raised frame
magick input.jpg -mattecolor gray -frame 10x10+5+5 output.jpg
# Shadow effect
magick input.jpg \
\( +clone -background black -shadow 80x3+5+5 \) \
+swap -background white -layers merge +repage \
output.jpg
# Rounded corners
magick input.jpg \
\( +clone -threshold -1 -draw "fill black polygon 0,0 0,15 15,0 fill white circle 15,15 15,0" \
\( +clone -flip \) -compose multiply -composite \
\( +clone -flop \) -compose multiply -composite \
\) -alpha off -compose copy_opacity -composite \
output.pngText & Annotations
Basic Text
# Simple text overlay
magick input.jpg -pointsize 30 -fill white -annotate +10+30 "Hello" output.jpg
# Positioned text
magick input.jpg -gravity south -pointsize 20 -fill white \
-annotate +0+10 "Copyright 2025" output.jpg
# Text with background
magick input.jpg -gravity center -pointsize 40 -fill white \
-undercolor black -annotate +0+0 "Watermark" output.jpgAdvanced Text
# Semi-transparent watermark
magick input.jpg \
\( -background none -fill "rgba(255,255,255,0.5)" \
-pointsize 50 label:"DRAFT" \) \
-gravity center -compose over -composite \
output.jpg
# Text with stroke
magick input.jpg -gravity center \
-stroke black -strokewidth 2 -fill white \
-pointsize 60 -annotate +0+0 "Title" \
output.jpg
# Custom font
magick input.jpg -font Arial-Bold -pointsize 40 \
-gravity center -fill white -annotate +0+0 "Text" \
output.jpgImage Composition
Overlay Images
# Basic overlay (top-left)
magick input.jpg overlay.png -composite output.jpg
# Position with gravity
magick input.jpg watermark.png -gravity southeast -composite output.jpg
# Position with offset
magick input.jpg watermark.png -gravity southeast \
-geometry +10+10 -composite output.jpg
# Center overlay
magick input.jpg logo.png -gravity center -composite output.jpgComposite Modes
# Over (default)
magick input.jpg overlay.png -compose over -composite output.jpg
# Multiply
magick input.jpg texture.png -compose multiply -composite output.jpg
# Screen
magick input.jpg light.png -compose screen -composite output.jpg
# Overlay blend mode
magick input.jpg pattern.png -compose overlay -composite output.jpgSide-by-Side
# Horizontal append
magick image1.jpg image2.jpg +append output.jpg
# Vertical append
magick image1.jpg image2.jpg -append output.jpg
# With spacing
magick image1.jpg image2.jpg -gravity center \
-background white -splice 10x0 +append output.jpgTransparency
Create Transparency
# Make color transparent
magick input.jpg -transparent white output.png
# Make similar colors transparent (with fuzz)
magick input.jpg -fuzz 10% -transparent white output.png
# Alpha channel operations
magick input.png -alpha set -channel A -evaluate multiply 0.5 +channel output.pngRemove Transparency
# Flatten with white background
magick input.png -background white -flatten output.jpg
# Flatten with custom color
magick input.png -background "#ff0000" -flatten output.jpgAdvanced Techniques
Vignette Effect
# Default vignette
magick input.jpg -vignette 0x20 output.jpg
# Custom vignette
magick input.jpg -background black -vignette 0x25+10+10 output.jpgDepth of Field Blur
# Radial blur from center
magick input.jpg \
\( +clone -blur 0x8 \) \
\( +clone -fill white -colorize 100 \
-fill black -draw "circle %[fx:w/2],%[fx:h/2] %[fx:w/2],%[fx:h/4]" \
-blur 0x20 \) \
-composite output.jpgHDR Effect
magick input.jpg \
\( +clone -colorspace gray \) \
\( -clone 0 -auto-level -modulate 100,150,100 \) \
-delete 0 -compose overlay -composite \
output.jpgTilt-Shift Effect
magick input.jpg \
\( +clone -sparse-color Barycentric '0,%[fx:h*0.3] gray0 0,%[fx:h*0.5] white 0,%[fx:h*0.7] gray0' \) \
\( +clone -blur 0x20 \) \
-compose blend -define compose:args=100 -composite \
output.jpgColor Management
Color Profiles
# Strip color profile
magick input.jpg -strip output.jpg
# Assign color profile
magick input.jpg -profile sRGB.icc output.jpg
# Convert between profiles
magick input.jpg -profile AdobeRGB.icc -profile sRGB.icc output.jpgColor Space Conversion
# Convert to sRGB
magick input.jpg -colorspace sRGB output.jpg
# Convert to CMYK (print)
magick input.jpg -colorspace CMYK output.tif
# Convert to LAB
magick input.jpg -colorspace LAB output.jpgPerformance Optimization
Memory Management
# Limit memory usage
magick -limit memory 2GB -limit map 4GB input.jpg -resize 50% output.jpg
# Set thread count
magick -limit thread 4 input.jpg -resize 50% output.jpg
# Streaming for large files
magick -define stream:buffer-size=0 huge.jpg -resize 50% output.jpgQuality vs Size
# Maximum quality (large file)
magick input.jpg -quality 95 output.jpg
# Balanced (recommended)
magick input.jpg -quality 85 -strip output.jpg
# Smaller file (acceptable quality)
magick input.jpg -quality 70 -sampling-factor 4:2:0 -strip output.jpg
# Progressive JPEG
magick input.jpg -quality 85 -interlace Plane -strip output.jpgCommon Recipes
Avatar/Profile Picture
# Square thumbnail
magick input.jpg -resize 200x200^ -gravity center -extent 200x200 avatar.jpg
# Circular avatar (PNG)
magick input.jpg -resize 200x200^ -gravity center -extent 200x200 \
\( +clone -threshold -1 -negate -fill white -draw "circle 100,100 100,0" \) \
-alpha off -compose copy_opacity -composite avatar.pngResponsive Images
# Generate multiple sizes
for size in 320 640 1024 1920; do
magick input.jpg -resize ${size}x -quality 85 -strip "output-${size}w.jpg"
donePhoto Enhancement
# Auto-enhance workflow
magick input.jpg \
-auto-level \
-unsharp 0x1 \
-brightness-contrast 5x10 \
-modulate 100,110,100 \
-quality 90 -strip \
output.jpgRMBG - Background Removal CLI
Local AI-powered background removal tool. Repository: https://github.com/mrgoonie/rmbg
Installation
npm install -g rmbg-cli
# or
pnpm install -g rmbg-cliUsage
# Basic usage (uses modnet model)
rmbg input.jpg
# Specify output path
rmbg input.jpg -o output.png
# Choose model
rmbg input.jpg -m briaai -o high-quality.png
# Set max resolution
rmbg image.jpg -r 4096 -o image-4k.pngCLI Options
-o, --output <path>- Output path (default:input-no-bg.png)-m, --model <model>- Model name (default:modnet)-r, --max-resolution <n>- Max resolution in pixels (default:2048)
Available Models
| Model | Size | Speed | Quality | Use Case |
|---|---|---|---|---|
u2netp | 4.5MB | ⚡⚡⚡ Fastest | Fair | Batch processing |
modnet | 25MB | ⚡⚡ Fast | Good | Default, balanced |
briaai | 44MB | ⚡ Slower | Excellent | High-quality |
isnet-anime | 168MB | ⚡ Slower | Specialized | Anime/manga |
silueta | 43MB | ⚡⚡⚡ Fast | Good | Portraits |
u2net-cloth | 170MB | ⚡ Slower | Specialized | Fashion/clothing |
Examples
# Fast processing
rmbg photo.jpg -m u2netp -o fast-result.png
# High quality output
rmbg photo.jpg -m briaai -r 4096 -o hq-result.png
# Batch processing
for img in *.jpg; do
rmbg "$img" -m u2netp -o "output/${img%.jpg}.png"
doneNotes
- Models download automatically on first use (~4-170MB depending on model)
- Cache location:
macOS: /var/folders/.../T/rmbg-cache/|Linux: /tmp/rmbg-cache/|Windows: %TEMP%\rmbg-cache\ - Supported formats: JPEG, PNG, WebP
- Max file size: 50MB
Media Processing Troubleshooting
FFmpeg Issues
Unknown Encoder Error
# Check available encoders
ffmpeg -encoders | grep h264
# Install codec libraries (Ubuntu/Debian)
sudo apt-get install libx264-dev libx265-dev libvpx-devMemory Errors
# Limit thread usage
ffmpeg -threads 4 input.mp4 output.mp4
# Process in segments for large files
ffmpeg -i large.mp4 -ss 0 -t 600 segment1.mp4
ffmpeg -i large.mp4 -ss 600 -t 600 segment2.mp4Slow Encoding
# Use faster preset (trades compression for speed)
ffmpeg -i input.mp4 -c:v libx264 -preset ultrafast output.mp4
# Use hardware acceleration
ffmpeg -hwaccel cuda -i input.mp4 -c:v h264_nvenc output.mp4ImageMagick Issues
"Not Authorized" Error
# Edit policy file
sudo nano /etc/ImageMagick-7/policy.xml
# Change from:
# <policy domain="coder" rights="none" pattern="PDF" />
# To:
# <policy domain="coder" rights="read|write" pattern="PDF" />Memory Limit Errors
# Increase memory limits
magick -limit memory 2GB -limit map 4GB input.jpg output.jpg
# Process in batches for large sets
ls *.jpg | xargs -n 10 -P 4 mogrify -resize 800xSlow Batch Processing
# Use parallel processing with GNU Parallel
ls *.jpg | parallel -j 4 magick {} -resize 800x resized-{}
# Or use mogrify for in-place edits (faster)
mogrify -resize 800x *.jpgRMBG Issues
Model Download Failures
# Check network connectivity
curl https://unpkg.com/@rmbg/model-modnet/modnet-256.onnx
# Use custom cache directory
RMBG_CACHE_DIR=/tmp/rmbg-cache rmbg input.jpg
# Clear cache and retry
rm -rf /tmp/rmbg-cache
rmbg input.jpgOut of Memory
# Use smaller model
rmbg input.jpg -m u2netp -o output.png
# Reduce resolution
rmbg input.jpg -r 1024 -o output.pngSlow Processing
# Use fastest model
rmbg input.jpg -m u2netp -o output.png
# Process smaller resolution
rmbg input.jpg -r 1024 -o output.pngPerformance Tips
1. Use CRF for quality control - Better than bitrate for video encoding 2. Copy streams when possible - Avoid re-encoding with -c copy 3. Hardware acceleration - GPU encoding 5-10x faster than CPU 4. Appropriate presets - Balance speed vs compression (fast, medium, slow) 5. Batch with mogrify - In-place image processing faster than individual commands 6. Strip metadata - Reduce file size with -strip flag 7. Progressive JPEG - Better web loading with -interlace Plane 8. Test on samples - Verify settings before processing large batches 9. Parallel processing - Use GNU Parallel for multiple files 10. Limit memory - Prevent crashes on large batches with -limit flags
#!/usr/bin/env python3
"""
Batch image resizing with multiple strategies.
Supports aspect ratio maintenance, smart cropping, thumbnail generation,
watermarks, format conversion, and parallel processing.
"""
import argparse
import subprocess
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
from typing import List, Optional, Tuple
class ImageResizer:
"""Handle image resizing operations using ImageMagick."""
def __init__(self, verbose: bool = False, dry_run: bool = False):
self.verbose = verbose
self.dry_run = dry_run
def check_imagemagick(self) -> bool:
"""Check if ImageMagick is available."""
try:
subprocess.run(
['magick', '-version'],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=True
)
return True
except (subprocess.CalledProcessError, FileNotFoundError):
return False
def build_resize_command(
self,
input_path: Path,
output_path: Path,
width: Optional[int],
height: Optional[int],
strategy: str,
quality: int,
watermark: Optional[Path] = None
) -> List[str]:
"""Build ImageMagick resize command based on strategy."""
cmd = ['magick', str(input_path)]
# Apply resize strategy
if strategy == 'fit':
# Fit within dimensions, maintain aspect ratio
geometry = f"{width or ''}x{height or ''}"
cmd.extend(['-resize', geometry])
elif strategy == 'fill':
# Fill dimensions, crop excess
if not width or not height:
raise ValueError("Both width and height required for 'fill' strategy")
cmd.extend([
'-resize', f'{width}x{height}^',
'-gravity', 'center',
'-extent', f'{width}x{height}'
])
elif strategy == 'cover':
# Cover dimensions, may exceed
if not width or not height:
raise ValueError("Both width and height required for 'cover' strategy")
cmd.extend(['-resize', f'{width}x{height}^'])
elif strategy == 'exact':
# Force exact dimensions, ignore aspect ratio
if not width or not height:
raise ValueError("Both width and height required for 'exact' strategy")
cmd.extend(['-resize', f'{width}x{height}!'])
elif strategy == 'thumbnail':
# Create square thumbnail
size = width or height or 200
cmd.extend([
'-resize', f'{size}x{size}^',
'-gravity', 'center',
'-extent', f'{size}x{size}'
])
# Add watermark if specified
if watermark:
cmd.extend([
str(watermark),
'-gravity', 'southeast',
'-geometry', '+10+10',
'-composite'
])
# Output settings
cmd.extend([
'-quality', str(quality),
'-strip',
str(output_path)
])
return cmd
def resize_image(
self,
input_path: Path,
output_path: Path,
width: Optional[int],
height: Optional[int],
strategy: str = 'fit',
quality: int = 85,
watermark: Optional[Path] = None
) -> bool:
"""Resize a single image."""
try:
# Ensure output directory exists
output_path.parent.mkdir(parents=True, exist_ok=True)
cmd = self.build_resize_command(
input_path, output_path, width, height,
strategy, quality, watermark
)
if self.verbose or self.dry_run:
print(f"Command: {' '.join(cmd)}")
if self.dry_run:
return True
subprocess.run(
cmd,
stdout=subprocess.PIPE if not self.verbose else None,
stderr=subprocess.PIPE if not self.verbose else None,
check=True
)
return True
except subprocess.CalledProcessError as e:
print(f"Error resizing {input_path}: {e}", file=sys.stderr)
if not self.verbose and e.stderr:
print(e.stderr.decode(), file=sys.stderr)
return False
except Exception as e:
print(f"Error processing {input_path}: {e}", file=sys.stderr)
return False
def batch_resize(
self,
input_paths: List[Path],
output_dir: Path,
width: Optional[int],
height: Optional[int],
strategy: str = 'fit',
quality: int = 85,
format_ext: Optional[str] = None,
watermark: Optional[Path] = None,
parallel: int = 1
) -> Tuple[int, int]:
"""Resize multiple images."""
success_count = 0
fail_count = 0
def process_image(input_path: Path) -> Tuple[Path, bool]:
"""Process single image for parallel execution."""
if not input_path.exists() or not input_path.is_file():
return input_path, False
# Determine output path
output_name = input_path.stem
if format_ext:
output_path = output_dir / f"{output_name}.{format_ext.lstrip('.')}"
else:
output_path = output_dir / input_path.name
if not self.dry_run:
print(f"Processing {input_path.name} -> {output_path.name}")
success = self.resize_image(
input_path, output_path, width, height,
strategy, quality, watermark
)
return input_path, success
# Process images
if parallel > 1:
with ThreadPoolExecutor(max_workers=parallel) as executor:
futures = [executor.submit(process_image, path) for path in input_paths]
for future in as_completed(futures):
_, success = future.result()
if success:
success_count += 1
else:
fail_count += 1
else:
for input_path in input_paths:
_, success = process_image(input_path)
if success:
success_count += 1
else:
fail_count += 1
return success_count, fail_count
def collect_images(paths: List[Path], recursive: bool = False) -> List[Path]:
"""Collect image files from paths."""
image_exts = {'.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.tiff', '.tif'}
images = []
for path in paths:
if path.is_file() and path.suffix.lower() in image_exts:
images.append(path)
elif path.is_dir():
pattern = '**/*' if recursive else '*'
for img_path in path.glob(pattern):
if img_path.is_file() and img_path.suffix.lower() in image_exts:
images.append(img_path)
return images
def main():
"""Main entry point."""
parser = argparse.ArgumentParser(
description='Batch image resizing with multiple strategies.'
)
parser.add_argument(
'inputs',
nargs='+',
type=Path,
help='Input image(s) or directory'
)
parser.add_argument(
'-o', '--output',
type=Path,
required=True,
help='Output directory'
)
parser.add_argument(
'-w', '--width',
type=int,
help='Target width in pixels'
)
parser.add_argument(
'-h', '--height',
type=int,
dest='img_height',
help='Target height in pixels'
)
parser.add_argument(
'-s', '--strategy',
choices=['fit', 'fill', 'cover', 'exact', 'thumbnail'],
default='fit',
help='Resize strategy (default: fit)'
)
parser.add_argument(
'-q', '--quality',
type=int,
default=85,
help='Output quality 0-100 (default: 85)'
)
parser.add_argument(
'-f', '--format',
help='Output format (e.g., jpg, png, webp)'
)
parser.add_argument(
'-wm', '--watermark',
type=Path,
help='Watermark image to overlay'
)
parser.add_argument(
'-p', '--parallel',
type=int,
default=1,
help='Number of parallel processes (default: 1)'
)
parser.add_argument(
'-r', '--recursive',
action='store_true',
help='Process directories recursively'
)
parser.add_argument(
'-n', '--dry-run',
action='store_true',
help='Show commands without executing'
)
parser.add_argument(
'-v', '--verbose',
action='store_true',
help='Verbose output'
)
args = parser.parse_args()
# Validate dimensions
if not args.width and not args.img_height:
print("Error: At least one of --width or --height required", file=sys.stderr)
sys.exit(1)
# Initialize resizer
resizer = ImageResizer(verbose=args.verbose, dry_run=args.dry_run)
# Check dependencies
if not resizer.check_imagemagick():
print("Error: ImageMagick not found", file=sys.stderr)
sys.exit(1)
# Collect input images
images = collect_images(args.inputs, args.recursive)
if not images:
print("Error: No images found", file=sys.stderr)
sys.exit(1)
print(f"Found {len(images)} image(s) to process")
# Create output directory
if not args.dry_run:
args.output.mkdir(parents=True, exist_ok=True)
# Process images
success, fail = resizer.batch_resize(
images,
args.output,
args.width,
args.img_height,
args.strategy,
args.quality,
args.format,
args.watermark,
args.parallel
)
print(f"\nResults: {success} succeeded, {fail} failed")
sys.exit(0 if fail == 0 else 1)
if __name__ == '__main__':
main()
#!/bin/bash
# Batch background removal script using RMBG CLI
# Usage: ./batch-remove-background.sh <input_dir> [output_dir] [model] [resolution]
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Default values
INPUT_DIR=""
OUTPUT_DIR=""
MODEL="modnet"
MAX_RESOLUTION="2048"
# Parse arguments
INPUT_DIR="$1"
if [ -n "$2" ]; then
OUTPUT_DIR="$2"
fi
if [ -n "$3" ]; then
MODEL="$3"
fi
if [ -n "$4" ]; then
MAX_RESOLUTION="$4"
fi
# Validate input directory
if [ -z "$INPUT_DIR" ]; then
echo -e "${RED}Error: Input directory is required${NC}"
echo ""
echo "Usage: $0 <input_dir> [output_dir] [model] [resolution]"
echo ""
echo "Arguments:"
echo " input_dir Input directory with images (required)"
echo " output_dir Output directory (default: input_dir/no-bg)"
echo " model Model name: u2netp, modnet, briaai, isnet-anime, silueta, u2net-cloth (default: modnet)"
echo " resolution Max resolution in pixels (default: 2048)"
echo ""
echo "Examples:"
echo " $0 ./photos"
echo " $0 ./photos ./output"
echo " $0 ./photos ./output briaai"
echo " $0 ./photos ./output briaai 4096"
exit 1
fi
if [ ! -d "$INPUT_DIR" ]; then
echo -e "${RED}Error: Input directory '$INPUT_DIR' not found${NC}"
exit 1
fi
# Set default output directory
if [ -z "$OUTPUT_DIR" ]; then
OUTPUT_DIR="$INPUT_DIR/no-bg"
fi
# Create output directory if it doesn't exist
mkdir -p "$OUTPUT_DIR"
# Check if rmbg-cli is installed
if ! command -v rmbg &> /dev/null; then
echo -e "${YELLOW}Warning: rmbg-cli not found${NC}"
echo "Installing rmbg-cli globally..."
npm install -g rmbg-cli
echo -e "${GREEN}✓ rmbg-cli installed${NC}"
fi
# Find all image files
IMAGE_FILES=$(find "$INPUT_DIR" -maxdepth 1 -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.webp" \))
TOTAL_FILES=$(echo "$IMAGE_FILES" | grep -v '^$' | wc -l | tr -d ' ')
if [ "$TOTAL_FILES" -eq 0 ]; then
echo -e "${YELLOW}Warning: No image files found in '$INPUT_DIR'${NC}"
exit 0
fi
# Display configuration
echo -e "${GREEN}Batch Background Removal Configuration:${NC}"
echo " Input Dir: $INPUT_DIR"
echo " Output Dir: $OUTPUT_DIR"
echo " Model: $MODEL"
echo " Resolution: $MAX_RESOLUTION"
echo " Total Files: $TOTAL_FILES"
echo ""
# Process each image
SUCCESS_COUNT=0
FAIL_COUNT=0
CURRENT=0
while IFS= read -r file; do
[ -z "$file" ] && continue
CURRENT=$((CURRENT + 1))
BASENAME=$(basename "$file")
OUTPUT_FILE="$OUTPUT_DIR/${BASENAME%.*}.png"
echo -e "${BLUE}[$CURRENT/$TOTAL_FILES]${NC} Processing: $BASENAME"
if rmbg "$file" -m "$MODEL" -o "$OUTPUT_FILE" -r "$MAX_RESOLUTION" 2>/dev/null; then
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
echo -e " ${GREEN}✓ Success${NC}"
else
FAIL_COUNT=$((FAIL_COUNT + 1))
echo -e " ${RED}✗ Failed${NC}"
fi
done <<< "$IMAGE_FILES"
# Display summary
echo ""
echo -e "${GREEN}Batch Processing Complete${NC}"
echo " Total: $TOTAL_FILES files"
echo " Success: $SUCCESS_COUNT files"
echo " Failed: $FAIL_COUNT files"
echo " Output: $OUTPUT_DIR"
if [ "$FAIL_COUNT" -gt 0 ]; then
exit 1
fi
#!/usr/bin/env python3
"""
Unified media conversion tool for video, audio, and images.
Auto-detects format and applies appropriate tool (FFmpeg or ImageMagick).
Supports quality presets, batch processing, and dry-run mode.
"""
import argparse
import subprocess
import sys
from pathlib import Path
from typing import List, Optional, Tuple
# Format mappings
VIDEO_FORMATS = {'.mp4', '.mkv', '.avi', '.mov', '.webm', '.flv', '.wmv', '.m4v'}
AUDIO_FORMATS = {'.mp3', '.aac', '.m4a', '.opus', '.flac', '.wav', '.ogg'}
IMAGE_FORMATS = {'.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.tiff', '.tif'}
# Quality presets
QUALITY_PRESETS = {
'web': {
'video_crf': 23,
'video_preset': 'medium',
'audio_bitrate': '128k',
'image_quality': 85
},
'archive': {
'video_crf': 18,
'video_preset': 'slow',
'audio_bitrate': '192k',
'image_quality': 95
},
'mobile': {
'video_crf': 26,
'video_preset': 'fast',
'audio_bitrate': '96k',
'image_quality': 80
}
}
def check_dependencies() -> Tuple[bool, bool]:
"""Check if ffmpeg and imagemagick are available."""
ffmpeg_available = subprocess.run(
['ffmpeg', '-version'],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
).returncode == 0
magick_available = subprocess.run(
['magick', '-version'],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
).returncode == 0
return ffmpeg_available, magick_available
def detect_media_type(file_path: Path) -> str:
"""Detect media type from file extension."""
ext = file_path.suffix.lower()
if ext in VIDEO_FORMATS:
return 'video'
elif ext in AUDIO_FORMATS:
return 'audio'
elif ext in IMAGE_FORMATS:
return 'image'
else:
return 'unknown'
def build_video_command(
input_path: Path,
output_path: Path,
preset: str = 'web'
) -> List[str]:
"""Build FFmpeg command for video conversion."""
quality = QUALITY_PRESETS[preset]
return [
'ffmpeg', '-i', str(input_path),
'-c:v', 'libx264',
'-preset', quality['video_preset'],
'-crf', str(quality['video_crf']),
'-c:a', 'aac',
'-b:a', quality['audio_bitrate'],
'-movflags', '+faststart',
'-y',
str(output_path)
]
def build_audio_command(
input_path: Path,
output_path: Path,
preset: str = 'web'
) -> List[str]:
"""Build FFmpeg command for audio conversion."""
quality = QUALITY_PRESETS[preset]
output_ext = output_path.suffix.lower()
codec_map = {
'.mp3': 'libmp3lame',
'.aac': 'aac',
'.m4a': 'aac',
'.opus': 'libopus',
'.flac': 'flac',
'.wav': 'pcm_s16le',
'.ogg': 'libvorbis'
}
codec = codec_map.get(output_ext, 'aac')
cmd = ['ffmpeg', '-i', str(input_path), '-c:a', codec]
# Add bitrate for lossy codecs
if codec not in ['flac', 'pcm_s16le']:
cmd.extend(['-b:a', quality['audio_bitrate']])
cmd.extend(['-y', str(output_path)])
return cmd
def build_image_command(
input_path: Path,
output_path: Path,
preset: str = 'web'
) -> List[str]:
"""Build ImageMagick command for image conversion."""
quality = QUALITY_PRESETS[preset]
return [
'magick', str(input_path),
'-quality', str(quality['image_quality']),
'-strip',
str(output_path)
]
def convert_file(
input_path: Path,
output_path: Path,
preset: str = 'web',
dry_run: bool = False,
verbose: bool = False
) -> bool:
"""Convert a single media file."""
media_type = detect_media_type(input_path)
if media_type == 'unknown':
print(f"Error: Unsupported format for {input_path}", file=sys.stderr)
return False
# Ensure output directory exists
output_path.parent.mkdir(parents=True, exist_ok=True)
# Build command based on media type
if media_type == 'video':
cmd = build_video_command(input_path, output_path, preset)
elif media_type == 'audio':
cmd = build_audio_command(input_path, output_path, preset)
else: # image
cmd = build_image_command(input_path, output_path, preset)
if verbose or dry_run:
print(f"Command: {' '.join(cmd)}")
if dry_run:
return True
try:
result = subprocess.run(
cmd,
stdout=subprocess.PIPE if not verbose else None,
stderr=subprocess.PIPE if not verbose else None,
check=True
)
return True
except subprocess.CalledProcessError as e:
print(f"Error converting {input_path}: {e}", file=sys.stderr)
if not verbose and e.stderr:
print(e.stderr.decode(), file=sys.stderr)
return False
except Exception as e:
print(f"Error converting {input_path}: {e}", file=sys.stderr)
return False
def batch_convert(
input_paths: List[Path],
output_dir: Optional[Path] = None,
output_format: Optional[str] = None,
preset: str = 'web',
dry_run: bool = False,
verbose: bool = False
) -> Tuple[int, int]:
"""Convert multiple files."""
success_count = 0
fail_count = 0
for input_path in input_paths:
if not input_path.exists():
print(f"Error: {input_path} not found", file=sys.stderr)
fail_count += 1
continue
# Determine output path
if output_dir:
output_name = input_path.stem
if output_format:
output_path = output_dir / f"{output_name}.{output_format.lstrip('.')}"
else:
output_path = output_dir / input_path.name
else:
if output_format:
output_path = input_path.with_suffix(f".{output_format.lstrip('.')}")
else:
print(f"Error: No output format specified for {input_path}", file=sys.stderr)
fail_count += 1
continue
print(f"Converting {input_path.name} -> {output_path.name}")
if convert_file(input_path, output_path, preset, dry_run, verbose):
success_count += 1
else:
fail_count += 1
return success_count, fail_count
def main():
"""Main entry point."""
parser = argparse.ArgumentParser(
description='Unified media conversion tool for video, audio, and images.'
)
parser.add_argument(
'inputs',
nargs='+',
type=Path,
help='Input file(s) to convert'
)
parser.add_argument(
'-o', '--output',
type=Path,
help='Output file or directory for batch conversion'
)
parser.add_argument(
'-f', '--format',
help='Output format (e.g., mp4, jpg, mp3)'
)
parser.add_argument(
'-p', '--preset',
choices=['web', 'archive', 'mobile'],
default='web',
help='Quality preset (default: web)'
)
parser.add_argument(
'-n', '--dry-run',
action='store_true',
help='Show commands without executing'
)
parser.add_argument(
'-v', '--verbose',
action='store_true',
help='Verbose output'
)
args = parser.parse_args()
# Check dependencies
ffmpeg_ok, magick_ok = check_dependencies()
if not ffmpeg_ok and not magick_ok:
print("Error: Neither ffmpeg nor imagemagick found", file=sys.stderr)
sys.exit(1)
# Handle single file vs batch conversion
if len(args.inputs) == 1 and args.output and not args.output.is_dir():
# Single file conversion
success = convert_file(
args.inputs[0],
args.output,
args.preset,
args.dry_run,
args.verbose
)
sys.exit(0 if success else 1)
else:
# Batch conversion
output_dir = args.output if args.output else Path.cwd()
if not args.output:
output_dir = None # Will convert in place with new format
success, fail = batch_convert(
args.inputs,
output_dir,
args.format,
args.preset,
args.dry_run,
args.verbose
)
print(f"\nResults: {success} succeeded, {fail} failed")
sys.exit(0 if fail == 0 else 1)
if __name__ == '__main__':
main()
Media Processing Scripts
Helper scripts for common media processing tasks.
Background Removal Scripts
remove-background.sh
Remove background from a single image using RMBG CLI.
# Basic usage
./remove-background.sh photo.jpg
# With specific model
./remove-background.sh photo.jpg briaai
# With custom output and resolution
./remove-background.sh photo.jpg briaai output.png 4096Arguments:
input- Input image file (required)model- Model name: u2netp, modnet, briaai, isnet-anime, silueta, u2net-cloth (default: modnet)output- Output file path (default: auto-generated)resolution- Max resolution in pixels (default: 2048)
batch-remove-background.sh
Remove backgrounds from all images in a directory.
# Basic usage
./batch-remove-background.sh ./photos
# With custom output directory
./batch-remove-background.sh ./photos ./output
# With specific model and resolution
./batch-remove-background.sh ./photos ./output briaai 4096Arguments:
input_dir- Input directory with images (required)output_dir- Output directory (default: input_dir/no-bg)model- Model name (default: modnet)resolution- Max resolution in pixels (default: 2048)
remove-bg-node.js
Node.js script for background removal with progress tracking.
# Basic usage
node remove-bg-node.js photo.jpg
# With options
node remove-bg-node.js photo.jpg -m briaai -o output.png -r 4096 -pOptions:
-o, --output <path>- Output file path-m, --model <name>- Model: briaai, modnet, u2netp-r, --resolution <n>- Max resolution-p, --progress- Show progress
Image Processing Scripts
batch_resize.py
Batch resize images with various options.
python batch_resize.py -i ./input -o ./output -w 800 -h 600Video Processing Scripts
video_optimize.py
Optimize videos for web with quality and size optimization.
python video_optimize.py -i input.mp4 -o output.mp4 --preset slow --crf 23media_convert.py
Convert media files between different formats.
python media_convert.py -i input.mkv -o output.mp4 --codec h264Requirements
Shell Scripts
- Bash (macOS, Linux)
- rmbg-cli:
npm install -g rmbg-cli - FFmpeg:
brew install ffmpegorapt-get install ffmpeg - ImageMagick:
brew install imagemagickorapt-get install imagemagick
Node.js Scripts
- Node.js 14+
- Dependencies:
npm install rmbg
Python Scripts
- Python 3.7+
- Dependencies:
pip install -r requirements.txt
Testing
Run tests:
cd tests
bash test_all.sh#!/bin/bash
# Background removal script using RMBG CLI
# Usage: ./remove-background.sh <input> [model] [output] [resolution]
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Default values
INPUT=""
MODEL="modnet"
OUTPUT=""
MAX_RESOLUTION="2048"
# Parse arguments
INPUT="$1"
if [ -n "$2" ]; then
MODEL="$2"
fi
if [ -n "$3" ]; then
OUTPUT="$3"
fi
if [ -n "$4" ]; then
MAX_RESOLUTION="$4"
fi
# Validate input
if [ -z "$INPUT" ]; then
echo -e "${RED}Error: Input file is required${NC}"
echo ""
echo "Usage: $0 <input> [model] [output] [resolution]"
echo ""
echo "Arguments:"
echo " input Input image file (required)"
echo " model Model name: u2netp, modnet, briaai, isnet-anime, silueta, u2net-cloth (default: modnet)"
echo " output Output file path (default: auto-generated)"
echo " resolution Max resolution in pixels (default: 2048)"
echo ""
echo "Examples:"
echo " $0 photo.jpg"
echo " $0 photo.jpg briaai"
echo " $0 photo.jpg briaai output.png"
echo " $0 photo.jpg briaai output.png 4096"
exit 1
fi
if [ ! -f "$INPUT" ]; then
echo -e "${RED}Error: Input file '$INPUT' not found${NC}"
exit 1
fi
# Check if rmbg-cli is installed
if ! command -v rmbg &> /dev/null; then
echo -e "${YELLOW}Warning: rmbg-cli not found${NC}"
echo "Installing rmbg-cli globally..."
npm install -g rmbg-cli
echo -e "${GREEN}✓ rmbg-cli installed${NC}"
fi
# Generate output filename if not provided
if [ -z "$OUTPUT" ]; then
BASENAME=$(basename "$INPUT" | sed 's/\.[^.]*$//')
OUTPUT="${BASENAME}-no-bg.png"
fi
# Display configuration
echo -e "${GREEN}Background Removal Configuration:${NC}"
echo " Input: $INPUT"
echo " Model: $MODEL"
echo " Output: $OUTPUT"
echo " Resolution: $MAX_RESOLUTION"
echo ""
# Remove background
echo "Processing..."
rmbg "$INPUT" -m "$MODEL" -o "$OUTPUT" -r "$MAX_RESOLUTION"
if [ $? -eq 0 ]; then
echo -e "${GREEN}✓ Background removed successfully${NC}"
echo " Output: $OUTPUT"
# Display file sizes
INPUT_SIZE=$(du -h "$INPUT" | cut -f1)
OUTPUT_SIZE=$(du -h "$OUTPUT" | cut -f1)
echo ""
echo "File sizes:"
echo " Input: $INPUT_SIZE"
echo " Output: $OUTPUT_SIZE"
else
echo -e "${RED}✗ Background removal failed${NC}"
exit 1
fi
#!/usr/bin/env node
/**
* Node.js background removal script using RMBG SDK
* Usage: node remove-bg-node.js <input> [options]
*/
const { rmbg, createBriaaiModel, createModnetModel, createU2netpModel } = require('rmbg')
const { readFileSync, writeFileSync, existsSync } = require('fs')
const { basename, extname } = require('path')
// Parse command line arguments
const args = process.argv.slice(2)
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
console.log(`
Background Removal using RMBG SDK
Usage: node remove-bg-node.js <input> [options]
Arguments:
input Input image file path (required)
Options:
-o, --output <path> Output file path (default: auto-generated)
-m, --model <name> Model: briaai, modnet, u2netp (default: modnet)
-r, --resolution <n> Max resolution in pixels (default: 2048)
-p, --progress Show progress information
-h, --help Show this help message
Examples:
node remove-bg-node.js photo.jpg
node remove-bg-node.js photo.jpg -m briaai -o output.png
node remove-bg-node.js photo.jpg -r 4096 -p
`)
process.exit(0)
}
// Parse options
const input = args[0]
let output = null
let model = 'modnet'
let maxResolution = 2048
let showProgress = false
for (let i = 1; i < args.length; i++) {
const arg = args[i]
if (arg === '-o' || arg === '--output') {
output = args[++i]
} else if (arg === '-m' || arg === '--model') {
model = args[++i]
} else if (arg === '-r' || arg === '--resolution') {
maxResolution = parseInt(args[++i], 10)
} else if (arg === '-p' || arg === '--progress') {
showProgress = true
}
}
// Validate input
if (!input) {
console.error('Error: Input file is required')
process.exit(1)
}
if (!existsSync(input)) {
console.error(`Error: Input file '${input}' not found`)
process.exit(1)
}
// Generate output filename if not provided
if (!output) {
const name = basename(input, extname(input))
output = `${name}-no-bg.png`
}
// Select model
let modelInstance
switch (model.toLowerCase()) {
case 'briaai':
modelInstance = createBriaaiModel()
break
case 'u2netp':
modelInstance = createU2netpModel()
break
case 'modnet':
default:
modelInstance = createModnetModel()
break
}
// Display configuration
console.log('Background Removal Configuration:')
console.log(` Input: ${input}`)
console.log(` Model: ${model}`)
console.log(` Output: ${output}`)
console.log(` Resolution: ${maxResolution}`)
console.log('')
// Remove background
async function removeBackground() {
try {
console.log('Processing...')
const startTime = Date.now()
const options = {
model: modelInstance,
maxResolution,
output
}
if (showProgress) {
options.onProgress = (progress, download, process) => {
const percent = Math.round(progress * 100)
const downloadPercent = Math.round(download * 100)
const processPercent = Math.round(process * 100)
process.stdout.write(
`\rProgress: ${percent}% | Download: ${downloadPercent}% | Process: ${processPercent}%`
)
}
}
await rmbg(input, options)
const duration = ((Date.now() - startTime) / 1000).toFixed(2)
if (showProgress) {
console.log('') // New line after progress
}
console.log('✓ Background removed successfully')
console.log(` Output: ${output}`)
console.log(` Duration: ${duration}s`)
// Display file sizes
const inputStats = require('fs').statSync(input)
const outputStats = require('fs').statSync(output)
console.log('')
console.log('File sizes:')
console.log(` Input: ${formatBytes(inputStats.size)}`)
console.log(` Output: ${formatBytes(outputStats.size)}`)
} catch (error) {
console.error('✗ Background removal failed:', error.message)
process.exit(1)
}
}
function formatBytes(bytes) {
if (bytes === 0) return '0 Bytes'
const k = 1024
const sizes = ['Bytes', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]
}
// Run
removeBackground()
# Media Processing Skill Dependencies
# Python 3.10+ required
# No Python package dependencies - uses system binaries
# Required system tools (install separately):
# - FFmpeg (video/audio processing)
# - ImageMagick (image processing)
# Testing dependencies (dev)
pytest>=8.0.0
pytest-cov>=4.1.0
pytest-mock>=3.12.0
# Installation instructions:
#
# Ubuntu/Debian:
# sudo apt-get install ffmpeg imagemagick
#
# macOS (Homebrew):
# brew install ffmpeg imagemagick
#
# Windows:
# choco install ffmpeg imagemagick
# or download from official websites
SQLite format 3@
.�
V�^
O-�
&�m ��a c��Q�}tabletracertracer
CREATE TABLE tracer (
-- A row per file indicating the tracer used for that file.
file_id integer primary key,
tracer text,
foreign key (file_id) references file (id)
)�
�etablearcarcCREATE TABLE arc (
-- If recording branches, a row per context per from/to line transition executed.
file_id integer, -- foreign key to `file`.
context_id integer, -- foreign key to `context`.
fromno integer, -- line number jumped from.
tono integer, -- line number jumped to.
foreign key (file_id) references file (id),
foreign key (context_id) references context (id),
unique (file_id, context_id, fromno, tono)
)%9indexsqlite_autoindex_arc_1arc��qtableline_bitsline_bits CREATE TABLE line_bits (
-- If recording lines, a row per context per file executed.
-- All of the line numbers for that file/context are in one numbits.
file_id integer, -- foreign key to `file`.
context_id integer, -- foreign key to `context`.
numbits blob, -- see the numbits functions in coverage.numbits
foreign key (file_id) references file (id),
foreign key (context_id) references context (id),
unique (file_id, context_id)
)1 Eindexsqlite_autoindex_line_bits_1line_bits
�� tablecontextcontextCREATE TABLE context (
-- A row per context measured.
id integer primary key,
context text,
unique (context)
)-Aindexsqlite_autoindex_context_1context��qtablefilefileCREATE TABLE file (
-- A row per file measured.
id integer primary key,
path text,
unique (path)
)';indexsqlite_autoindex_file_1file�[�tablemetametaCREATE TABLE meta (
-- Key-value pairs, to record metadata about the data
key text,
value text,
unique (key)
-- Possible keys:
-- 'has_arcs' boolean -- Is this data recording branches?
-- 'sys_argv' text -- The coverage command line that recorded the data.
-- 'version' text -- The version of coverage.py that made the file.
-- 'when' text -- Datetime when the file was created.
)';indexsqlite_autoindex_meta_1meta�++�utablecoverage_schemacoverage_schemaCREATE TABLE coverage_schema (
-- One row, to record the version of the schema in this db.
version integer
)
��
���has_arcs0version7.11.0
���has_arcs
version
{�+�T
�
{e�O/mnt/d/www/claudekit/claudekit-engineer/.claude/skills/media-processing/scripts/video_optimize.pyp�e/mnt/d/www/claudekit/claudekit-engineer/.claude/skills/media-processing/scripts/tests/test_video_optimize.pyd�M/mnt/d/www/claudekit/claudekit-engineer/.claude/skills/media-processing/scripts/media_convert.pyo�c/mnt/d/www/claudekit/claudekit-engineer/.claude/skills/media-processing/scripts/tests/test_media_convert.pyc�K/mnt/d/www/claudekit/claudekit-engineer/.claude/skills/media-processing/scripts/batch_resize.pyn�a/mnt/d/www/claudekit/claudekit-engineer/.claude/skills/media-processing/scripts/tests/test_batch_resize.py
|,U��
�
|f�O/mnt/d/www/claudekit/claudekit-engineer/.claude/skills/media-processing/scripts/video_optimize.pyq�e/mnt/d/www/claudekit/claudekit-engineer/.claude/skills/media-processing/scripts/tests/test_video_optimize.pye�M/mnt/d/www/claudekit/claudekit-engineer/.claude/skills/media-processing/scripts/media_convert.pyp�c/mnt/d/www/claudekit/claudekit-engineer/.claude/skills/media-processing/scripts/tests/test_media_convert.pyd�K/mnt/d/www/claudekit/claudekit-engineer/.claude/skills/media-processing/scripts/batch_resize.pyn�a /mnt/d/www/claudekit/claudekit-engineer/.claude/skills/media-processing/scripts/tests/test_batch_resize.py
��
��
���kC
�9 t��v��f_��\�j��
F��͔r��8���L�9�< 7 p�RV۶���[kkkkkk�����}����ɻ��S����3��o'��<���?�& Nt)��z�֬�=�y=�����۳�i���z��3 jt)�m��_��\�y��֟���'k�]o뮷Y�[?���l���ן�� , Z>������~ ���)����NN�o8@0 b~v�.i�TU?�Pg�
V\; �+�_
�������
pytest>=7.4.0
pytest-cov>=4.1.0
#!/usr/bin/env python3
"""Tests for batch_resize.py"""
import sys
from pathlib import Path
from unittest.mock import MagicMock, call, patch
import pytest
# Add parent directory to path
sys.path.insert(0, str(Path(__file__).parent.parent))
from batch_resize import ImageResizer, collect_images
class TestImageResizer:
"""Test ImageResizer class."""
def setup_method(self):
"""Set up test fixtures."""
self.resizer = ImageResizer(verbose=False, dry_run=False)
@patch("subprocess.run")
def test_check_imagemagick_available(self, mock_run):
"""Test ImageMagick availability check."""
mock_run.return_value = MagicMock(returncode=0)
assert self.resizer.check_imagemagick() is True
@patch("subprocess.run")
def test_check_imagemagick_unavailable(self, mock_run):
"""Test when ImageMagick is not available."""
mock_run.side_effect = FileNotFoundError()
assert self.resizer.check_imagemagick() is False
def test_build_resize_command_fit_strategy(self):
"""Test command building for 'fit' strategy."""
cmd = self.resizer.build_resize_command(
Path("input.jpg"),
Path("output.jpg"),
width=800,
height=600,
strategy="fit",
quality=85
)
assert "magick" in cmd
assert str(Path("input.jpg")) in cmd
assert "-resize" in cmd
assert "800x600" in cmd
assert "-quality" in cmd
assert "85" in cmd
assert "-strip" in cmd
def test_build_resize_command_fill_strategy(self):
"""Test command building for 'fill' strategy."""
cmd = self.resizer.build_resize_command(
Path("input.jpg"),
Path("output.jpg"),
width=800,
height=600,
strategy="fill",
quality=85
)
assert "-resize" in cmd
assert "800x600^" in cmd
assert "-gravity" in cmd
assert "center" in cmd
assert "-extent" in cmd
def test_build_resize_command_thumbnail_strategy(self):
"""Test command building for 'thumbnail' strategy."""
cmd = self.resizer.build_resize_command(
Path("input.jpg"),
Path("output.jpg"),
width=200,
height=None,
strategy="thumbnail",
quality=85
)
assert "200x200^" in cmd
assert "-gravity" in cmd
assert "center" in cmd
def test_build_resize_command_with_watermark(self):
"""Test command building with watermark."""
watermark = Path("watermark.png")
cmd = self.resizer.build_resize_command(
Path("input.jpg"),
Path("output.jpg"),
width=800,
height=None,
strategy="fit",
quality=85,
watermark=watermark
)
assert str(watermark) in cmd
assert "-gravity" in cmd
assert "southeast" in cmd
assert "-composite" in cmd
def test_build_resize_command_exact_strategy(self):
"""Test command building for 'exact' strategy."""
cmd = self.resizer.build_resize_command(
Path("input.jpg"),
Path("output.jpg"),
width=800,
height=600,
strategy="exact",
quality=85
)
assert "800x600!" in cmd
def test_build_resize_command_fill_requires_dimensions(self):
"""Test that 'fill' strategy requires both dimensions."""
with pytest.raises(ValueError):
self.resizer.build_resize_command(
Path("input.jpg"),
Path("output.jpg"),
width=800,
height=None,
strategy="fill",
quality=85
)
@patch("subprocess.run")
def test_resize_image_success(self, mock_run):
"""Test successful image resize."""
mock_run.return_value = MagicMock(returncode=0)
result = self.resizer.resize_image(
Path("input.jpg"),
Path("output/output.jpg"),
width=800,
height=None,
strategy="fit",
quality=85
)
assert result is True
mock_run.assert_called_once()
@patch("subprocess.run")
def test_resize_image_dry_run(self, mock_run):
"""Test resize in dry-run mode."""
resizer = ImageResizer(dry_run=True)
result = resizer.resize_image(
Path("input.jpg"),
Path("output.jpg"),
width=800,
height=None
)
assert result is True
mock_run.assert_not_called()
@patch("subprocess.run")
def test_resize_image_failure(self, mock_run):
"""Test resize failure handling."""
mock_run.side_effect = Exception("Resize failed")
result = self.resizer.resize_image(
Path("input.jpg"),
Path("output.jpg"),
width=800,
height=None
)
assert result is False
class TestCollectImages:
"""Test image collection functionality."""
def test_collect_images_from_file(self, tmp_path):
"""Test collecting a single image file."""
img_file = tmp_path / "test.jpg"
img_file.touch()
images = collect_images([img_file])
assert len(images) == 1
assert images[0] == img_file
def test_collect_images_from_directory(self, tmp_path):
"""Test collecting images from directory."""
(tmp_path / "image1.jpg").touch()
(tmp_path / "image2.png").touch()
(tmp_path / "text.txt").touch()
images = collect_images([tmp_path])
assert len(images) == 2
assert all(img.suffix.lower() in {'.jpg', '.png'} for img in images)
def test_collect_images_recursive(self, tmp_path):
"""Test recursive image collection."""
subdir = tmp_path / "subdir"
subdir.mkdir()
(tmp_path / "image1.jpg").touch()
(subdir / "image2.jpg").touch()
images = collect_images([tmp_path], recursive=True)
assert len(images) == 2
images_non_recursive = collect_images([tmp_path], recursive=False)
assert len(images_non_recursive) == 1
def test_collect_images_filters_extensions(self, tmp_path):
"""Test that only image files are collected."""
(tmp_path / "image.jpg").touch()
(tmp_path / "doc.pdf").touch()
(tmp_path / "text.txt").touch()
images = collect_images([tmp_path])
assert len(images) == 1
assert images[0].suffix.lower() == '.jpg'
def test_collect_images_multiple_paths(self, tmp_path):
"""Test collecting from multiple paths."""
dir1 = tmp_path / "dir1"
dir2 = tmp_path / "dir2"
dir1.mkdir()
dir2.mkdir()
(dir1 / "image1.jpg").touch()
(dir2 / "image2.png").touch()
images = collect_images([dir1, dir2])
assert len(images) == 2
class TestBatchResize:
"""Test batch resize functionality."""
def setup_method(self):
"""Set up test fixtures."""
self.resizer = ImageResizer(verbose=False, dry_run=False)
@patch.object(ImageResizer, "resize_image")
def test_batch_resize_success(self, mock_resize, tmp_path):
"""Test successful batch resize."""
mock_resize.return_value = True
input_images = [
tmp_path / "image1.jpg",
tmp_path / "image2.jpg"
]
for img in input_images:
img.touch()
output_dir = tmp_path / "output"
success, fail = self.resizer.batch_resize(
input_images,
output_dir,
width=800,
height=None,
strategy="fit"
)
assert success == 2
assert fail == 0
assert mock_resize.call_count == 2
@patch.object(ImageResizer, "resize_image")
def test_batch_resize_with_failures(self, mock_resize, tmp_path):
"""Test batch resize with some failures."""
mock_resize.side_effect = [True, False, True]
input_images = [
tmp_path / "image1.jpg",
tmp_path / "image2.jpg",
tmp_path / "image3.jpg"
]
for img in input_images:
img.touch()
output_dir = tmp_path / "output"
success, fail = self.resizer.batch_resize(
input_images,
output_dir,
width=800,
height=None
)
assert success == 2
assert fail == 1
@patch.object(ImageResizer, "resize_image")
def test_batch_resize_format_conversion(self, mock_resize, tmp_path):
"""Test batch resize with format conversion."""
mock_resize.return_value = True
input_images = [tmp_path / "image.png"]
input_images[0].touch()
output_dir = tmp_path / "output"
self.resizer.batch_resize(
input_images,
output_dir,
width=800,
height=None,
format_ext="jpg"
)
# Check that resize_image was called with .jpg extension
call_args = mock_resize.call_args[0]
assert call_args[1].suffix == ".jpg"
class TestResizeStrategies:
"""Test different resize strategies."""
def setup_method(self):
"""Set up test fixtures."""
self.resizer = ImageResizer()
def test_fit_strategy_maintains_aspect(self):
"""Test that 'fit' strategy maintains aspect ratio."""
cmd = self.resizer.build_resize_command(
Path("input.jpg"),
Path("output.jpg"),
width=800,
height=600,
strategy="fit",
quality=85
)
# Should have resize without ^ or !
resize_idx = cmd.index("-resize")
geometry = cmd[resize_idx + 1]
assert "^" not in geometry
assert "!" not in geometry
def test_cover_strategy_fills_dimensions(self):
"""Test that 'cover' strategy fills dimensions."""
cmd = self.resizer.build_resize_command(
Path("input.jpg"),
Path("output.jpg"),
width=800,
height=600,
strategy="cover",
quality=85
)
resize_idx = cmd.index("-resize")
geometry = cmd[resize_idx + 1]
assert "^" in geometry
def test_exact_strategy_ignores_aspect(self):
"""Test that 'exact' strategy ignores aspect ratio."""
cmd = self.resizer.build_resize_command(
Path("input.jpg"),
Path("output.jpg"),
width=800,
height=600,
strategy="exact",
quality=85
)
resize_idx = cmd.index("-resize")
geometry = cmd[resize_idx + 1]
assert "!" in geometry
if __name__ == "__main__":
pytest.main([__file__, "-v"])
#!/usr/bin/env python3
"""Tests for media_convert.py"""
import sys
from pathlib import Path
from unittest.mock import MagicMock, patch
import pytest
# Add parent directory to path
sys.path.insert(0, str(Path(__file__).parent.parent))
from media_convert import (
build_audio_command,
build_image_command,
build_video_command,
check_dependencies,
convert_file,
detect_media_type,
)
class TestMediaTypeDetection:
"""Test media type detection."""
def test_detect_video_formats(self):
"""Test video format detection."""
assert detect_media_type(Path("test.mp4")) == "video"
assert detect_media_type(Path("test.mkv")) == "video"
assert detect_media_type(Path("test.avi")) == "video"
assert detect_media_type(Path("test.mov")) == "video"
def test_detect_audio_formats(self):
"""Test audio format detection."""
assert detect_media_type(Path("test.mp3")) == "audio"
assert detect_media_type(Path("test.aac")) == "audio"
assert detect_media_type(Path("test.flac")) == "audio"
assert detect_media_type(Path("test.wav")) == "audio"
def test_detect_image_formats(self):
"""Test image format detection."""
assert detect_media_type(Path("test.jpg")) == "image"
assert detect_media_type(Path("test.png")) == "image"
assert detect_media_type(Path("test.gif")) == "image"
assert detect_media_type(Path("test.webp")) == "image"
def test_detect_unknown_format(self):
"""Test unknown format detection."""
assert detect_media_type(Path("test.txt")) == "unknown"
assert detect_media_type(Path("test.doc")) == "unknown"
def test_case_insensitive(self):
"""Test case-insensitive detection."""
assert detect_media_type(Path("TEST.MP4")) == "video"
assert detect_media_type(Path("TEST.JPG")) == "image"
class TestCommandBuilding:
"""Test command building functions."""
def test_build_video_command_web_preset(self):
"""Test video command with web preset."""
cmd = build_video_command(
Path("input.mp4"),
Path("output.mp4"),
preset="web"
)
assert "ffmpeg" in cmd
assert "-i" in cmd
assert str(Path("input.mp4")) in cmd
assert "-c:v" in cmd
assert "libx264" in cmd
assert "-crf" in cmd
assert "23" in cmd
assert "-preset" in cmd
assert "medium" in cmd
assert str(Path("output.mp4")) in cmd
def test_build_video_command_archive_preset(self):
"""Test video command with archive preset."""
cmd = build_video_command(
Path("input.mp4"),
Path("output.mp4"),
preset="archive"
)
assert "18" in cmd # CRF for archive
assert "slow" in cmd # Preset for archive
def test_build_audio_command_mp3(self):
"""Test audio command for MP3 output."""
cmd = build_audio_command(
Path("input.wav"),
Path("output.mp3"),
preset="web"
)
assert "ffmpeg" in cmd
assert "-c:a" in cmd
assert "libmp3lame" in cmd
assert "-b:a" in cmd
def test_build_audio_command_flac(self):
"""Test audio command for FLAC (lossless)."""
cmd = build_audio_command(
Path("input.wav"),
Path("output.flac"),
preset="web"
)
assert "flac" in cmd
assert "-b:a" not in cmd # No bitrate for lossless
def test_build_image_command(self):
"""Test image command building."""
cmd = build_image_command(
Path("input.png"),
Path("output.jpg"),
preset="web"
)
assert "magick" in cmd
assert str(Path("input.png")) in cmd
assert "-quality" in cmd
assert "85" in cmd
assert "-strip" in cmd
assert str(Path("output.jpg")) in cmd
class TestDependencyCheck:
"""Test dependency checking."""
@patch("subprocess.run")
def test_check_dependencies_both_available(self, mock_run):
"""Test when both tools are available."""
mock_run.return_value = MagicMock(returncode=0)
ffmpeg_ok, magick_ok = check_dependencies()
assert ffmpeg_ok is True
assert magick_ok is True
@patch("subprocess.run")
def test_check_dependencies_ffmpeg_only(self, mock_run):
"""Test when only FFmpeg is available."""
def side_effect(*args, **kwargs):
if "ffmpeg" in args[0]:
return MagicMock(returncode=0)
return MagicMock(returncode=1)
mock_run.side_effect = side_effect
ffmpeg_ok, magick_ok = check_dependencies()
assert ffmpeg_ok is True
assert magick_ok is False
class TestFileConversion:
"""Test file conversion functionality."""
@patch("subprocess.run")
@patch("media_convert.detect_media_type")
def test_convert_video_file_dry_run(self, mock_detect, mock_run):
"""Test video conversion in dry-run mode."""
mock_detect.return_value = "video"
result = convert_file(
Path("input.mp4"),
Path("output.mp4"),
preset="web",
dry_run=True
)
assert result is True
mock_run.assert_not_called()
@patch("subprocess.run")
@patch("media_convert.detect_media_type")
def test_convert_image_file_success(self, mock_detect, mock_run):
"""Test successful image conversion."""
mock_detect.return_value = "image"
mock_run.return_value = MagicMock(returncode=0)
result = convert_file(
Path("input.png"),
Path("output.jpg"),
preset="web"
)
assert result is True
mock_run.assert_called_once()
@patch("subprocess.run")
@patch("media_convert.detect_media_type")
def test_convert_file_error(self, mock_detect, mock_run):
"""Test conversion error handling."""
mock_detect.return_value = "video"
mock_run.side_effect = Exception("Conversion failed")
result = convert_file(
Path("input.mp4"),
Path("output.mp4")
)
assert result is False
@patch("media_convert.detect_media_type")
def test_convert_unknown_format(self, mock_detect):
"""Test conversion with unknown format."""
mock_detect.return_value = "unknown"
result = convert_file(
Path("input.txt"),
Path("output.txt")
)
assert result is False
class TestQualityPresets:
"""Test quality preset functionality."""
def test_web_preset_settings(self):
"""Test web preset values."""
cmd = build_video_command(
Path("input.mp4"),
Path("output.mp4"),
preset="web"
)
cmd_str = " ".join(cmd)
assert "23" in cmd_str # CRF
assert "128k" in cmd_str # Audio bitrate
def test_archive_preset_settings(self):
"""Test archive preset values."""
cmd = build_video_command(
Path("input.mp4"),
Path("output.mp4"),
preset="archive"
)
cmd_str = " ".join(cmd)
assert "18" in cmd_str # Higher quality CRF
assert "192k" in cmd_str # Higher audio bitrate
def test_mobile_preset_settings(self):
"""Test mobile preset values."""
cmd = build_video_command(
Path("input.mp4"),
Path("output.mp4"),
preset="mobile"
)
cmd_str = " ".join(cmd)
assert "26" in cmd_str # Lower quality CRF
assert "96k" in cmd_str # Lower audio bitrate
if __name__ == "__main__":
pytest.main([__file__, "-v"])
Related skills
FAQ
Which tools does media-processing use?
FFmpeg for video and audio encoding, ImageMagick for image manipulation, and RMBG for AI-powered background removal.
Which tool should I use for still images?
ImageMagick, which is optimized for still images including batch resizing via mogrify.