In a previous post I demonstrated how to read Exif Metadata from Image Files – Reading Exif Metadata from Image Files. It was suggested by a reader that it would be nice to be able to read a folder of images and put the results in a file.

Here is a script which does that. You select the folder and after processing, a CSV is created in that location containing various Exif info.

ExifInfoByFolder

ExifInfoByFolder-CSVExifInfoByFolder-CSV2ExifInfoByFolder-CSV3

The Exif format and processing was previously discussed so I’ll just comment briefly on the script changes.

The previous version included the below Exif tags –

  • Color Space
  • Contrast
  • Date Taken
  • Exposure Time
  • Exposure Mode
  • f-Stop
  • File Path
  • Flash
  • Focal Length
  • Height
  • Height Resolution
  • ISO
  • Camera Maker
  • Camera Model
  • Metering
  • Orientation
  • Saturation
  • Scene Type
  • Sharpness
  • White Balance
  • Width
  • Width Resolution

In this version I included the below additional Exit tags –

  • Aperture
  • Maximum Aperture
  • Brightness
  • Compressed Bits per Pixel
  • Compression Type
  • Custom Rendered
  • Digital Zoom Ratio
  • Exif Version
  • Exposure Compensation
  • File Source
  • Focal Length in 35mm Format
  • Gain Control
  • Light Source
  • Sensing Method
  • Shutter Speed
  • Software Version
  • Subject Distance
  • Subject Distance Range

The interface was simplified to just a TextBox and three Buttons and a couple Labels. I first hide the console then setup the main WinForm.

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

# Hide Console
Add-Type -Name Window -Namespace Console -MemberDefinition '
 [DllImport("Kernel32.dll")]
 public static extern IntPtr GetConsoleWindow();

[DllImport("user32.dll")]
 public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
 '
 function Hide-Console {
 $consolePtr = [Console.Window]::GetConsoleWindow()
 [Console.Window]::ShowWindow($consolePtr, 0)
 }
 If (-not $psISE) {hide-console}

# Set Global Values
$global:output = @()
$global:folderName = ""

# Main Form 
$mainForm = New-Object System.Windows.Forms.Form
$mainForm.Font = "Comic Sans MS,8.25"
$mainForm.Text = " Image File Exif MetaData By Folder"
$mainForm.FormBorderStyle = "FixedDialog"
$mainForm.ForeColor = "White"
$mainForm.BackColor = "DarkBlue"
$mainForm.width = 600
$mainForm.height = 250

# Title Label
$titleLabel = New-Object System.Windows.Forms.Label
$titleLabel.Font = "Comic Sans MS,14"
$titleLabel.ForeColor = "Yellow"
$titleLabel.Location = "30,20"
$titleLabel.Size = "400,30"
$titleLabel.Text = "Image File Exif MetaData by Folder"
$mainForm.Controls.Add($titleLabel)er"
$mainForm.Controls.Add($titleLabel)

Next is the TextBox for the path to the images folder and the Buttons to Browse, Process and Exit.

# Input Box
$textBoxIn = New-Object System.Windows.Forms.TextBox
$textBoxIn.Location = "35, 70"
$textBoxIn.Size = "500, 20"
$textBoxIn.Text = ""
$mainForm.Controls.Add($textBoxIn)

# Input Box Label
$ProcessLabel = New-Object System.Windows.Forms.Label
$ProcessLabel.Location = "35, 100"
$ProcessLabel.Size = "300, 23"
$ProcessLabel.ForeColor = "White" 
$ProcessLabel.Text = "Input Images Folder"
$ProcessLabel.Font = "Comic Sans MS,12"
$mainForm.Controls.Add($ProcessLabel)

# Browse Input Button
$buttonBrowse = New-Object System.Windows.Forms.Button
$buttonBrowse.Location = "35, 150"
$buttonBrowse.Size = "75, 23"
$buttonBrowse.ForeColor = "Red"
$buttonBrowse.BackColor = "White"
$buttonBrowse.Text = "Browse"
$buttonBrowse.add_Click({selectFolder})
$mainForm.Controls.Add($buttonBrowse)

