Skip to content

Instantly share code, notes, and snippets.

@someshinyobject
Last active September 17, 2024 19:10
Show Gist options
  • Save someshinyobject/617bf00556bc43af87cd to your computer and use it in GitHub Desktop.
Save someshinyobject/617bf00556bc43af87cd to your computer and use it in GitHub Desktop.
<#
.SYNOPSIS
Resize an image
.DESCRIPTION
Resize an image based on a new given height or width or a single dimension and a maintain ratio flag.
The execution of this CmdLet creates a new file named "OriginalName_resized" and maintains the original
file extension
.PARAMETER Width
The new width of the image. Can be given alone with the MaintainRatio flag
.PARAMETER Height
The new height of the image. Can be given alone with the MaintainRatio flag
.PARAMETER ImagePath
The path to the image being resized
.PARAMETER MaintainRatio
Maintain the ratio of the image by setting either width or height. Setting both width and height and also this parameter
results in an error
.PARAMETER Percentage
Resize the image *to* the size given in this parameter. It's imperative to know that this does not resize by the percentage but to the percentage of
the image.
.PARAMETER SmoothingMode
Sets the smoothing mode. Default is HighQuality.
.PARAMETER InterpolationMode
Sets the interpolation mode. Default is HighQualityBicubic.
.PARAMETER PixelOffsetMode
Sets the pixel offset mode. Default is HighQuality.
.EXAMPLE
Resize-Image -Height 45 -Width 45 -ImagePath "Path/to/image.jpg"
.EXAMPLE
Resize-Image -Height 45 -MaintainRatio -ImagePath "Path/to/image.jpg"
.EXAMPLE
#Resize to 50% of the given image
Resize-Image -Percentage 50 -ImagePath "Path/to/image.jpg"
.NOTES
Written By:
Christopher Walker
#>
Function Resize-Image() {
[CmdLetBinding(
SupportsShouldProcess=$true,
PositionalBinding=$false,
ConfirmImpact="Medium",
DefaultParameterSetName="Absolute"
)]
Param (
[Parameter(Mandatory=$True)]
[ValidateScript({
$_ | ForEach-Object {
Test-Path $_
}
})][String[]]$ImagePath,
[Parameter(Mandatory=$False)][Switch]$MaintainRatio,
[Parameter(Mandatory=$False, ParameterSetName="Absolute")][Int]$Height,
[Parameter(Mandatory=$False, ParameterSetName="Absolute")][Int]$Width,
[Parameter(Mandatory=$False, ParameterSetName="Percent")][Double]$Percentage,
[Parameter(Mandatory=$False)][System.Drawing.Drawing2D.SmoothingMode]$SmoothingMode = "HighQuality",
[Parameter(Mandatory=$False)][System.Drawing.Drawing2D.InterpolationMode]$InterpolationMode = "HighQualityBicubic",
[Parameter(Mandatory=$False)][System.Drawing.Drawing2D.PixelOffsetMode]$PixelOffsetMode = "HighQuality",
[Parameter(Mandatory=$False)][String]$NameModifier = "resized"
)
Begin {
If ($Width -and $Height -and $MaintainRatio) {
Throw "Absolute Width and Height cannot be given with the MaintainRatio parameter."
}
If (($Width -xor $Height) -and (-not $MaintainRatio)) {
Throw "MaintainRatio must be set with incomplete size parameters (Missing height or width without MaintainRatio)"
}
If ($Percentage -and $MaintainRatio) {
Write-Warning "The MaintainRatio flag while using the Percentage parameter does nothing"
}
}
Process {
ForEach ($Image in $ImagePath) {
$Path = (Resolve-Path $Image).Path
$Dot = $Path.LastIndexOf(".")
#Add name modifier (OriginalName_{$NameModifier}.jpg)
$OutputPath = $Path.Substring(0,$Dot) + "_" + $NameModifier + $Path.Substring($Dot,$Path.Length - $Dot)
$OldImage = New-Object -TypeName System.Drawing.Bitmap -ArgumentList $Path
# Grab these for use in calculations below.
$OldHeight = $OldImage.Height
$OldWidth = $OldImage.Width
If ($MaintainRatio) {
$OldHeight = $OldImage.Height
$OldWidth = $OldImage.Width
If ($Height) {
$Width = $OldWidth / $OldHeight * $Height
}
If ($Width) {
$Height = $OldHeight / $OldWidth * $Width
}
}
If ($Percentage) {
$Product = ($Percentage / 100)
$Height = $OldHeight * $Product
$Width = $OldWidth * $Product
}
$Bitmap = New-Object -TypeName System.Drawing.Bitmap -ArgumentList $Width, $Height
$NewImage = [System.Drawing.Graphics]::FromImage($Bitmap)
#Retrieving the best quality possible
$NewImage.SmoothingMode = $SmoothingMode
$NewImage.InterpolationMode = $InterpolationMode
$NewImage.PixelOffsetMode = $PixelOffsetMode
$NewImage.DrawImage($OldImage, $(New-Object -TypeName System.Drawing.Rectangle -ArgumentList 0, 0, $Width, $Height))
If ($PSCmdlet.ShouldProcess("Resized image based on $Path", "save to $OutputPath")) {
$Bitmap.Save($OutputPath)
}
$Bitmap.Dispose()
$NewImage.Dispose()
$OldImage.Dispose()
}
}
}
@someshinyobject
Copy link
Author

