๐ Introduction: Why Compress Images with PowerShell ?
We all face this scenario:
-
๐ You need to send photos via email—but attachments are too large.
-
๐ You want to upload dozens of images to a shared drive quickly.
-
๐ผ️ You build a presentation, and images bloat your file size.
Traditional image editors are fine—but slow if you're dealing with many files. PowerShell lets you:
-
Batch-compress dozens or hundreds of images.
-
Automate compression directly in your file system.
-
Customize output quality, size, and format—all via script.
This guide shows you slim, clear, powerful ways to compress images using PowerShell in Windows 11—no third-party apps needed.
1️⃣ Understanding Image Compression Basics
Before coding, know your compression methods:
-
๐️ Lossy compression: Reduces file size by removing image detail (e.g., JPEG quality 80 → smaller output).
-
๐️ Lossless compression: Reorganizes data without losing quality (e.g., PNG compression).
In PowerShell, common approaches:
-
Use .NET’s System.Drawing.Image class for quality-based JPEG compression.
-
Use Windows Imaging Component (WIC) via COM objects for PNG optimization.
-
Use PowerShell wrappers for built-in COM or .NET methods.
๐ฏ Goal: Scripts that take a folder of images and produce a compressed version with minimal quality loss.
2️⃣ Set Up: Prepping PowerShell Environment
Ensure your PowerShell is ready:
-
Open PowerShell (admin): Press Win + S, type
PowerShell
, right-click and Run as administrator. -
Verify version: Run:
$PSVersionTable.PSVersion
Make sure you're running PowerShell 5.1+—any version in Windows 11 works fine.
-
Enable scripting (if needed):
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Confirm with Y.
With PowerShell ready, let’s start compressing!
3️⃣ Trick 1: Compress a Single JPEG Using .NET
This is your base trick:
function Compress-Jpeg {
param(
[string]$InputPath,
[string]$OutputPath,
[int]$Quality = 75
)
Add-Type -AssemblyName System.Drawing
$img = [System.Drawing.Image]::FromFile($InputPath)
$jpegEncoder = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() |
Where-Object { $_.MimeType -eq "image/jpeg" }
$encoderParams = New-Object System.Drawing.Imaging.EncoderParameters(1)
$encoderParams.Param[0] = [System.Drawing.Imaging.EncoderParameter] `
([System.Drawing.Imaging.Encoder]::Quality, $Quality)
$img.Save($OutputPath, $jpegEncoder, $encoderParams)
$img.Dispose()
}
๐ How it works:
-
Loads the image via
System.Drawing.Image
. -
Finds JPEG encoder.
-
Sets quality parameter.
-
Saves compressed output.
๐ Usage example:
Compress-Jpeg -InputPath "C:\Images\pic1.jpg" `
-OutputPath "C:\Compressed\pic1_small.jpg" `
-Quality 60
This trick is fast and easy for single files.
4️⃣ Trick 2: Batch Compress a Folder of JPEGs
Automate compression across folders:
function Compress-JpegFolder {
param(
[string]$SourceFolder,
[string]$DestFolder,
[int]$Quality = 75
)
if (-Not (Test-Path $DestFolder)) { New-Item -ItemType Directory -Path $DestFolder }
Get-ChildItem $SourceFolder -Include *.jpg,*.jpeg -Recurse | ForEach-Object {
$destPath = Join-Path $DestFolder $_.FullName.Substring($SourceFolder.Length + 1)
$dir = Split-Path $destPath
if (-Not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force }
Compress-Jpeg -InputPath $_.FullName -OutputPath $destPath -Quality $Quality
Write-Host "Compressed" $_.Name "→" $destPath
}
}
๐ Usage example:
Compress-JpegFolder -SourceFolder "C:\Images" `
-DestFolder "C:\Compressed" `
-Quality 70
This recursively processes JPEG images, protecting original images and mirroring folder structure.
5️⃣ Trick 3: Preserve Original Metadata (EXIF) After Compression
By default, metadata might be lost—let's preserve it:
function Compress-JpegWithMetadata {
param($InputPath, $OutputPath, $Quality = 75)
Add-Type -AssemblyName System.Drawing
$img = [System.Drawing.Image]::FromFile($InputPath)
$encoder = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() |
Where-Object { $_.MimeType -eq "image/jpeg" }
$encoderParams = New-Object System.Drawing.Imaging.EncoderParameters(1)
$encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter(
[System.Drawing.Imaging.Encoder]::Quality, $Quality
)
$newImg = New-Object System.Drawing.Bitmap $img
foreach ($p in $img.PropertyItems) {
$newImg.SetPropertyItem($p)
}
$newImg.Save($OutputPath, $encoder, $encoderParams)
$img.Dispose(); $newImg.Dispose()
}
๐ง Metadata like GPS, camera data, or thumbnails is retained.
Use this function inside your folder routine for metadata safety.
6️⃣ Trick 4: Compress PNGs with Lossless Optimization
For PNGs, adjust compression rather than quality:
function Compress-Png {
param($InputPath, $OutputPath)
$wicFactory = New-Object -ComObject WIA.ImageFile
try {
$image = $wicFactory.LoadFile($InputPath)
$encoder = New-Object -ComObject WIA.ImageProcess
$encoder.Filters.Add($encoder.FilterInfos["Convert"].FilterID)
$encoder.Filters.Item(1).Properties["FormatID"].Value = "{B96B3CAB-0728-11D3-9D7B-0000F81EF32E}" # PNG
$encoder.Filters.Item(1).Properties["Quality"].Value = 90
$result = $encoder.Apply($image)
$result.SaveFile($OutputPath)
} catch {
Write-Warning "Failed to compress PNG: $_"
}
}
๐ Usage:
Compress-Png "C:\Images\logo.png" "C:\Compressed\logo_small.png"
This preserves visual quality, suitable for screenshots, logos, and icons.
7️⃣ Trick 5: Batch Compress Both JPEG and PNG in a Folder
Combine functions into a clean routine:
function Compress-Images {
param($SourceFolder, $DestFolder, [int]$JpegQuality = 75)
if (-not (Test-Path $DestFolder)) { New-Item Directory $DestFolder }
Get-ChildItem $SourceFolder -Recurse -File | ForEach-Object {
$ext = $_.Extension.ToLower()
$outPath = Join-Path $DestFolder $_.FullName.Substring($SourceFolder.Length + 1)
$outDir = Split-Path $outPath
if (-not (Test-Path $outDir)) { New-Item Directory $outDir -Force }
switch ($ext) {
".jpg" { Compress-JpegWithMetadata $_.FullName $outPath -Quality $JpegQuality }
".jpeg"{ Compress-JpegWithMetadata $_.FullName $outPath -Quality $JpegQuality }
".png" { Compress-Png $_.FullName $outPath }
default { Copy-Item $_.FullName $outPath }
}
Write-Host "Processed" $_.Name
}
}
๐ Usage:
Compress-Images -SourceFolder "C:\Images" `
-DestFolder "C:\Compressed" `
-JpegQuality 65
Clean, uniform processing for mixed image collections.
8️⃣ Trick 6: Add Logging and Progress
Transparent automation with logging:
function BatchCompressAndLog {
param($Source, $Dest, $JpegQuality = 75, $LogFile = "compress_log.txt")
$started = Get-Date
Add-Content $LogFile "=== Compression started: $started ==="
Get-ChildItem $Source -Recurse -File | ForEach-Object -Begin {
$count = 0
} -Process {
$count++
Write-Progress -Activity "Compressing Images" -Status "$count files…" -PercentComplete (($count/$((Get-ChildItem $Source -Recurse).Count))*100)
$ext = $_.Extension.ToLower()
$out = Join-Path $Dest $_.FullName.Substring($Source.Length + 1)
if (-not (Test-Path (Split-Path $out))) { New-Item -ItemType Directory -Path (Split-Path $out) -Force }
try {
if ($ext -in ".jpg", ".jpeg") {
Compress-JpegWithMetadata $_.FullName $out -Quality $JpegQuality
Add-Content $LogFile "$(Get-Date): Compressed JPEG: $_ → $out"
} elseif ($ext -eq ".png") {
Compress-Png $_.FullName $out
Add-Content $LogFile "$(Get-Date): Compressed PNG: $_ → $out"
} else {
Copy-Item $_.FullName $out
}
} catch {
Add-Content $LogFile "$(Get-Date): Error processing $_ → $_"
}
} -End {
$ended = Get-Date
Add-Content $LogFile "=== Compression ended: $ended ==="
}
}
๐ Usage:
BatchCompressAndLog -Source "C:\Images" `
-Dest "C:\Compressed" `
-JpegQuality 60 `
-LogFile "C:\Scripts\compress_log.txt"
You get a progress bar and detailed logging for audits.
9️⃣ Trick 7: Automate via Task Scheduler
Make compression hands-free at chosen times (e.g., after backup):
-
Save your batch script (
Compress-Script.ps1
). -
Open Task Scheduler.
-
Create a Basic Task:
-
Name & trigger (daily, after logon).
-
Action: Start a Program →
Powershell.exe
-
Arguments:
-ExecutionPolicy Bypass -File "C:\Scripts\Compress-Script.ps1"
-
-
Finish the wizard.
-
๐ Your images compress on schedule—no typing required.
๐ Trick 8: Convert and Compress to WebP Format
WebP offers great compression—if you’re okay with new formats:
function Convert-ToWebP {
param($InputPath, $OutputPath, [int]$Quality = 75)
$webpDll = "C:\Tools\libwebp\libwebp.dll"
Add-Type -Path $webpDll
$bmp = [System.Drawing.Image]::FromFile($InputPath)
$ms = New-Object System.IO.MemoryStream
$bmp.Save($ms, [System.Drawing.Imaging.ImageFormat]::Bmp)
$bytes = $ms.ToArray()
[WebP.WebP]::EncodeRGB($bytes, $bmp.Width, $bmp.Height, $OutputPath, $Quality)
$bmp.Dispose(); $ms.Dispose()
}
Notes:
-
You need
libwebp.dll
and a WebP wrapper (like WebP‑dotnet). -
Files become
.webp
, smaller and web-friendly.
1️⃣1️⃣ Trick 9: Prompt User for Folders
Make your script interactive:
[void]Add-Type -AssemblyName System.Windows.Forms
$sourceDialog = New-Object System.Windows.Forms.FolderBrowserDialog
$sourceDialog.Description = "Select source folder"
$destDialog = New-Object System.Windows.Forms.FolderBrowserDialog
$destDialog.Description = "Select destination folder"
if ($sourceDialog.ShowDialog() -eq "OK" -and $destDialog.ShowDialog() -eq "OK") {
Compress-Images -SourceFolder $sourceDialog.SelectedPath -DestFolder $destDialog.SelectedPath -JpegQuality 70
}
This GUI gives you friendly folder pickers.
1️⃣2️⃣ Trick 10: Build Complete Compression Tool
Tie everything together:
Main: {
param($Quality = 70, $LogPath = "$env:USERPROFILE\compress_log.txt")
Write-Host "Choose folders to compress..."
[void]Add-Type -AssemblyName System.Windows.Forms
$src, $dest = SelectFolders()
BatchCompressAndLog -Source $src -Dest $dest -JpegQuality $Quality -LogFile $LogPath
Write-Host "Done! Check log at $LogPath"
}
function SelectFolders { … }
Save this as CompressImagesGUI.ps1
, double-click to run—choose source, destination, and compression level.
๐ก️ Safety Tips & Considerations
-
Backup originals: Always run scripts on copies.
-
Test quality: Try 70–85 for good size/quality balance.
-
Keep metadata: Use metadata functions for EXIF crucial images.
-
Avoid overwriting originals.
-
Version checks: Use
$PSVersionTable
and.NET
version in advanced scripts. -
Logging: Use logs to track compression and troubleshoot.
๐ Real‑World Case: Alice’s Shared Drive Size Reduction
Scenario: Alice shares 300 client photos (10 MB each) via OneDrive.
-
Scheduled script (Task Scheduler): compresses to 800 KB each.
-
She uses quality 70 JPEG, logs each file.
-
Results: total size cut from 3 GB to 240 MB—upload time slashed from 8 hours to 30 mins.
All done automatically nightly—no manual hassle!
๐ป Troubleshooting
-
Errors loading System.Drawing? Run PowerShell as admin and ensure .NET 4.x.
-
COM issues with WIA? Install Windows Media Feature Pack on N editions.
-
WebP script errors? Confirm
libwebp.dll
path and DLL architecture (x86 vs x64). -
Silent failures in scheduled tasks? Log output via
*> C:\task_log.txt
. -
Quality too low or blurred images? Increase quality parameter slightly and retest small batch.
๐ Summary Checklist
Task | Script/Path |
---|---|
Compress single JPEG | Compress-Jpeg |
Preserve EXIF metadata | Compress-JpegWithMetadata |
Batch process JPEGs folder | Compress-JpegFolder |
Compress PNGs losslessly | Compress-Png |
Combined batch for all | Compress-Images |
Logging & progress feedback | BatchCompressAndLog |
Automate via Task Scheduler | Basic task in Task Scheduler |
Convert to WebP format | Convert-ToWebP with libwebp DLL import |
GUI folder selection | FolderBrowserDialog demos |
Complete tool script | CompressImagesGUI.ps1 |
You now have a robust, flexible system to compress and manage images in Windows 11 using PowerShell—fully automated, performant, and safe.
✅ Final Thoughts
By harnessing the full power of PowerShell and .NET, you’ve unlocked image compression at scale. You can now:
-
Share photos seamlessly without bulky files.
-
Automate compressions for presentations, backups, or web uploads.
-
Maintain image quality and metadata intact.
-
Schedule tasks to run in the background.
-
Customize output for advanced formats like WebP.
Comments
Post a Comment