Share via


PowerShell script to read metadata info from pictures

Question

Saturday, October 24, 2015 8:21 AM

Hi Guys,

I'm new to powershell scripts. I need a help with the below code:

Function Get-FileMetaData
{

Param([string[]]$folder)
foreach($sFolder in $folder)
{
$a = 0
$objShell = New-Object -ComObject Shell.Application
$objFolder = $objShell.namespace($sFolder)

foreach ($File in $objFolder.items())
{
$FileMetaData = New-Object PSOBJECT
for ($a ; $a -le 266; $a++)
{
if($objFolder.getDetailsOf($File, $a))
{
$hash += @{$($objFolder.getDetailsOf($objFolder.items, $a)) =
$($objFolder.getDetailsOf($File, $a)) }
$FileMetaData | Add-Member $hash
$hash.clear()
} #end if
} #end for
$a=0
$FileMetaData
} #end foreach $file
} #end foreach $sfolder
} #end Get-FileMetaData

$picdata = Get-FileMetaData -Folder 'C:\Users\mohad\Desktop\Shell\image' | select name, Dimensions, size, width, height
$picdata

My need is below:

1. I'm converting this code into exe, so the script should give info abt all the images from the folder in which it is started (the folder will always be named as "images" )
2. The result should be stored in a csv file name as "image data".
3. I want to extract additional info such as "horizontal/vertical resolution' of an image, color representation etc.

please help me!! TIA

All replies (18)

Saturday, October 24, 2015 2:02 PM ✅Answered | 1 vote

This is that:

** Get-FileMetaData <foldername> |select height,width,'horizontal resolution','vertical resolution'**

You just have to specify which properties you want out of the 256 that are captured.

\(ツ)_/


Tuesday, October 27, 2015 9:48 PM ✅Answered | 1 vote

That's because you are piping $picdata to the Export-CSV cmdlet, but you have never set $picdata equal to any value.  I suspect you intended for $picdata to contain the return data from your Get-FileMetaData function.

$picdata = Get-FileMetaData $Env:userprofile\Desktop\Shell\image |select name, height,width,'horizontal resolution','vertical resolution'
$picdata | Export-csv $Env:userprofile\Desktop\Shell\image\exportfile.csv

Or you can pipe your Get-FileMetaData directly to the Export-CSV cmdlet without using a variable at all

Get-FileMetaData $Env:userprofile\Desktop\Shell\image |select name, height,width,'horizontal resolution','vertical resolution' |
 Export-csv $Env:userprofile\Desktop\Shell\image\exportfile.csv

Saturday, October 24, 2015 10:39 AM

To read image EXIF data you must use a tool designed to read EXIF data.  Windows cannot read this data with any native API.  What you see are the common image EXIF properties which are hard coded into the shell.

The issue is that every imaging device vendor adds EXIF data in a proprietary format.  Alos different imaging standards use custom extensions to the image metadata.

start here: https://en.wikipedia.org/wiki/Exchangeable_image_file_format

and: http://www.codeproject.com/Articles/47486/Understanding-and-Reading-Exif-Data

\(ツ)_/


Saturday, October 24, 2015 10:42 AM

Here is a good tutorial: http://blogs.technet.com/b/heyscriptingguy/archive/2014/02/06/use-powershell-to-find-metadata-from-photograph-files.aspx

You will find that much of the camera data is in binary and has to be decoded.

\(ツ)_/


Saturday, October 24, 2015 10:49 AM

Thanks a lot for your answer. I'm new to programming and very new to powershell. I have managed to put this code together with great struggle and need the tweaks to be done. Can you please help me with modifying the code? like what should be replaced with what ? It would be of great help.


Saturday, October 24, 2015 10:51 AM

Thanks a ton for your reply . I'm new to programming and very new to powershell. I have managed to put this code together with great struggle and need the tweaks to be done.

The things which you are mentioning appears very technical to me. Can you please help me with the code? I just need the image info such as heihgt width size vertical/horizontal resolution etc.


Saturday, October 24, 2015 11:39 AM

Sorry but I cannot do this for you. The forum can answer specific questions about scripting but is not intended as a free code writing forum.

