
Media Processing
- 34 installs
- 7 repo stars
- Updated June 18, 2026
- duc01226/easyplatform
Processes multimedia files using FFmpeg, ImageMagick, or AI background-removal tools.
About
A skill that processes audio, image, and video files with FFmpeg, ImageMagick, and AI background removal. A developer uses it to automate media conversion and editing tasks.
- FFmpeg and ImageMagick processing
- AI background removal
Media Processing by the numbers
- 34 all-time installs (skills.sh)
- Ranked #949 of 1,335 Generative Media skills by installs in the Skillselion catalog
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/duc01226/easyplatform --skill media-processingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 34 |
|---|---|
| repo stars | ★ 7 |
| Last updated | June 18, 2026 |
| Repository | duc01226/easyplatform ↗ |
What it does
Processes multimedia files using FFmpeg, ImageMagick, or AI background-removal tools.
Files
Codex compatibility note:
>
- Invoke repository skills with$skill-namein Codex; this mirrored copy rewrites legacy Claude/skill-namereferences.
- Task tracker mandate: BEFORE executing any workflow or skill step, create/update task tracking for all steps and keep it synchronized as progress changes.
- User-question prompts mean to ask the user directly in Codex.
- Ignore Claude-specific mode-switch instructions when they appear.
- Strict execution contract: when a user explicitly invokes a skill, execute that skill protocol as written.
- Subagent authorization: when a skill is user-invoked or AI-detected and its protocol requires subagents, that skill activation authorizes use of the required spawn_agent subagent(s) for that task.- Do not skip, reorder, or merge protocol steps unless the user explicitly approves the deviation first.
- For workflow skills, execute each listed child-skill step explicitly and report step-by-step evidence.
- If a required step/tool cannot run in this environment, stop and ask the user before adapting.
<!-- CODEX:PROJECT-REFERENCE-LOADING:START -->
Codex Project-Reference Loading (No Hooks)
Codex does not receive Claude hook-based doc injection. When coding, planning, debugging, testing, or reviewing, open project docs explicitly using this routing.
Always read:
docs/project-config.json(project-specific paths, commands, modules, and workflow/test settings)docs/project-reference/docs-index-reference.md(routes to the fulldocs/project-reference/*catalog)docs/project-reference/lessons.md(always-on guardrails and anti-patterns)
Situation-based docs:
- Backend/CQRS/API/domain/entity changes:
backend-patterns-reference.md,domain-entities-reference.md,project-structure-reference.md - Frontend/UI/styling/design-system:
frontend-patterns-reference.md,scss-styling-guide.md,design-system/README.md - Spec/test-case planning or TC mapping:
feature-docs-reference.md - Integration test implementation/review:
integration-test-reference.md - E2E test implementation/review:
e2e-test-reference.md - Code review/audit work:
code-review-rules.mdplus domain docs above based on changed files
Do not read all docs blindly. Start from docs-index-reference.md, then open only relevant files for the task.
<!-- CODEX:PROJECT-REFERENCE-LOADING:END -->
Quick Summary
Goal: Process multimedia files using FFmpeg for video/audio encoding, conversion, streaming, and filtering.
Workflow:
1. Identify -- Match input to correct FFmpeg operation (convert, trim, merge, compress) 2. Execute -- Run FFmpeg command with appropriate codec and quality settings 3. Verify -- Check output file integrity and quality
Key Rules:
- Use tool selection table to pick correct FFmpeg operation
- Prefer hardware-accelerated encoding when available
- Always verify output file exists and is playable
Be skeptical. Apply critical thinking, sequential thinking. Every claim needs traced proof, confidence percentages (Idea should be more than 80%).
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
---
[IMPORTANT] Use task tracking to break ALL work into small tasks BEFORE starting — including tasks for each file read. This prevents context loss from long files. For simple tasks, AI MUST ATTENTION ask user whether to skip.
<!-- SYNC:ai-mistake-prevention -->
AI Mistake Prevention — Failure modes to avoid on every task:
>
Check downstream references before deleting. Deleting components causes documentation and code staleness cascades. Map all referencing files before removal.
Verify AI-generated content against actual code. AI hallucinates APIs, class names, and method signatures. Always grep to confirm existence before documenting or referencing.
Trace full dependency chain after edits. Changing a definition misses downstream variables and consumers derived from it. Always trace the full chain.
Trace ALL code paths when verifying correctness. Confirming code exists is not confirming it executes. Always trace early exits, error branches, and conditional skips — not just happy path.
When debugging, ask "whose responsibility?" before fixing. Trace whether bug is in caller (wrong data) or callee (wrong handling). Fix at responsible layer — never patch symptom site.
Assume existing values are intentional — ask WHY before changing. Before changing any constant, limit, flag, or pattern: read comments, check git blame, examine surrounding code.
Verify ALL affected outputs, not just the first. Changes touching multiple stacks require verifying EVERY output. One green check is not all green checks.
Holistic-first debugging — resist nearest-attention trap. When investigating any failure, list EVERY precondition first (config, env vars, DB names, endpoints, DI registrations, data preconditions), then verify each against evidence before forming any code-layer hypothesis.
Surgical changes — apply the diff test. Bug fix: every changed line must trace directly to the bug. Don't restyle or improve adjacent code. Enhancement task: implement improvements AND announce them explicitly.
Surface ambiguity before coding — don't pick silently. If request has multiple interpretations, present each with effort estimate and ask. Never assume all-records, file-based, or more complex path.
<!-- /SYNC:ai-mistake-prevention -->
<!-- SYNC:critical-thinking-mindset -->
Critical Thinking Mindset — Apply critical thinking, sequential thinking. Every claim needs traced proof, confidence >80% to act.
Anti-hallucination: Never present guess as fact — cite sources for every claim, admit uncertainty freely, self-check output for errors, cross-reference independently, stay skeptical of own confidence — certainty without evidence root of all hallucination.
<!-- /SYNC:critical-thinking-mindset -->
<!-- SYNC:critical-thinking-mindset:reminder -->
MUST ATTENTION apply critical thinking — every claim needs traced proof, confidence >80% to act. Anti-hallucination: never present guess as fact.
<!-- /SYNC:critical-thinking-mindset:reminder -->
<!-- SYNC:ai-mistake-prevention:reminder -->
MUST ATTENTION apply AI mistake prevention — holistic-first debugging, fix at responsible layer, surface ambiguity before coding, re-read files after compaction.
<!-- /SYNC:ai-mistake-prevention:reminder -->
Closing Reminders
- MANDATORY IMPORTANT MUST ATTENTION break work into small todo tasks using task tracking BEFORE starting
- MANDATORY IMPORTANT MUST ATTENTION search codebase for 3+ similar patterns before creating new code
- MANDATORY IMPORTANT MUST ATTENTION cite
file:lineevidence for every claim (confidence >80% to act) - MANDATORY IMPORTANT MUST ATTENTION add a final review todo task to verify work quality
[TASK-PLANNING] Before acting, analyze task scope and systematically break it into small todo tasks and sub-tasks using task tracking.
<!-- CODEX:SYNC-PROMPT-PROTOCOLS:START -->
Hookless Prompt Protocol Mirror (Auto-Synced)
Source: .claude/hooks/lib/prompt-injections.cjs + .claude/.ck.json
[WORKFLOW-EXECUTION-PROTOCOL] [BLOCKING] Workflow Execution Protocol — MANDATORY IMPORTANT MUST CRITICAL. Do not skip for any reason.
Generic portability boundary: Reusable skills and protocol text stay project-neutral; project-specific conventions are discovered from docs/project-config.json and docs/project-reference/. Apply shared AI-SDD from shared/sdd-artifact-contract.md. Read docs/project-config.json and docs/project-reference/docs-index-reference.md, then open the project reference docs named there. Any supported AI tool may execute when this shared context and local docs are available.
1. DETECT: Match prompt against workflow catalog 2. ANALYZE: Find best-match workflow AND evaluate if a custom step combination would fit better 3. ASK (REQUIRED FORMAT): Use a direct user question with this structure unless the user explicitly invoked a workflow/skill and the local protocol treats explicit invocation as confirmation:
- Question: "Which workflow do you want to activate?"
- Option 1: "Activate [BestMatch Workflow] (Recommended)"
- Option 2: "Activate custom workflow: [step1 → step2 → ...]" (include one-line rationale)
4. ACTIVATE (if confirmed): Call $workflow-start <workflowId> for standard; sequence custom steps manually 5. CREATE TASKS: task tracking for ALL workflow steps 6. EXECUTE: Follow each step in sequence [CRITICAL-THINKING-MINDSET] Apply critical thinking, sequential thinking. Every claim needs traced proof, confidence >80% to act. Anti-hallucination principle: Never present guess as fact — cite sources for every claim, admit uncertainty freely, self-check output for errors, cross-reference independently, stay skeptical of own confidence — certainty without evidence root of all hallucination. AI Attention principle (Primacy-Recency): Put the 3 most critical rules at both top and bottom of long prompts/protocols so instruction adherence survives long context windows. Goal-driven execution: Define success criteria first, loop until verified, and stop only when observable checks pass. Tests verify intent: Tests must protect business rules/invariants and fail when the protected intent breaks, not only mirror current behavior.
[LESSON-LEARNED-REMINDER] [BLOCKING] Task Planning & Continuous Improvement — MANDATORY. Do not skip.
Break work into small tasks (task tracking) before starting. Add final task: "Analyze AI mistakes & lessons learned".
Extract lessons — ROOT CAUSE ONLY, not symptom fixes:
1. Name the FAILURE MODE (reasoning/assumption failure), not symptom — "assumed API existed without reading source" not "used wrong enum value". 2. Generality test: does this failure mode apply to ≥3 contexts/codebases? If not, abstract one level up. 3. Write as a universal rule — strip project-specific names/paths/classes. Useful on any codebase. 4. Consolidate: multiple mistakes sharing one failure mode → ONE lesson. 5. Recurrence gate: "Would this recur in future session WITHOUT this reminder?" — No → skip $learn. 6. Auto-fix gate: "Could $code-review/$code-simplifier/$security/$lint catch this?" — Yes → improve review skill instead. 7. BOTH gates pass → ask user to run $learn. [TASK-PLANNING] [MANDATORY] BEFORE executing any workflow or skill step, create/update task tracking for all planned steps, then keep it synchronized as each step starts/completes.
<!-- CODEX:SYNC-PROMPT-PROTOCOLS:END -->
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, processing) => {
const percent = Math.round(progress * 100);
const downloadPercent = Math.round(download * 100);
const processPercent = Math.round(processing * 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
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"])