Monthly Archives: September 2014

Resize image and preserve ratio with Powershell

My recently created company website Keluro has two blogs: one in french and one in english. The main blog pages are a preview list of the existing posts. I gave the possibility to the writer to put a thumbnail image in the preview. It’s a simply <img /> tag where a css class is responsible for the resizing and to display the image in a 194px X 194px box while keeping the original aspect ratio. Most of the time this preview is a reduction of an image that is displayed in the blog post. Everything was fine until I found out that the these blog pages did not received a good mark while inspecting them with PageSpeedInsights . It basically says that the thumbnails were not optimized enough… For SEO reasons I want these blog pages to load quickly so I needed to resize these images once for all even if it has to duplicate the image.

Resizing and keeping aspect ratio

Resizing two pictures with landscape and portrait aspect ratio to make them fill a given canvas.

I think that most of us already had to do such kind of image resizing task. You can use many available software to do that: Paint, Office Picture Manager, Gimp, Inkscape etc. However, when it comes to manipulate many pictures, it could be really useful to use a script. Let me share with you this Powershell script that you can use to resize your .jpg pictures. Note that there is also a quality parameter (from 1 to 100) that you can use if you need to compress more the image.

Param ( [Parameter(Mandatory=$True)] [ValidateNotNull()] $imageSource,
[Parameter(Mandatory=$True)] [ValidateNotNull()] $imageTarget,
[Parameter(Mandatory=$true)][ValidateNotNull()] $quality )

if (!(Test-Path $imageSource)){throw( "Cannot find the source image")}
if(!([System.IO.Path]::IsPathRooted($imageSource))){throw("please enter a full path for your source path")}
if(!([System.IO.Path]::IsPathRooted($imageTarget))){throw("please enter a full path for your target path";)}
if ($quality -lt 0 -or $quality -gt 100){throw( "quality must be between 0 and 100.")}

[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$bmp = [System.Drawing.Image]::FromFile($imageSource)

#hardcoded canvas size...
$canvasWidth = 194.0
$canvasHeight = 194.0

#Encoder parameter for image quality
$myEncoder = [System.Drawing.Imaging.Encoder]::Quality
$encoderParams = New-Object System.Drawing.Imaging.EncoderParameters(1)
$encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter($myEncoder, $quality)
# get codec
$myImageCodecInfo = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders()|where {$_.MimeType -eq 'image/jpeg'}

#compute the final ratio to use
$ratioX = $canvasWidth / $bmp.Width;
$ratioY = $canvasHeight / $bmp.Height;
$ratio = $ratioY
if($ratioX -le $ratioY){
  $ratio = $ratioX
}

#create resized bitmap
$newWidth = [int] ($bmp.Width*$ratio)
$newHeight = [int] ($bmp.Height*$ratio)
$bmpResized = New-Object System.Drawing.Bitmap($newWidth, $newHeight)
$graph = [System.Drawing.Graphics]::FromImage($bmpResized)

$graph.Clear([System.Drawing.Color]::White)
$graph.DrawImage($bmp,0,0 , $newWidth, $newHeight)

#save to file
$bmpResized.Save($imageTarget,$myImageCodecInfo, $($encoderParams))
$graph.Dispose()
$bmpResized.Dispose()
$bmp.Dispose()

Now suppose that you have saved and named the script above “MakePreviewImages.ps1”. You may use it in a loop statement such as the following one where we assume that MakePreviewImages.ps1 is located under the current directory and the images are in a subfolder called “images”.

Get-ChildItem .images -Recurse -Include *.jpg | Foreach-Object{
   $newName = $_.FullName.Substring(0, $_.FullName.Length - 4) + "_resized.jpg"
     ./MakePreviewImages.ps1 $_.FullName $newName 75
   }