# Process Button
$buttonProcess = New-Object System.Windows.Forms.Button
$buttonProcess.Location = "240,150"
$buttonProcess.Size = "75, 23"
$buttonProcess.ForeColor = "Red"
$buttonProcess.BackColor = "White"
$buttonProcess.Text = "Process"
$buttonProcess.add_Click({processFiles})
$mainForm.Controls.Add($buttonProcess)

# Exit Button 
$exitButton = New-Object System.Windows.Forms.Button
$exitButton.Location = "450,150"
$exitButton.Size = "75,23"
$exitButton.ForeColor = "Red"
$exitButton.BackColor = "White"
$exitButton.Text = "Exit"
$exitButton.add_Click({$mainForm.close()})
$mainForm.Controls.Add($exitButton)

I also added a StatusStrip Class line on the bottom of the form to display progress. In it I show the file names as they’re processed and indicate completion.

# Status Strip and Label
$statusStrip = New-Object System.Windows.Forms.StatusStrip
$statusStrip.ForeColor = "Blue"
$statusStrip.BackColor = "White"

$statusLabel = New-Object System.Windows.Forms.ToolStripStatusLabel
[void]$statusStrip.Items.Add($statusLabel)
$statusLabel.AutoSize = $true
$statusLabel.ForeColor = "Blue"
$statusLabel.BackColor = "White"
$statusLabel.Text = "Ready!"
$mainForm.Controls.Add($statusStrip)

The files are selected by folder using the FolderBrowserDialog class.

function selectFolder {
    $statusLabel.Text = "Ready!"
	$selectForm = New-Object System.Windows.Forms.FolderBrowserDialog
	$getKey = $selectForm.ShowDialog()
	If ($getKey -eq "OK") {
        	$textBoxIn.Text = $selectForm.SelectedPath
	}
} # End SelectFolder

In the main loop I initialize and fill a array with the names of the files. The output filename is set and the StatusStrip is updated. The StatusStrip.Refresh() allows each filename to be written to the Status Line as it is processed without the normally buffering. Finally I attempt to load the image space within a try/catch so that if the load fails I know the file does not contain an image and I set a variable accordingly. The loop will run through all files, skipping the attempt to gathering tags from non-image files.

function processFiles {
    $global:folderName = $textBoxIn.Text

    # Get All Files from Select Path
    $files = @()
    $files = Get-ChildItem $global:folderName
    # Process All Files
    Foreach ($file in $files) {
        $data = "" | Select filename, aperture, maxAperture, brightness, colorSpace, compressedBitsPerPixel, `
                            compression, contrast, customRendered, dateTaken, digitalZoomRatio, exposureTime,`
                            exifVersion, exposureBias, exposureMode, fileSource, fstop, filepath, `
                            flash, focalLength, focalLength35mmFormat, gainControl, height, heightRes, iso, `
                            lightSource, cameraMaker, cameraModel, cameraSoftware, meteringMode, orientation, `
                            saturation, sceneType, sensingMethod, sharpness, shutter, subjectDistance, `
                            subjectDistanceRange, whiteBalance, width, widthRes 

        # Load image from file
        $filefullname = $global:folderName + "\" + $file
        $statusLabel.Text = "Processing " + $filefullname
        $statusStrip.Refresh()
        $imageType = "Y"
        Try {                       
            $photo = [System.Drawing.Image]::FromFile($filefullname)
        } Catch {
            $imageType = "N"
        }

I only gather the image tags if the file is an image type. I try/catch each value just to control error messages. Not all tags exist on every file as different manufactures publish different tags.

        # If Image File Exist Then Get Properties and Write File
        If ($imageType -eq "Y") {
            # Aperture
            Try { 
                $apertureProperty = $photo.GetPropertyItem(37378)
                if ($apertureProperty -ne $null){$aperture = MakeNumber($apertureProperty)}
                if ($aperture -ne $null){$data.aperture = $aperture}
            } Catch {}

Finally the output CSV file is written to disk from the $global:output array it has been gathered into.

    #write output
    $statusLabel.Text = "Complete!"
    $outputFile = $global:folderName + "\"  + "ExifInfo" + ".csv"
    $global:output | Export-CSV $outputFile -NoTypeInformation

The complete script can be downloaded here – ExifInfoByFolder.ps1

Advertisements