Expert Solution for 2011 Scripting Games Advanced Event 8: Use PowerShell to Resize Images and to Remove EXIF Data


Summary: Microsoft PowerShell MVP, Marc van Orsouw, resizes images and removes EXIF metadata while solving 2011 Scripting Games Advanced Event 8.

Microsoft Scripting Guy, Ed Wilson, here. Marc van Orsouw (MoW) is our expert commentator for Advanced Event 8.

Image of Marc van Orsouw

Marc has been a Windows PowerShell MVP for the last six years. He is best known for his blog, and for his utilities for Windows PowerShell, such as PowerTab and the PowerShell WMI explorer. He was among the very early adapters of Windows PowerShell, has  lots of hands-on experience , has spoken about Windows PowerShell at diverse events, and he is running the PowerShell Script Club in Zurich for the Swiss IT user group. He is from the Netherlands, but he has been living in Switzerland for two years, and he is working as a technical consultant at WinWorkers Schweiz GmbH, mostly in the Microsoft System Center area…and of course, he is using automation and Windows PowerShell wherever feasible.

Worked solution

First, a disclaimer. This is not a production ready script, it just does enough to fulfill the requirements, but with some work, I think it would make a cool and handy tool and a good addition to the Imaging library.

When I started looking at Advanced Event 8, my first thought was to use no external dependencies. I knew it was possible to get the EXIF data by using .NET, and I have done that before. But whilst searching for info, I was reminded of the fact that a lot of refactoring has to be done to extract the interesting data, because a lot of properties need their own custom conversion (a good example is the GPS information that I really wanted to have in). Also see this post from James O’ Neil.

I knew that James’s library was included in the PowerShellPack module, and I decided to use that module in my solution after all. I was making a dependency on the PowerShellPack anyway, so I figured I could use WPK as well for the interface part, knowing that I could “steal” the basic interface from an article by Doug Finke.

However, when I got started, I missed some of the functionality that I needed and expected in the library from James. The Get-ImageProperty function was a bit too basic, and it did not do the job for me. The problem is not getting the values, but rather because they are byte arrays, converting the values to usable data.

And the Get-ImagePropery function only got me part of the way. The byte arrays are converted, but a lot of data is still not in a readable format, and it needs more conversion to be usable. This is shown in the following image.

Image of code

With further research aimed at a more complete solution, I found the information I was looking for—again on James’ blog, and there seemed to be a newer version of the image module available on MSDN.

After downloading and installing the module, I found Get-Exifdata, which did what I needed. It gives a much nicer list of properties, and it also did the GPS coordinates conversion—the work I did not want to do again myself. The use of this function is illustrated here. 

PS C:\powershell\SG2011> Get-Image ‘C:\Users\morsouw\Pictures\ww-xmasparty2010\P1000501.JPG’ | Get-Exif


LightSource      : Flash

FocalLength      : 4.1

CaptureMode      : Standard

FileSource       : Digital still camera

ExposureBias     : 0

DateTaken        : 15.12.2010 19:14:09

Orientation      : 0

Title            :

Sharpness        : Normal

Height           : 480

Keywords         :

Exposuretime     : 0.0666666666666667

FocalLength35mm  : 25

Model            : DMC-TZ10

Contrast         : Normal

Comment          :

ISO              : 800

ExposureMode     : Auto

Manufacturer     : Panasonic

Subject          :

Artist           :

Copyright        :

WhiteBalance     : Auto

Author           :

Flash            : Flash Auto, Flash fired

GPS              : 47°27’13.95″N  8°34’35.62″E

Software         : Ver.1.0

Path             : C:\Users\morsouw\Pictures\ww-xmasparty2010\P1000501.JPG

Width            : 640

MaxApperture     : 3.44

SubjectRange     :

ColorSpace       : sRGB

DigitalZoomRatio :

FNumber          : 3.3

MeteringMode     : Multi-Segment

ExposureProgram  : Program: Normal

Saturation       : Normal

After I got this output, I spent a lot of time searching, but it still saved me a lot of work to get decent information for the properties window, and I was ready to work on the interface part.

I tweaked the interface a bit to add the controls I needed. There were no big problems in doing that. I added some grid rows for the extra directory pickers, and an extra vertical grid to add the RTF box for the properties. It is not a fancy interface but, but I wanted to show that by using StackPanels and grids, you can easily build a basic interface, even in code, as long it you do not need it to be fancy. In a couple of minutes, I came up with the interface shown in the following image.

Image of interface

Then came the hard part…finding out how to remove ALL the EXIF data. Even in the newer imaging library, I could not find a good way to do this. I spent a lot of time figuring out a way to do this by using the .NET classes. Although I could set the simple text properties, I could not find out how to do what I wanted and remove all properties. I found some examples in C# to remove the binary data, but that looked a bit too “Brute Force.”

I later found out that I could remove the properties as easy as this, but I somehow completely overlooked the Remove method then.

$i = [System.Drawing.Image]::FromFile(‘C:\Users\morsouw\Pictures\ww-xmasparty2010\P1000501.JPG’ )

$i.PropertyItems |% {$i.RemovePropertyItem($} )

Then it struck me that it might be much easier to copy the image data to a new image and then save it instead of trying to remove the properties. I first came up with something like this:

$ip = [System.Drawing.Image]::FromFile(‘C:\powershell\SG2011\P1000500.JPG’).GetHbitmap() $new = [System.Drawing.Image]::FromHbitmap($ip)

But because the Resize method on the Get-Image function in the Image library came from the COM object, I had problems saving the image again because the SaveFile() method did not allow me to save the Get-Image function that was locked in the file.

Then I looked at the .NET image object again, and I found out that I could actually resize and remove all the EXIF data in one go by using the method shown here.


This enabled me to throw away the painfully acquired code to double save the image, first removing properties and then resizing. Therefore, I ended up only using the Get-Exifdata cmdlet from the library, and then the Resize function that I mentioned earlier.

One of the bigger problems I had with the library was that it was using a COM object that kept the file locked. Because I needed to open it in .NET and also remove the attributes, I kept having locked files.

Get-Image ‘C:\Users\morsouw\Pictures\ww-xmasparty2010\P1000501.JPG’ | gm


   TypeName: System.__ComObject#{f4243b65-3f63-4d99-93cd-86b6d62c5eb2}


 [System.Drawing.Image]::FromFile(‘C:\powershell\SG2011\SHARE_P1000500.JPG’) | gm


   TypeName: System.Drawing.Bitmap

As you can see in the previous code, one time the file is opened by using a COM object and the other time by using .NET. I dispose the .NET objects after use, and therefore unlock the files again.



For the extra points of this event, I added the Folder browsers, checking for the modules, and loading them when available at the start of the script. I also hacked in the Convert-All function (very dirty actually, and I should have done some refactoring for that). Hence, for the script to be really useful, it needs some cleaning up and additions like proportional scaling. But I think it shows how you can relatively easily (with some help from the community) build a small GUI tool in Windows PowerShell.

Enjoy and greetings,

The complete script will be posted in the Script Repository after this event is complete and Marc can get it uploaded.

Thank you, Marc.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy