High-performance motion detection utility for comparing JPEG images. Optimized for Pi Zero with ARM-safe image loading and decode-time scaling.
- Pi Zero optimized - ARM-safe libjpeg-turbo instead of problematic stb_image
- Decode-time scaling - images scaled during JPEG decode (much more efficient)
- Automatic Pi Zero protection - large images auto-scaled to prevent crashes
- Memory efficient - up to 16x memory reduction with scale factor 4
- Ultra-fast processing - optimized for video surveillance and real-time analysis
- File size pre-check - ultra-fast motion detection based on file size changes
- Flexible thresholds - pixel-level and percentage-based motion detection
- Grayscale processing - 3x faster analysis option
- Fast blur filter - noise reduction with optimized separable filtering
# Build
make
# Basic usage
./motion-detector image1.jpg image2.jpg
# With options
./motion-detector frame1.jpg frame2.jpg -t 30 -s 2 -b -v
- libjpeg-turbo development headers
# On Pi Zero/Debian/Ubuntu:
sudo apt update
sudo apt install libjpeg-turbo8-dev
# On macOS:
brew install jpeg-turbo
# Compile
make
# Install to system (optional)
make install
# Test Pi Zero compatibility
make test-pi
./motion-detector <image1> <image2> [options]
Option | Description | Default |
---|---|---|
-t <threshold> |
Pixel sensitivity: How different pixels must be to count as "changed" (0-255). Lower = more sensitive | 25 |
-s <scale> |
Decode scale factor: 1=full, 2=half, 4=quarter, 8=eighth (JPEG scaled during decode!) | 1 |
-m <motion_pct> |
Motion percentage threshold | 1.0 |
-f [threshold] |
File size mode: Ultra-fast pre-check based on file size changes (threshold as number, default: 5) | 5 |
-rgb |
RGB mode: Use RGB instead of grayscale (slower but more accurate) | - |
-u |
Ultra-fast mode: fastest IDCT + upsampling (15-25% faster, lower quality) | - |
-b |
Blur mode: Apply fast blur for noise reduction (separable filter) | - |
-v |
Verbose output: Detailed statistics with timing breakdown | - |
The pixel threshold controls how sensitive motion detection is:
- Value range: 0-255 (brightness difference between pixels)
- Lower values (5-15): Very sensitive - detects small changes, lighting shifts, noise
- Medium values (20-35): Balanced - detects clear movement, ignores minor changes
- Higher values (40-80): Less sensitive - only detects major movements
Examples:
# Very sensitive - detects even small lighting changes
./motion-detector img1.jpg img2.jpg -t 10
# Default sensitivity - good for most cases
./motion-detector img1.jpg img2.jpg -t 25
# Less sensitive - only major movements
./motion-detector img1.jpg img2.jpg -t 50
How it works: Each pixel is compared between images. If the brightness difference exceeds the threshold, it's counted as "changed". More changed pixels = more motion detected.
The blur filter helps reduce false positives from image noise and compression artifacts:
- Separable filtering: Optimized horizontal + vertical passes (5x faster than standard blur)
- Grayscale optimization: In grayscale mode, converts to grayscale first then blurs only 1 channel
- Noise reduction: Smooths out JPEG compression artifacts and sensor noise
- Real-time friendly: Only 2x slowdown on Pi Zero (vs 8x with naive implementation)
When to use blur:
- Security cameras: Reduces false alarms from compression artifacts
- Low light: Minimizes noise-induced false positives
- High sensitivity: When using low thresholds (-t 5-15)
- Poor quality images: JPEG artifacts and noise cleanup
Examples:
# Security camera with noise reduction
./motion-detector cam_prev.jpg cam_curr.jpg -t 15 -b
# High sensitivity with blur to prevent false positives
./motion-detector img1.jpg img2.jpg -t 10 -b -s 2
# Ultra-fast with blur on Pi Zero
./motion-detector frame1.jpg frame2.jpg -s 4 -u -b
# Basic motion detection (auto-optimized for Pi Zero)
./motion-detector prev.jpg curr.jpg
# High sensitivity with RGB processing
./motion-detector frame1.jpg frame2.jpg -t 15 -rgb
# Fast processing with 1/4 scale (16x memory reduction)
./motion-detector large1.jpg large2.jpg -s 4
# Ultra-fast processing with 1/8 scale (64x memory reduction)
./motion-detector vid1.jpg vid2.jpg -s 8
# Ultra-fast decode with fastest IDCT + upsampling
./motion-detector img1.jpg img2.jpg -u
# Ultra-fast file size check
./motion-detector cam1.jpg cam2.jpg -f
# Extreme speed: ultra-fast + quarter scale
./motion-detector large1.jpg large2.jpg -u -s 4
# Detailed analysis with timing
./motion-detector img1.jpg img2.jpg -v -t 20
# With blur for noise reduction
./motion-detector noisy1.jpg noisy2.jpg -b -t 15
# Pi Zero optimized for FullHD images
./motion-detector hd1.jpg hd2.jpg -s 4 -v
# Use in scripts
if ./motion-detector img1.jpg img2.jpg -s 2; then
echo "Motion detected!"
fi
# Capture percentage
result=$(./motion-detector img1.jpg img2.jpg -v | grep "Motion:" | cut -d' ' -f2)
- Default mode: Outputs
1
(motion detected) or0
(no motion) - Verbose mode (
-v
): Detailed statistics and percentages - Exit codes:
0
= no motion,1
= motion detected,2
= error
Images are scaled during JPEG decode - much more efficient than pixel skipping:
-s 1
: Full resolution (default)-s 2
: Half resolution (4x less memory, 2x faster)-s 4
: Quarter resolution (16x less memory, 4x faster)-s 8
: Eighth resolution (64x less memory, 8x faster)
Large images are automatically scaled to prevent crashes:
- Images > 1280x720 → automatically scaled to 1/2 during decode
- Prevents segfaults while maintaining functionality
- Decode scaling (
-s
): Real memory reduction during JPEG decode - Grayscale (default): 3x faster than RGB, use
-rgb
to enable RGB - Ultra-fast mode (
-u
): Fastest IDCT + upsampling (15-25% faster, lower quality) - Blur filter (
-b
): Noise reduction with separable filtering (2x slowdown, better accuracy) - File size (
-f
): ~1000x faster than pixel analysis - Verbose (
-v
): Detailed timing breakdown and statistics
Ultra-fast motion detection based on file size changes:
- Parameter format:
-f [threshold]
where threshold is a number (default: 5) - Threshold meaning: Percentage difference in file sizes to trigger motion detection
- Speed: ~1 microsecond vs ~1 millisecond for full pixel analysis
Examples:
# Use default threshold (5% file size difference)
./motion-detector prev.jpg curr.jpg -f
# Custom threshold: 5% file size difference
./motion-detector prev.jpg curr.jpg -f 5
# More sensitive: 2% file size difference
./motion-detector cam1.jpg cam2.jpg -f 2
# Less sensitive: 10% file size difference
./motion-detector stable1.jpg stable2.jpg -f 10
How it works: Compares file sizes and calculates percentage difference. If the difference exceeds the threshold (as percentage), motion is detected. Much faster than pixel analysis but less accurate.
Scale Factor | Memory Usage | Load Time | Motion Time | Total Time | Final Size |
---|---|---|---|---|---|
1x (full) | 900 KB | 1.54 ms | 0.42 ms | 1.96 ms | 640x480 |
1x + ultra | 900 KB | 1.29 ms | 0.36 ms | 1.65 ms | 640x480 |
2x (half) | 225 KB | 0.87 ms | 0.07 ms | 0.95 ms | 320x240 |
4x (quarter) | 56 KB | 0.89 ms | 0.03 ms | 0.92 ms | 160x120 |
4x + ultra | 56 KB | 0.68 ms | 0.02 ms | 0.70 ms | 160x120 |
- Decode scaling: 16x memory reduction with 4x scale, real memory savings
- Ultra-fast mode: 15-25% speed boost with fastest IDCT + upsampling
- No segfaults: ARM-safe libjpeg-turbo instead of problematic stb_image
- Real scaling: Images actually smaller in memory, not just pixel skipping
- Pi Zero ready: Automatic protection for large images
This version is specifically optimized for Pi Zero and ARM systems:
- ARM-safe: Uses libjpeg-turbo instead of problematic stb_image
- Memory efficient: Automatic scaling for large images
- No segfaults: Tested and stable on Pi Zero hardware
- Real-time capable: Fast enough for video surveillance
- JPEG: Full support with decode-time scaling
- Other formats: Not supported (JPEG focus for Pi Zero optimization)
git clone <repository>
cd motion-detector
make install-deps # Auto-install dependencies
make # Build
./motion-detector image1.jpg image2.jpg
Ubuntu/Debian/Pi Zero:
git clone <repository>
cd motion-detector
# Auto-install dependencies
make install-deps
# Or manual install:
# sudo apt update
# sudo apt install libjpeg-turbo8-dev build-essential pkg-config
make
./test_pi_zero.sh # Test compatibility
CentOS/RHEL/Fedora:
git clone <repository>
cd motion-detector
# Auto-install dependencies
make install-deps
# Or manual install:
# sudo yum install libjpeg-turbo-devel gcc-c++ make pkg-config
# sudo dnf install libjpeg-turbo-devel gcc-c++ make pkg-config # Fedora
make
macOS (Homebrew):
brew install jpeg-turbo
git clone <repository>
cd motion-detector
make
Static build (Linux/Pi Zero only):
# For deployment without dependencies
make static
# Pi Zero optimized static build
make pi-zero
Manual installation (if pkg-config fails):
# Install libjpeg-turbo manually, then:
make JPEG_LIBS="-ljpeg" JPEG_CFLAGS="-I/usr/include"
Quick fix for all dependency issues:
# Auto-install dependencies for your system
make install-deps
# Check if everything is installed correctly
make check-deps
Error: Package libjpeg was not found
or jpeglib.h: No such file or directory
# Step 1: Check what's missing
make check-deps
# Step 2: Auto-install (recommended)
make install-deps
# Step 3: Manual install if needed
# Ubuntu/Debian:
sudo apt install libjpeg-turbo8-dev pkg-config
# CentOS/RHEL:
sudo yum install libjpeg-turbo-devel pkg-config
# Fedora:
sudo dnf install libjpeg-turbo-devel pkg-config
# macOS:
brew install jpeg-turbo
Still not working? Manual path override:
# Find libjpeg installation:
find /usr -name "jpeglib.h" 2>/dev/null
find /opt -name "jpeglib.h" 2>/dev/null
# Compile with manual path:
make JPEG_CFLAGS="-I/path/to/include" JPEG_LIBS="-ljpeg"
Target | Description | Platform |
---|---|---|
make |
Standard build with dynamic linking | All |
make static |
Static build (no dependencies) | Linux/Pi Zero only |
make pi-zero |
Pi Zero optimized static build | Pi Zero/ARM |
make debug |
Debug build with symbols | All |
make clean |
Clean build artifacts | All |
make install |
Install to system | Linux/macOS |
make test-pi |
Run Pi Zero compatibility tests | All |
make check-deps |
Check if dependencies are installed | All |
make install-deps |
Auto-install dependencies | Linux/macOS |
Static builds include all dependencies and can run on systems without libjpeg-turbo installed:
# On build machine (with libjpeg-turbo-dev):
make static
# Copy to target Pi Zero:
scp motion-detector-static pi@raspberrypi:~/motion-detector
# Run on Pi Zero (no dependencies needed):
./motion-detector image1.jpg image2.jpg
Pi Zero optimized build:
# Cross-compile for Pi Zero (on x86_64):
make pi-zero
# Or compile directly on Pi Zero:
make static
#!/bin/bash
# Motion detection pipeline
for i in {1..100}; do
if ./motion-detector frame$((i-1)).jpg frame$i.jpg -g -s 4; then
echo "Motion in frame $i"
# Process motion event
fi
done
import subprocess
import sys
def detect_motion(img1, img2, threshold=25):
result = subprocess.run([
'./motion-detector', img1, img2,
'-t', str(threshold), '-g'
], capture_output=True)
return result.returncode == 1
# Usage
if detect_motion('prev.jpg', 'curr.jpg'):
print("Motion detected!")
# Use static build for reliable deployment
*/5 * * * * /home/pi/motion-detector-static /home/pi/cam/prev.jpg /home/pi/cam/curr.jpg -s 4 && echo "Motion detected at $(date)" >> /var/log/motion.log
# Add to crontab for periodic checking
* * * * * /path/to/motion-detector /tmp/prev.jpg /tmp/curr.jpg -f && /path/to/alert.sh
# Default universal build
make
# Static version (no dependencies) - Linux only
make static
# Pi Zero debug version (troubleshooting)
make pi-debug
# Pi Zero static debug version - Linux only
make pi-debug-static
# Development build with debug symbols
make debug
# Clean build files
make clean
For Pi Zero deployment, use the static builds from GitHub Actions to avoid library version conflicts:
- Download the static binary from GitHub Actions artifacts:
motion-detector-pi-zero-static
- Copy to Pi Zero:
scp motion-detector-pi-zero-static pi@doorcamera:~/motion-detector
- Make executable:
chmod +x motion-detector
- Run without any library dependencies
Note: Static linking is only available on Linux. macOS builds are dynamic only.
- JPEG (.jpg, .jpeg) - optimized processing
- PNG (.png) - full support
- BMP (.bmp) - basic support
- Other formats - via stb_image library
- Use grayscale (
-g
) for 3x speed boost - Scale down (
-s 2
or-s 4
) for faster processing - File size mode (
-f
) for ultra-fast detection - Appropriate thresholds - lower = more sensitive
- Blur filtering (
-b
) for noisy images
- Memory safe: Automatic bounds checking and safe allocations
- Error handling: Graceful fallbacks for unsupported operations
- Cross-platform: Works on x86, ARM, and embedded systems
- Optimized: Smart caching and processing optimizations
- Lightweight: Single binary with minimal dependencies
MIT License - see LICENSE file for details.
- Fork the repository
- Create your feature branch
- Make your changes
- Test on multiple platforms
- Submit a pull request
The project automatically builds and tests on multiple platforms via GitHub Actions.