I suggest purchasing or downloading one of the numerous tools that can read EXIF data.  PowerShell cannot read EXIF data.  The Shell metadata is all you can get.  The camera data is in the extended EXIF.  Microsoft Photo Viewer can display some common camera data.  For specific camera data we usually have to use the vendors tools.  For example, Nikon provides extra camera data and also provides a viewer that can read it.

If the file metadata does not have what you want then you will have to use a different tool.

You can also get raw image data with the System.Drawing classes.  This requires advanced scripting skills.  Search for examples.

\(ツ)_/


Saturday, October 24, 2015 12:30 PM | 1 vote

Here is a better version that gets up to 256 properties. I ran it against my Nikon and Scanner data and all of the EXIF data is missing,  Only the basic and common items are read by Windows.

Function Get-FileMetaData{
    Param (
        [Parameter(
            Mandatory = $true,
            ValueFromPipeline=$true
        )][string]$FolderPath
    )
    
    begin{
        $shell = New-Object -ComObject Shell.Application
    }
    
    Process {
        $FolderPath
        $folder = $shell.namespace($FolderPath)
        $index=@{}
        for ($i = 0; $i -lt 256; $i++) {
            if ($tag = $folder.GetDetailsOf('.', $i)) {
                Try{
                    $index.Add($tag, $i)
                }
                Catch{ Write-Host $tag  -fore green}
            }
        }
        
        foreach ($fi in $folder.items()) {
            $data=@{}
            $index.GetEnumerator() |
            ForEach-Object{
                $value = $folder.getDetailsOf($fi, $_.Value)
                $data.Add($_.Name, $value)
            }
            [pscustomobject]$data
        }
        
    }
}

\(ツ)_/


Saturday, October 24, 2015 12:41 PM

Good thing I looked.

Scott Hanselmann has don a blog on how to use a free DLL to get picture data in PowerShell.

http://www.hanselman.com/blog/AccessingEXIFPhotoDataFromJPEGsWithPowerShell.aspx

I am pretty sure that it will not get the extended camera data because it is not vendor specific and I see no facility to add vendor IDs to the mix.

\(ツ)_/


Saturday, October 24, 2015 1:13 PM

Hiii, Thanks a jillion for your help..I will try out the code and keep you posted..

FYI, I just want an images basic information such as height width size vertical/horizontal resolution etc.

-> making sure the converted exe runs in the folder from where it is executed (NOTE: The exe will be used by different users).

-> The result should be imported to a csv file in the same place where the exe is present.

I dont want the camera model etc etc or any other complicated info from an image.  The code which I posted above gave all the details...Have I misunderstood something here? 


Saturday, October 24, 2015 2:07 PM

Ok is it possible to read only selected properties instead of all the 256? Cos in case if a folder has 100 images, then the time to read all this and display the O/p will take long time


Saturday, October 24, 2015 2:19 PM

Ok is it possible to read only selected properties instead of all the 256? Cos in case if a folder has 100 images, then the time to read all this and display the O/p will take long time

See my last post.  It has an example.

\(ツ)_/


Saturday, October 24, 2015 2:41 PM

I used the below code for selecting the property :

Get-FileMetaData C:\Users\mohad\Desktop\Shell\image |select name, height,width,'horizontal resolution','vertical resolution'

But here, we should mention the path from where the image details should be fetched. But when i convert this into exe and distributed to others, the username will differ and the exe wont run. is there a way to solve this?


Saturday, October 24, 2015 2:53 PM | 1 vote

I used the below code for selecting the property :

Get-FileMetaData C:\Users\mohad\Desktop\Shell\image |select name, height,width,'horizontal resolution','vertical resolution'

But here, we should mention the path from where the image details should be fetched. But when i convert this into exe and distributed to others, the username will differ and the exe wont run. is there a way to solve this?

Instead of hard coding the path in your code, you should use the "$Env:USERPROFILE" environmental variable to get the profile path if the directory is always going to be on the user's desktop in a Shell\image folder.  Otherwise, you need to either have you script prompt for input or make your path relative to your script.


Saturday, October 24, 2015 3:15 PM

OK so is this the format to be used ?:

Get-FileMetaData $Env:userprofile\Desktop\Shell\image |select name, height,width,'horizontal resolution','vertical resolution'

Saturday, October 24, 2015 4:12 PM | 1 vote

OK so is this the format to be used ?:

Get-FileMetaData $Env:userprofile\Desktop\Shell\image |select name, height,width,'horizontal resolution','vertical resolution'

 

yes, that should do it.


Monday, October 26, 2015 3:48 AM

I used the below code:

Function Get-FileMetaData{
    Param (
        [Parameter(
            Mandatory = $true,
            ValueFromPipeline=$true
        )][string]$FolderPath
    )
    
    begin{
        $shell = New-Object -ComObject Shell.Application
    }
    
    Process {
        $FolderPath
        $folder = $shell.namespace($FolderPath)
        $index=@{}
        for ($i = 0; $i -lt 256; $i++) {
            if ($tag = $folder.GetDetailsOf('.', $i)) {
                Try{
                    $index.Add($tag, $i)
                }
                Catch{ Write-Host $tag  -fore green}
            }
        }
        
        foreach ($fi in $folder.items()) {
            $data=@{}
            $index.GetEnumerator() |
            ForEach-Object{
                $value = $folder.getDetailsOf($fi, $_.Value)
                $data.Add($_.Name, $value)
            }
            [pscustomobject]$data
        }
        
    }
}

Get-FileMetaData $Env:userprofile\Desktop\Shell\image |select name, height,width,'horizontal resolution','vertical resolution'
$picdata | Export-csv $Env:userprofile\Desktop\Shell\image\exportfile.csv

But I'm getting this error message while writing the data into a CSV:

Export-Csv : Cannot bind argument to parameter 'InputObject' because it is null.
At C:\Users\mohad\Desktop\Shell\micro.ps1:40 char:12
+ $picdata | Export-csv $Env:userprofile\Desktop\Shell\image\exportfile.csv
+            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Export-Csv], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.ExportCsvCommand
W

Friday, May 22, 2020 2:30 PM

Yes, very late to the party, still - take a look at the following code for JPG with JFIF header:

# block read$bytes = Get-Content $jpg_file_name -AsByteStream -TotalCount 18
$bytes | Format-Hex
## piece by piece read
$file_jpg = Get-Item $jpg_file_name
$stream_jpg = $file_jpg.OpenRead()

# SOI - Start of Image : FF D8
[byte[]]$soi = 0, 0
if(! $stream_jpg.Read($soi, 0, 2) -eq 2) { EXIT}
"$($soi[0].ToString("X")) $($soi[1].ToString("X"))"

# APP0 - App0 marker : FF E0
[byte[]]$app0 = 0, 0
if(! $stream_jpg.Read($app0, 0, 2) -eq 2) { EXIT}
"$($app0[0].ToString("X")) $($app0[1].ToString("X"))"

# length of JFIF APP0 marker segment, excluding App0 marker
[byte[]]$blen = 0, 0
if(! $stream_jpg.Read($blen, 0, 2) -eq 2) { EXIT}
[INt16]$len = $blen[1] + 256*$blen[0]
$len

# JFIF identifier - 4A 46 49 46 00 = "JFIF" in ASCII, terminated by a null byte
[byte[]]$jfif = 0, 0, 0, 0, 0
if(! $stream_jpg.Read($jfif, 0, 5) -eq 5) { EXIT}
"$($jfif[0].ToString("X")) $($jfif[1].ToString("X")) $($jfif[2].ToString("X")) $($jfif[3].ToString("X")) $($jfif[4].ToString("X"))"

# JFIF version
[byte[]]$jfifver = 0, 0
if(! $stream_jpg.Read($jfifver, 0, 2) -eq 2) { EXIT}
"Version $($jfifver[0].ToString("00")).$($jfifver[1].ToString("00"))"

# Density units - 00, 01, or 02
[byte[]]$densu = 255
if(! $stream_jpg.Read($densu, 0, 1) -eq 1) { EXIT}
$densu[0].ToString("00")

# Density (DPI) 
[byte[]]$dens = 0, 0, 0, 0
if(! $stream_jpg.Read($dens, 0, 4) -eq 4) { EXIT}
$hdpi = $dens[1]+256*$dens[0]
$vdpi = $dens[3]+256*$dens[2]
"Horizontal: $($hdpi)dpi ; Vertical: $($vdpi)dpi"

# thumbnail info following ...

Of course, it's easy to adapt something like this for whatever format one needs.