Fixed an issue with the percentage variable.

@adbertram
Copy link

Thanks for this function! I'm including it in an upcoming TechSnips.io video I'm doing. You may want to include Add-Type -AssemblyName 'System.Drawing' though. It won't work without it loaded.

Good work on this. Feel free to join us if you'd like. ;) https://www.techsnips.io/join-us

@piers7
Copy link

piers7 commented Oct 15, 2018

I tried this on a bunch of JPEGs at 50%, but whilst the pixel sizes were halved, the file sizes all went up! Think there is a quality setting you need to expose somewhere.

@jonathancounihan
Copy link

@piers7, You must save the file as a jpeg, the default value is png.

ie use

    $Bitmap.Save($OutputPath, [System.Drawing.Imaging.ImageFormat]::Jpeg)

@kirillito
Copy link

Probably worth to add $OldImage.Dispose() next to

            $Bitmap.Dispose()
            $NewImage.Dispose()

to release original files.

@someshinyobject
Copy link
Author

@Kirill Yea that's a great idea! Thanks. Updated the gist.

@OutOfThisPlanet
Copy link

OutOfThisPlanet commented Sep 20, 2019

I used your function as a starting place for a project I was working on (I really like it, thanks!)

One addition I added was the ability of grabbing the "Date Taken" attribute from a photograph.

The code for grabbing that property was something like this:

        $Image = "\\Full\Path\To\Image.jpg"
        [reflection.assembly]::LoadWithPartialName("System.Drawing") | Out-Null
        $pic = New-Object System.Drawing.Bitmap($Image)
        $bitearr = $pic.GetPropertyItem(36867).Value 
        $string = [System.Text.Encoding]::ASCII.GetString($bitearr) 
        $DateTime = [datetime]::ParseExact($string,"yyyy:MM:dd HH:mm:ss`0",$Null)
        $FormattedDate = Get-Date $DateTime -Format "yyyyMMdd"
        $FormattedDate
        $pic.Dispose() 

@khima-xceleration
Copy link

how to preserve the image's transparent background ?

@jerry-ne
Copy link

Great function, thx! As @adbertram wrote, adding Add-Type -AssemblyName 'System.Drawing' is necessary for proper working!

@johandanforth
Copy link

Nice stuff! Helped me save a lot of time, and worked nice. Perhaps a -Replace and -Verbose option would be nice

@johandanforth
Copy link

how to preserve the image's transparent background ?

my .png files kept their transparency fine

@invokeImmediately
Copy link

I appreciate the helpful function! It saved me a lot of time looking for needles in haystacks of hi-res photos sitting behind a slow network connection.

I did find a minor bug that only affects processing more than one image. Say that I set the $MaintainRatio flag and provide the $Width as the absolute basis for resizing an array of images with length > 1. I would expect the height of each image to be calculated based on this provided width and each image's original aspect ratio. But because lines 89 through 94 are inside the For-Each, this is not technically what happens. The first iteration through the For-Each, the expected behavior does occur. Alas, every additional iteration through the For-Each after the first ends up recalculating the $Width based on the $Height that was determined during the first iteration. In effect, the original $Width parameter gets ignored after the first image is processed. Because of this, there is a chance that most images processed will end up with an unexpected width.

@nate8282
Copy link

Worked great. I added the following line so I could batch convert all images within a folder. Thanks!

$ImagePath = (Get-ChildItem -Path $ImagePath).FullName

@brokedarius
Copy link

nice, maybe you could add some test-paths to see if the image output path already exists and offer to overwrite or skip?

@dabbinavo
Copy link

This does not work anymore for me on macos. I get "System.Drawing.Common is not supported on this platform.".
Think this is due to the following breaking change: https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/system-drawing-common-windows-only

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment