en-US/about_PSImageConverter.help.txt
|
TOPIC about_PSImageConverter SHORT DESCRIPTION Cross-platform image conversion (HEIC/HEIF and more), powered by Magick.NET. LONG DESCRIPTION PSImageConverter converts images, for example HEIC/HEIF photos taken with an iPhone, to JPEG, PNG, WebP, AVIF, HEIC, JPEG XL, TIFF, BMP or GIF and reads their metadata. The Magick.NET library and its native ImageMagick build are bundled with the module, so no codecs need to be installed; only HEIC output uses the HEVC encoder of the operating system (see WRITING HEIC). It requires PowerShell 7.4 or later and runs on Windows, Linux and macOS (x64 and arm64; on Alpine/musl Linux x64 only). Cmdlets: Convert-Image Converts every format Magick.NET can read to the -Format Jpeg, Png, WebP, Avif, Heic, Jxl (JPEG XL), Tiff, Bmp or Gif. ConvertTo-Jpeg Convert-Image -Format Jpeg. ConvertTo-Png Convert-Image -Format Png (lossless, no -Quality). ConvertTo-WebP Convert-Image -Format WebP. ConvertTo-Avif Convert-Image -Format Avif. ConvertTo-Heic Convert-Image -Format Heic (needs the HEVC encoder of the operating system, see WRITING HEIC). ConvertTo-JpegXL Convert-Image -Format Jxl. ConvertFrom-Heic Converts .heic/.heif/.hif files only; -Format defaults to Jpeg. Get-ImageInfo Returns the format, dimensions, orientation, camera and date taken of images; its objects can be piped into the conversion cmdlets. The conversion cmdlets take -Path (wildcards) or -LiteralPath (aliases PSPath and LP; files piped from Get-ChildItem and the objects of Get-ImageInfo bind here; folders are skipped), -DestinationPath, -Quality (1 to 100, default 90, for Jpeg, WebP, Avif, Heic and Jxl), -StripMetadata, -Force, -WhatIf and -Confirm, and return a FileInfo object for every written file, the same object Get-Item returns, so it can be piped into the next cmdlet. The output gets the base name of the source and the extension of the format (.jpg, .png, .webp, .avif, .heic, .jxl, .tif, .bmp, .gif). A file given more than once in a call (e.g. by the patterns *.heic and IMG_*) is converted or described once. A conversion never overwrites its source file or a file written earlier in the same call, also not when the folder is reached through another path (a junction, a symbolic link, a subst or mapped drive, a UNC path), and never leaves a truncated or temporary file behind, also not when it is stopped with Ctrl+C. Wildcards skip hidden files and folders (on Windows those with the Hidden attribute, on Linux and macOS names that start with a dot), as Get-ChildItem does without -Force; -LiteralPath and a folder name without wildcards reach them. METADATA The EXIF orientation is applied to the pixels, so the output is upright. Without -StripMetadata the output keeps the metadata its format can carry: Jpeg, Png EXIF, XMP, IPTC, ICC colour profile, comments WebP, Avif EXIF, XMP, ICC colour profile Heic Windows, Linux: EXIF, XMP, ICC colour profile; macOS: ICC colour profile (sRGB as nclx colour information), no EXIF or XMP (see WRITING HEIC) Jxl EXIF, XMP; ICC colour profile only at -Quality 100 Tiff XMP, IPTC, ICC colour profile, comments Gif IPTC, ICC colour profile (browsers ignore it in GIF files), comments Bmp ICC colour profile Tiff, Gif and Bmp output (and Heic output on macOS) gets no EXIF data (date taken, camera, GPS location); a warning is written for every source with EXIF data unless -StripMetadata is used. -StripMetadata removes EXIF (including the GPS location), XMP, IPTC and comments but keeps the ICC colour profile. The colours are kept when the source carries an ICC profile, as iPhone photos do. A HEIC or AVIF source that signals its colours only through nclx/CICP (no ICC profile) with primaries other than sRGB/BT.709 (e.g. Display P3 or BT.2020, also HDR with PQ or HLG) is converted without colour conversion, so its colours look less saturated; Avif output keeps the CICP values. Lossy Jxl output (-Quality below 100) is always sRGB, because the bundled JPEG XL encoder stores an ICC colour profile only in lossless files. The pixels of a source with an ICC profile (e.g. Display P3 or Adobe RGB) are converted to sRGB first, so the photo looks the same; colours outside sRGB are clipped. Use -Quality 100 or Avif to keep a wide gamut. A grey profile in a colour image, which viewers ignore, is ignored here as well. Chrome shows a lossless Jxl file whose profile describes its tone curve as a table (e.g. Photoshop's 'Dot Gain 20%' grey profiles) without colour management; Firefox applies such a profile. Bytes after the size the header of an ICC profile declares are not part of the profile (Samsung Galaxy HEIC photos, for example, have two zero bytes there, and so do JPEGs converted from them). The profile is written without them in every output format, with unchanged colours (-Verbose says so). The Png encoder refuses a profile with such bytes. An ICC profile that the colour engine cannot apply, because it is damaged (e.g. cut short inside its tag data) or names an unknown colour space, is left out of every output (-Verbose says so): viewers ignore it in the source, Png and lossless Jxl output could not be written with it, and Chrome and Edge would not display an Avif that carries it. A profile that the colour engine can apply but the Png or lossless Jxl encoder cannot write (e.g. a version 4 profile whose length is not a multiple of 4, a header that declares a few more bytes than the profile has although all its tags are complete, or, for Jxl, a rendering intent above 3) is left out of that output only, so the file is still written (-Verbose says so). Grey images with a grey ICC profile (e.g. Photoshop's 'Dot Gain 20%' or 'Gray Gamma 1.8', scanned greyscale) keep their grey pixels and the profile in Jpeg, Png, Tiff and lossless Jxl output. WebP, Avif, Heic, Bmp and Gif store grey pixels as RGB, for which a grey profile is not valid, so the pixels are converted to sRGB first. This includes a grey JPEG XL with a grey profile, which ImageMagick reads as RGB. A profile made for other pixels is not valid: viewers ignore a grey profile in a colour image, and browsers apply an RGB profile to the pixels of a grey JPEG, while Windows (Photos, Explorer) rejects it. Such a profile is kept, except where the file could not be written or would look different with it: it is left out of lossless Jxl output and, as a grey profile on colour pixels, out of Avif, Heic and Bmp output, and out of Jpeg, Png and Tiff output when the colour pixels are all neutral grey (these formats store them as grey). Neutral grey pixels with an RGB profile (a black-and-white photo in Adobe RGB) are stored as RGB in Jpeg, Png and Tiff output, so the profile stays valid. When the orientation is applied, an EXIF thumbnail is removed (it would show rotated against the photo), and the XMP orientation and pixel dimensions are updated. CMYK images (e.g. print JPEGs) stay CMYK with their colour profile in Jpeg and Tiff output; for the other formats they are converted to sRGB through their CMYK profile. Jpeg has no transparency: transparent and half-transparent pixels are placed on a white background (paper white for a CMYK image). So does Heic output on Windows, whose encoder takes opaque pixels only. The other formats keep the alpha channel (Gif only fully transparent or opaque pixels). FRAMES AND DAMAGED FILES Only the first frame of an animated GIF or WebP, a multi-page TIFF or a HEIC file with several images is converted. A warning is written for every such source: 'Only the first of <N> frames is converted: <path>'. A source that ImageMagick can read but reports as damaged, e.g. a JPEG cut off by an interrupted copy (the missing part becomes grey) or an animated GIF cut off after its first frame (its frames cannot be counted then), is converted with the warning 'The image may be damaged (<reasons>): <path>'; Get-ImageInfo writes the same warning. A JPEG that lacks only its end marker gets this warning as well, although its pixels are complete: the decoder reports it like a file that is cut off. Other ImageMagick warnings about a source go to the verbose stream. WRITING HEIC HEVC, the codec of HEIC, is covered by patent pools, and shipping an HEVC encoder would pass their licensing duties on to every user, so the module ships none. ConvertTo-Heic and -Format Heic use the encoder of the operating system: Windows 10 1809+ the WIC HEIF encoder; install the 'HEVC Video Extensions' from the Microsoft Store (and the 'HEIF Image Extension' if it is missing) macOS ImageIO; nothing to install, but the Mac needs an HEVC encoder Linux libheif 1.12 or later with its HEVC encoder x265 (built in or as a plugin): Ubuntu 24.04 and 26.04, Debian 13: sudo apt install libheif1 libheif-plugin-x265 Ubuntu 22.04, Debian 12: sudo apt install libheif1 Fedora: enable RPM Fusion free (https://rpmfusion.org), then sudo dnf install libheif-freeworld Arch: sudo pacman -S libheif Alpine: apk add libheif libheif-x265 The first HEIC conversion in a session checks with a small test image that the encoder works; after installing one, start a new session. When it is missing, the call stops before any file is read, also with -WhatIf, with one terminating error HeicEncoderNotAvailable that says what to install. -Format Avif stores AV1 in the same HEIF container and needs nothing installed. Windows and Linux keep EXIF and XMP as they are (the EXIF orientation is set to 1) and the ICC colour profile; macOS writes no EXIF or XMP and keeps the ICC colour profile, except sRGB, which ImageIO stores as nclx colour information instead. Windows stores no alpha channel (transparent pixels are placed on white, as for Jpeg); Linux and macOS keep it. An image the encoder rejects fails with ImageWriteFailed: on Windows a side longer than 22528 pixels; on Linux a side longer than 32768 pixels, one shorter than 32 pixels next to one longer than 4096, or an image whose slices stay larger than Magick.NET reads even in 13 slices; which sizes ImageIO rejects on macOS is not checked yet. On Linux an image is encoded as one HEVC slice. Magick.NET cannot read a slice larger than 16 MiB, so only when that slice is larger than 15,000,000 bytes (a photo with much detail from about 25 megapixels, random noise from about 10) is the image encoded again in up to 13 slices. libheif 1.17 and older store such slices as several extents (parts of the file), which Windows' own viewer refuses; the module joins them into one. Windows' own viewer (Photos, Explorer) does not show a HEIC image with a side shorter than 8 pixels, whichever program wrote it, and ignores the alpha channel of a HEIC file. It does not open HEIC files written on Linux with a side longer than 16384 pixels. At -Quality 90 HEIC files are larger than JPEG files at 90 (about 1.1 to 2 times on Windows, 2.4 to 3.4 times with x265 on Linux); -Quality 60 to 80 gives about the size of JPEG at 90. Like AVIF and WebP, HEIC stores colour at half resolution (4:2:0), so thin coloured lines blur slightly. In a container that denies the NUMA memory-policy calls (Docker's default seccomp profile), x265 writes harmless lines to the console that PowerShell cannot redirect: with x265 3.5 about 50 lines 'set_mempolicy: Operation not permitted' per image up to 8 megapixels (and once more for the encoder check of a session) and about one per processor for a larger one, with x265 4.1 about one line 'unable to set thread affinity for NUMA node mask' per processor for an image larger than 8 megapixels or of several slices, which gets an explicit thread pool. x265 4.1 makes no pool of its own there, so it encodes smaller images 3 to 5 times more slowly. Both go away with docker run --cap-add SYS_NICE. On Arch Linux, libheif writes 'dlopen: ... cannot open shared object file' lines for plugins whose optional libraries are not installed; they are harmless too. ERRORS Errors about a file are non-terminating: the cmdlets continue with the next file, $? is $false afterwards, -ErrorVariable holds one record per failed file and -ErrorAction Stop stops at the first one. Every error has a stable id: PathNotFound the input matches no file; for a pattern the message names a drive or folder that does not exist, a folder part that is a file, a folder part with wildcards that no folder (or only a hidden one) matches, or, when the folder exists, adds that wildcards skip hidden files and that [ and ] are wildcard characters and how to match them; for a PSDrive that is not global it names the drive (see PSDRIVES) InvalidWildcardPattern -Path is not a valid pattern ('a[.jpg'); use -LiteralPath UnsupportedProvider the input is not a file system path (one error per input, also for a pattern) NotHeicFile ConvertFrom-Heic: no .heic/.heif/.hif file TargetIsSource the output would overwrite the source DuplicateTarget two sources of one call map to one output TargetExists the output exists and -Force is not set ConfirmationFailed -Confirm (or the caller's $ConfirmPreference) asks for a confirmation the host cannot show, e.g. in pwsh -NonInteractive; use -Confirm:$false ImageReadFailed the source cannot be read ImageWriteFailed the output cannot be written (also when the HEIC encoder rejects the image), or the destination folder cannot be created or written to The target object is the source file; for PathNotFound, InvalidWildcardPattern and UnsupportedProvider it is the input as given. A -DestinationPath that cannot name a folder is a parameter error: it stops the whole call with one terminating error before any file is read, also with -WhatIf, and try/catch catches it without -ErrorAction Stop. Use $_ in the catch block or $Error[0] for the error record: -ErrorVariable holds the exception for these errors (its ErrorRecord property has the record). The ids are InvalidDestinationPath (white space only, an unknown drive or provider, invalid characters, on Windows also a colon after the drive such as C:\x:y, a drive-qualified path that climbs above the root of its drive such as Temp:\..\x, a PSDrive that is not global, see PSDRIVES), UnsupportedProvider (not a file system path) and DestinationNotDirectory (the path or one of its parent folders is a file, a junction or symbolic link whose target does not exist, or a link that cannot be resolved, such as one that points to itself); the target object is the -DestinationPath value. -Format Heic on a computer without a working HEVC encoder stops the call the same way with HeicEncoderNotAvailable (target object Heic). A relative path that climbs above the root of its drive stays at the root, as in other cmdlets. A folder that does not exist yet is created when the first file is written; when that fails (no permission, a read-only file system) or the folder cannot be written to, each file gets ImageWriteFailed. ConvertFrom-Heic and the ConvertTo-* cmdlets pass all files of one call to a single Convert-Image, so the errors it finds have the FullyQualifiedErrorId '<Id>,Convert-Image'. The checks they make before carry their own name: the -DestinationPath errors of every one of them, HeicEncoderNotAvailable of ConvertTo-Heic and ConvertFrom-Heic, and the input errors (PathNotFound, InvalidWildcardPattern, UnsupportedProvider, NotHeicFile) of ConvertFrom-Heic. With Import-Module -Prefix, the errors raised inside Convert-Image during such a call keep the module's unprefixed name ',Convert-Image', while a direct call of the prefixed cmdlet and the checks of a prefixed wrapper carry the prefixed name. Separate calls or processes that write the same output file at the same time without -Force are not coordinated: on Windows the later one fails with TargetExists or ImageWriteFailed; on Linux and macOS both can succeed and the later file replaces the earlier one. With -Force the later file replaces the earlier one on every platform. Give parallel conversions different destination folders. PREFERENCE VARIABLES Like compiled cmdlets, the cmdlets follow the preference variables of their caller ($WhatIfPreference, $ConfirmPreference, $ErrorActionPreference, $WarningPreference, $InformationPreference, $VerbosePreference, $DebugPreference), also when a script or function sets them in its own scope. Functions of a script module do not see such values by themselves (PowerShell issue #4568), so each cmdlet copies them when it starts. A function with SupportsShouldProcess that is called with -WhatIf therefore passes it on, and $ErrorActionPreference = 'Stop' in a script makes the first error terminating. A common parameter given to the cmdlet itself (-WhatIf, -Confirm, -ErrorAction, -WarningAction, -InformationAction, -Verbose, -Debug) wins. PSDRIVES PSDrives that are not global cannot be used. A script module resolves paths in its own session state, which sees only the global drives; this is a limitation of the PowerShell engine (Microsoft.PowerShell.Archive behaves the same). A drive created with New-PSDrive without -Scope Global inside a function (also by a script dot-sourced there), at the top of a script run by its path or with & (.\setup.ps1, & .\setup.ps1), or in another module therefore fails in -Path and -LiteralPath with PathNotFound and in -DestinationPath with InvalidDestinationPath, and so does a relative input path while the current location is on such a drive. The message names the drive and its root. Create the drive with New-PSDrive -Scope Global, or use the file system path. A drive created at the prompt, in the profile, at the top level of pwsh -File or by a script dot-sourced there (. .\setup.ps1) is global and works. When such a drive has the name of a global drive (e.g. a script's own Temp:), the module would read and write on the global drive instead, so it fails the same way and the message names both roots. For a drive of another provider the message says that only file system paths are supported. Files piped from Get-ChildItem or Get-Item on such a drive work, because their PSPath holds the file system path, and so does a relative -DestinationPath; strings and the output of Resolve-Path (which has no PSPath) do not. EXAMPLES PS C:\> ConvertFrom-Heic -Path .\IMG_1234.HEIC PS C:\> Get-ChildItem -Path C:\Pictures -Filter *.heic | ConvertFrom-Heic -Format Png PS C:\> Get-ChildItem -Path C:\Pictures -Filter *.jpg | ConvertTo-WebP -Quality 80 -DestinationPath C:\Pictures\web PS C:\> ConvertTo-Avif -Path .\IMG_1234.HEIC -StripMetadata PS C:\> ConvertTo-Heic -Path .\export\*.png -Quality 80 PS C:\> Convert-Image -Path C:\Pictures\*.png -Format Tiff -WhatIf PS C:\> Get-ChildItem -Path C:\Pictures -Filter *.heic | Get-ImageInfo PS C:\> Get-ImageInfo -Path .\*.heic | Where-Object -Property Width -GT 4000 | ConvertTo-Jpeg NOTE: Thank you to all those who contributed to this module, by writing code, sharing opinions, and provided feedback. TROUBLESHOOTING NOTE: Look out on the GitHub repository for issues and new releases: https://github.com/fasteiner/PSImageConverter SEE ALSO - Get-Help Convert-Image -Full - https://github.com/dlemstra/Magick.NET KEYWORDS HEIC, HEIF, HEVC, Image, Conversion, JPEG, PNG, WebP, AVIF, JpegXL, ImageMagick |