Someone sends you a folder of .webp files and the upload form only takes JPG. You don't need ImageMagick for this: Windows has shipped a WebP decoder since 1809 (as the Store-delivered Microsoft.WebpImageExtension), and WIC exposes it to PowerShell. But the built-in path has two sharp edges I only found by testing them.
First: renaming is not converting
$ head -c 16 photo.webp | xxd
00000000: 5249 4646 4e0a 0000 5745 4250 5650 3820 RIFF....WEBPVP8
Rename it to photo.jpg and those bytes don't change. Most viewers sniff the content and open it anyway, which is exactly why people think the rename worked - until something that actually validates the format rejects it. Python agrees about what it really is:
>>> Image.open("renamed.jpg").format
'WEBP'
The no-install route: WIC via PowerShell
Add-Type -AssemblyName PresentationCore
$src = "C:\Pictures\image.webp"
$frame = [System.Windows.Media.Imaging.BitmapDecoder]::Create([Uri]$src, 'None', 'OnLoad').Frames[0]
$enc = New-Object System.Windows.Media.Imaging.JpegBitmapEncoder
$enc.QualityLevel = 90
$enc.Frames.Add($frame)
$out = [System.IO.File]::Create([IO.Path]::ChangeExtension($src, '.jpg'))
$enc.Save($out); $out.Close()
BitmapDecoder.Create picks the codec by content, and on a machine with the WebP extension you can confirm which one it chose:
$dec = [System.Windows.Media.Imaging.BitmapDecoder]::Create([Uri]$src, 'None', 'OnLoad')
$dec.CodecInfo.FriendlyName # -> Microsoft Webp Decoder
A whole folder is the same thing in a loop:
Add-Type -AssemblyName PresentationCore
Get-ChildItem "C:\Pictures\*.webp" | ForEach-Object {
$f = [System.Windows.Media.Imaging.BitmapDecoder]::Create([Uri]$_.FullName, 'None', 'OnLoad').Frames[0]
$e = New-Object System.Windows.Media.Imaging.JpegBitmapEncoder
$e.QualityLevel = 90
$e.Frames.Add($f)
$o = [System.IO.File]::Create([IO.Path]::ChangeExtension($_.FullName, '.jpg'))
$e.Save($o); $o.Close()
}
Gotcha 1: the alpha channel is gone
This is the part worth knowing before you run it over a folder of logos. The frame that comes back from the Microsoft WebP decoder is Bgr32 - no alpha. Transparent pixels arrive black, and because the data is already flattened, switching to PngBitmapEncoder doesn't save you:
$f.Format # -> Bgr32
# transparent WebP -> PNG, then inspect the corner pixel:
# (0, 0, 0, 255) # opaque black, not transparent
Pillow reads the same file as RGBA, so if transparency matters, do it in Python and decide what goes behind it:
from PIL import Image
from pathlib import Path
for src in Path(".").glob("*.webp"):
im = Image.open(src)
if im.mode in ("RGBA", "LA", "P"): # composite onto white instead of black
im = im.convert("RGBA")
bg = Image.new("RGB", im.size, (255, 255, 255))
bg.paste(im, mask=im.split()[3])
im = bg
im.convert("RGB").save(src.with_suffix(".jpg"), quality=90)
Gotcha 2: animated WebP silently loses everything after frame 1
The decoder does expose the frames - $dec.Frames.Count returned 4 for my test file, and Pillow reported n_frames = 4 for the same one. But Frames[0] is what the snippet above encodes, and JPEG has nowhere to put the rest. If the motion matters, GIF is two lines:
Image.open("anim.webp").save("anim.gif", save_all=True, loop=0)
And it gets bigger
WebP -> JPG is a re-encode of already-lossy data, so you pay twice: a little more generation loss, and usually a larger file. My 2.6 KB test WebP became 7.4 KB as a quality-90 JPEG. That tracks with Google's own numbers - lossy WebP is 25-34% smaller than JPEG at equivalent SSIM, lossless is 26% smaller than PNG - so converting "to save space" is backwards. Keep the original, and if size is the goal, resize instead of re-encoding.
Quick reference
| Need | Use |
|---|---|
| One or two files, no install | Any browser-based converter that runs client-side |
| A folder, nothing installed | PowerShell + WIC (above) |
| Transparency preserved or flattened your way | Pillow |
| Animation kept | Pillow -> GIF |
Everything above was run on Windows 11 with Microsoft.WebpImageExtension 1.2.31: the codec name, the Bgr32 frame format, the black corner pixel, the frame counts and the file sizes are measured, not assumed.
Longer version with the non-developer paths (Paint, Photos, a drag-and-drop converter that doesn't upload): https://www.coding-now.com/en/guides/webp-to-jpg?utm_source=devto
Has the alpha-drop bitten anyone else, or is there a WIC flag I missed?
Top comments (0)