Powershell Tricks to Compress Images for Sharing in Windows 11

 ๐ŸŒŸ 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.

Powershell Tricks to Compress Images for Sharing in Windows 11

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:

  1. Open PowerShell (admin): Press Win + S, type PowerShell, right-click and Run as administrator.

  2. Verify version: Run:

    $PSVersionTable.PSVersion  
    

    Make sure you're running PowerShell 5.1+—any version in Windows 11 works fine.

  3. 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):

  1. Save your batch script (Compress-Script.ps1).

  2. Open Task Scheduler.

  3. Create a Basic Task:

    • Name & trigger (daily, after logon).

    • Action: Start a ProgramPowershell.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