├── ConvertTo-Jpeg.ps1 ├── LICENSE └── README.md /ConvertTo-Jpeg.ps1: -------------------------------------------------------------------------------- 1 | # ConvertTo-Jpeg - Converts RAW (and other) image files to the widely-supported JPEG format 2 | # https://github.com/DavidAnson/ConvertTo-Jpeg 3 | 4 | Param ( 5 | [Parameter( 6 | Mandatory = $true, 7 | Position = 1, 8 | ValueFromPipeline = $true, 9 | ValueFromPipelineByPropertyName = $true, 10 | ValueFromRemainingArguments = $true, 11 | HelpMessage = "Array of image file names to convert to JPEG")] 12 | [Alias("FullName")] 13 | [String[]] 14 | $Files, 15 | 16 | [Parameter( 17 | HelpMessage = "Fix extension of JPEG files without the .jpg extension")] 18 | [Switch] 19 | [Alias("f")] 20 | $FixExtensionIfJpeg, 21 | 22 | [Parameter( 23 | HelpMessage = "Remove existing extension of non-JPEG files before adding .jpg")] 24 | [Switch] 25 | [Alias("r")] 26 | $RemoveOriginalExtension 27 | ) 28 | 29 | Begin 30 | { 31 | # Technique for await-ing WinRT APIs: https://fleexlab.blogspot.com/2018/02/using-winrts-iasyncoperation-in.html 32 | Add-Type -AssemblyName System.Runtime.WindowsRuntime 33 | $runtimeMethods = [System.WindowsRuntimeSystemExtensions].GetMethods() 34 | $asTaskGeneric = ($runtimeMethods | ? { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' })[0] 35 | Function AwaitOperation ($WinRtTask, $ResultType) 36 | { 37 | $asTaskSpecific = $asTaskGeneric.MakeGenericMethod($ResultType) 38 | $netTask = $asTaskSpecific.Invoke($null, @($WinRtTask)) 39 | $netTask.Wait() | Out-Null 40 | $netTask.Result 41 | } 42 | $asTask = ($runtimeMethods | ? { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncAction' })[0] 43 | Function AwaitAction ($WinRtTask) 44 | { 45 | $netTask = $asTask.Invoke($null, @($WinRtTask)) 46 | $netTask.Wait() | Out-Null 47 | } 48 | 49 | # Reference WinRT assemblies 50 | [Windows.Storage.StorageFile, Windows.Storage, ContentType=WindowsRuntime] | Out-Null 51 | [Windows.Graphics.Imaging.BitmapDecoder, Windows.Graphics, ContentType=WindowsRuntime] | Out-Null 52 | } 53 | 54 | Process 55 | { 56 | # Summary of imaging APIs: https://docs.microsoft.com/en-us/windows/uwp/audio-video-camera/imaging 57 | foreach ($file in $Files) 58 | { 59 | Write-Host $file -NoNewline 60 | try 61 | { 62 | try 63 | { 64 | # Get SoftwareBitmap from input file 65 | $file = Resolve-Path -LiteralPath $file 66 | $inputFile = AwaitOperation ([Windows.Storage.StorageFile]::GetFileFromPathAsync($file)) ([Windows.Storage.StorageFile]) 67 | $inputFolder = AwaitOperation ($inputFile.GetParentAsync()) ([Windows.Storage.StorageFolder]) 68 | $inputStream = AwaitOperation ($inputFile.OpenReadAsync()) ([Windows.Storage.Streams.IRandomAccessStreamWithContentType]) 69 | $decoder = AwaitOperation ([Windows.Graphics.Imaging.BitmapDecoder]::CreateAsync($inputStream)) ([Windows.Graphics.Imaging.BitmapDecoder]) 70 | } 71 | catch 72 | { 73 | # Ignore non-image files 74 | Write-Host " [Unsupported]" 75 | continue 76 | } 77 | if ($decoder.DecoderInformation.CodecId -eq [Windows.Graphics.Imaging.BitmapDecoder]::JpegDecoderId) 78 | { 79 | $extension = $inputFile.FileType 80 | if ($FixExtensionIfJpeg -and ($extension -ne ".jpg") -and ($extension -ne ".jpeg")) 81 | { 82 | # Rename JPEG-encoded files to have ".jpg" extension 83 | $newName = $inputFile.Name -replace ($extension + "$"), ".jpg" 84 | AwaitAction ($inputFile.RenameAsync($newName)) 85 | Write-Host " => $newName" 86 | } 87 | else 88 | { 89 | # Skip JPEG-encoded files 90 | Write-Host " [Already JPEG]" 91 | } 92 | continue 93 | } 94 | $bitmap = AwaitOperation ($decoder.GetSoftwareBitmapAsync()) ([Windows.Graphics.Imaging.SoftwareBitmap]) 95 | 96 | # Determine output file name 97 | # Get name of original file, including extension 98 | $fileName = $inputFile.Name 99 | if ($RemoveOriginalExtension) 100 | { 101 | # If removing original extension, get the original file name without the extension 102 | $fileName = $inputFile.DisplayName 103 | } 104 | # Add .jpg to the file name 105 | $outputFileName = $fileName + ".jpg" 106 | 107 | # Write SoftwareBitmap to output file 108 | $outputFile = AwaitOperation ($inputFolder.CreateFileAsync($outputFileName, [Windows.Storage.CreationCollisionOption]::ReplaceExisting)) ([Windows.Storage.StorageFile]) 109 | $outputStream = AwaitOperation ($outputFile.OpenAsync([Windows.Storage.FileAccessMode]::ReadWrite)) ([Windows.Storage.Streams.IRandomAccessStream]) 110 | $encoder = AwaitOperation ([Windows.Graphics.Imaging.BitmapEncoder]::CreateAsync([Windows.Graphics.Imaging.BitmapEncoder]::JpegEncoderId, $outputStream)) ([Windows.Graphics.Imaging.BitmapEncoder]) 111 | $encoder.SetSoftwareBitmap($bitmap) 112 | $encoder.IsThumbnailGenerated = $true 113 | 114 | # Do it 115 | AwaitAction ($encoder.FlushAsync()) 116 | Write-Host " -> $outputFileName" 117 | } 118 | catch 119 | { 120 | # Report full details 121 | throw $_.Exception.ToString() 122 | } 123 | finally 124 | { 125 | # Clean-up 126 | if ($inputStream -ne $null) { [System.IDisposable]$inputStream.Dispose() } 127 | if ($outputStream -ne $null) { [System.IDisposable]$outputStream.Dispose() } 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018-2019 David Anson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ConvertTo-Jpeg 2 | 3 | > A PowerShell script that converts RAW (and other) image files to the widely-supported JPEG format 4 | 5 | ## Overview 6 | 7 | Many cameras - and many phones - save photos in a custom file format. 8 | Known as [RAW images](https://en.wikipedia.org/wiki/Raw_image_format), these files contain richer information than the widely-supported [JPEG format](https://en.wikipedia.org/wiki/JPEG) allows, but they are not as widely supported by tools. 9 | To easily view, edit, or share pictures, it can be handy to convert RAW images to the JPEG format. 10 | 11 | `ConvertTo-Jpeg.ps1` is a [PowerShell](https://en.wikipedia.org/wiki/PowerShell) script that uses the [Windows.Graphics.Imaging API](https://docs.microsoft.com/en-us/uwp/api/windows.graphics.imaging) to create a JPEG-encoded copy of each image that is passed to it. 12 | (The original file is not modified.) 13 | 14 | ## Related 15 | 16 | - [ConvertTo-Heic.ps1](https://github.com/DavidAnson/ConvertTo-Heic): A PowerShell script that converts image files to the efficient HEIC format 17 | 18 | ## Examples 19 | 20 | ### Converting Files 21 | 22 | Passing parameters: 23 | 24 | ```PowerShell 25 | PS C:\T> .\ConvertTo-Jpeg.ps1 C:\T\Pictures\IMG_1234.HEIC C:\T\Pictures\IMG_5678.HEIC 26 | C:\T\Pictures\IMG_1234.HEIC -> IMG_1234.HEIC.jpg 27 | C:\T\Pictures\IMG_5678.HEIC -> IMG_5678.HEIC.jpg 28 | ``` 29 | 30 | Pipeline via `dir`: 31 | 32 | ```PowerShell 33 | PS C:\T> dir C:\T\Pictures | .\ConvertTo-Jpeg.ps1 34 | C:\T\Pictures\IMG_1234.HEIC -> IMG_1234.HEIC.jpg 35 | C:\T\Pictures\IMG_5678.HEIC -> IMG_5678.HEIC.jpg 36 | C:\T\Pictures\Kitten.jpg [Already JPEG] 37 | C:\T\Pictures\Notes.txt [Unsupported] 38 | ``` 39 | 40 | Pipeline via `Get-ChildItem`: 41 | 42 | ```PowerShell 43 | PS C:\T> Get-ChildItem C:\T\Pictures -Filter *.HEIC | .\ConvertTo-Jpeg.ps1 44 | C:\T\Pictures\IMG_1234.HEIC -> IMG_1234.HEIC.jpg 45 | C:\T\Pictures\IMG_5678.HEIC -> IMG_5678.HEIC.jpg 46 | ``` 47 | 48 | ### Renaming Files 49 | 50 | Sometimes files have the wrong extension. 51 | To rename JPEG-encoded files that don't have the standard `.jpg` extension, use the `-FixExtensionIfJpeg` 52 | switch (alias `-f`). 53 | (The `=>` in the output indicates that the file was renamed vs. converted.) 54 | 55 | ```PowerShell 56 | PS C:\T> dir C:\T\Pictures\*.HEIC | .\ConvertTo-Jpeg.ps1 -FixExtensionIfJpeg 57 | C:\T\Pictures\IMG_1234 (Edited).HEIC => IMG_1234 (Edited).jpg 58 | C:\T\Pictures\IMG_1234.HEIC -> IMG_1234.HEIC.jpg 59 | ``` 60 | 61 | ### Removing existing extensions 62 | 63 | To remove the existing extension of a file, use the `-RemoveOriginalExtension` switch (alias `-r`). 64 | 65 | ```PowerShell 66 | PS C:\T> dir C:\T\Pictures\*.HEIC | .\ConvertTo-Jpeg.ps1 -RemoveOriginalExtension 67 | C:\T\Pictures\IMG_1234.HEIC -> IMG_1234.jpg 68 | C:\T\Pictures\IMG_5678.HEIC -> IMG_5678.jpg 69 | ``` 70 | 71 | ## Formats 72 | 73 | | Decoder | Extensions | 74 | | ---------------------------- | ---------- | 75 | | BMP Decoder | .BMP .DIB .RLE | 76 | | CUR Decoder | .CUR | 77 | | DDS Decoder | .DDS | 78 | | DNG Decoder | .DNG | 79 | | GIF Decoder | .GIF | 80 | | ICO Decoder | .ICO .ICON | 81 | | JPEG Decoder | .EXIF .JFIF .JPE .JPEG .JPG | 82 | | Microsoft Camera Raw Decoder | .ARW .CR2 .CRW .DNG .ERF .KDC .MRW .NEF .NRW .ORF .PEF .RAF .RAW .RW2 .RWL .SR2 .SRW | 83 | | *Microsoft HEIF Decoder* | .AVCI .AVCS .HEIC .HEICS .HEIF .HEIFS | 84 | | *Microsoft Webp Decoder* | .WEBP | 85 | | PNG Decoder | .PNG | 86 | | TIFF Decoder | .TIF .TIFF | 87 | | WMPhoto Decoder | .JXR .WDP | 88 | 89 | ## HEIC/HEIF 90 | 91 | Windows 10's April 2018 Update (version 1803) added support for [HEIC/HEIF images](https://en.wikipedia.org/wiki/High_Efficiency_Image_File_Format) to the Windows.Graphics.Imaging API. 92 | `ConvertTo-Jpeg.ps1` uses the new decoder automatically if it's available. 93 | To enable the decoder, install the Microsoft [HEIF and HEVC Media Extensions](https://www.microsoft.com/store/productId/9NTLD6MSD8BM) bundle (or else both of [HEVC Video Extensions](https://www.microsoft.com/store/productId/9NMZLZ57R3T7) and [HEIF Image Extensions](https://www.microsoft.com/store/productId/9PMMSR1CGPWG)). 94 | Once done, the built-in Photos app (and other programs that use the Windows.Graphics.Imaging API) will be able to open HEIC/HEIF images. 95 | 96 | ## WEBP 97 | 98 | If the default support for [WebP](https://en.wikipedia.org/wiki/WebP) images is missing or incomplete, consider installing the Microsoft [Webp Image Extensions](https://www.microsoft.com/en-us/p/webp-image-extensions/9pg2dk419drg). 99 | As above, the built-in Photos app is a great way to verify support for WEBP images. 100 | --------------------------------------------------------------------------------