Home

Reading Exif Metadata from Image Files By Folder

2 Comments

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

PowerShell Prevent Screen Saver from Starting

2 Comments

This script will keep the Windows Screen Saver from coming on. The script runs in the System Tray using NotifyIcon, while being controlled by a Timer object, as it toggles the NumLock key.

Toggling the NumLock (or CapsLock) key provide enough interaction to the system to prevent the Screen Saver from enabling. I have tested under Windows XP, 7 and 8 Screen Savers. Also tested on Domain Member workstations running Windows 2008 R2 Functional Level AD Group Policy enabled Screen Savers.

Sending regular keystrokes or jiggling the mouse a few pixels no longer works to prevent the Screen Saver from enabling.

Here is the Script running in the System Tray, showing the MouseOver –

NoSSaver

First I add the .Net Namespaces, hide the Host Console and setup the Main Form. It has almost no properties and gets hidden.

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

# Hide Host 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()
    [void][Console.Window]::ShowWindow($consolePtr, 0)
}
hide-console

# Main Form Object
$mainForm = New-Object System.Windows.Forms.form
# Hide Main Form Window
$mainForm.ShowInTaskbar = $false
$mainForm.WindowState   = "minimized"

Next I added a Timer object. It will count down a predefined time and then process an event, calling the sendKeys function. The Timer Interval Delay is in Milliseconds i.e. 60k = 1 minute. The add_Tick set the event, calling the Function. The Timer control is then started.

# Timer Object
$Timer          = New-Object System.Windows.Forms.Timer
$Timer.Interval = 30000 
$Timer.add_Tick({sendKeys}) 
$Timer.start()

I then define the “About” information form. I define the Form, add a Label and a ListBox with Items containing the script description. Lastly an “OK” Button to Close the Form.

# 'About' Window Setup
# About Form Object
$aboutForm      = New-Object system.Windows.Forms.Form
$aboutForm.Size = New-Object System.Drawing.Size(250,220) 
$aboutForm.Font = New-Object System.Drawing.Font("Calibri",12,[System.Drawing.FontStyle]::Regular)
$aboutForm.Icon = $Icon
$aboutForm.Text = " About NoSSaver"
$aboutForm.StartPosition = "CenterScreen"
# About Label Object
$aboutLabel          = New-Object System.Windows.Forms.Label
$aboutLabel.font     = new-object system.drawing.font("Calibri",14,[system.drawing.fontstyle]::bold)
$aboutLabel.Location = New-Object System.Drawing.Size(10,10) 
$aboutLabel.Size     = New-Object System.Drawing.Size(220,20) 
$aboutLabel.Text     = "NoSSaver Script"
# Add About Label to Form
$aboutForm.Controls.Add($aboutLabel) 
# About ListBox Object
$aboutListBox          = New-Object System.Windows.Forms.ListBox 
$aboutListBox.Font     = new-object system.drawing.font("Calibri",11,[system.drawing.fontstyle]::regular)
$aboutListBox.Location = New-Object System.Drawing.Size(16,40) 
$aboutListBox.Size     = New-Object System.Drawing.Size(200,20) 
$aboutListBox.Height   = 100
# Add Info to About ListBox
[void] $aboutListBox.Items.Add("     NoSSaver.ps1")
[void] $aboutListBox.Items.Add("  Prevents the Windows")
[void] $aboutListBox.Items.Add("Screensaver from starting ")
[void] $aboutListBox.Items.Add("  by Wayne Lindimore")
[void] $aboutListBox.Items.Add("adminscache.wordpress.com")
# Add About ListBox to Form
$aboutForm.Controls.Add($aboutListBox) 
# About OK Button Object
$OKButton          = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(70,150)
$OKButton.Size     = New-Object System.Drawing.Size(75,23)
$OKButton.Text     = "OK"
$OKButton.Add_Click({$aboutForm.Close()})
# Add About OK Button to Form
$aboutForm.Controls.Add($OKButton)

NoSSaverContext

NoSSaverAbout1

Next is the NotifyIcon object. First I decode the embedded icon. I encoded the icon with my PowerShell Base64 Encoding of Images script. After encoding the image into a text file with Image2Base64, I insert the text block into the script using a Here-String.

# NotifyIcon Setup
# Decode and Set Icon
[string]$IconB64=@"
AAABAAQAGBgAAAEAIACICQAARgAAACAgAAABACAAqBAAAM4JAAAQEAAAAQAgAGgEAAB2GgAAFhYAAAEA
IAAQCAAA3h4AACgAAAAYAAAAMAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
.
.
.
i/aIjYv2ipGPlAAA//8AAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///AA+ABgAMAABADAAAwA8AAcAP4A
PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAA///wA
"@

To decode the icon and make it available for use, the string is converted into a MemoryStream. The stream has a backing store of memory instead disk or a network connection, which we’ll need as we have no file to use. A bitmap image is made from the stream. We collect the icons handle using the GetHicon() method from the bitmap image. The icon is created using the FromHandle method. We now have an icon image available for use have been read from the source script file and decoded and created in memory instead of read from a disk file.

$Iconstream = [System.IO.MemoryStream][System.Convert]::FromBase64String($IconB64)
$Iconbmp    = [System.Drawing.Bitmap][System.Drawing.Image]::FromStream($Iconstream)
$Iconhandle = $Iconbmp.GetHicon()
$Icon       = [System.Drawing.Icon]::FromHandle($Iconhandle)

I then define the NotifyIcon and make it visible. Within the NotifyIcon I define a ContextMenu which is visible on a right-click of the icon. I add a MenuItem to it for the Exit option. Lastly I define a click event action to clean up and exit the script.

# NotifyIcon Objects
$NotifyIcon  = New-Object System.Windows.Forms.NotifyIcon
$ContextMenu = New-Object System.Windows.Forms.ContextMenu
$NotifyIcon.ContextMenu = $ContextMenu
$NotifyIcon.Icon = $Icon
$NotifyIcon.Visible = $True
$NotifyIcon.Text = "NoSSaver"
$MenuItem1 = New-Object System.Windows.Forms.MenuItem -ArgumentList "About..."
$MenuItem2 = New-Object System.Windows.Forms.MenuItem -ArgumentList "Exit"
# Add Context MenuItems
[void] $contextMenu.MenuItems.Add($MenuItem1)
[void] $contextMenu.MenuItems.Add($MenuItem2)
# Add 'About' MenuItem
$MenuItem1.add_Click({
    [void] $aboutForm.ShowDialog() 
})
# Add Exit MenuItem
$MenuItem2.add_Click({
	$Timer.stop()
	$NotifyIcon.Visible = $False
	$mainForm.close()
})

The Timer event calls the sendKeys Function which toggles the NumLock key on and off.

Function sendKeys() {           
    $shell = New-Object -com "Wscript.Shell"
    $shell.sendkeys(“{NumLock}{NumLock}”)
    $shell.Dispose
}

If started from a Command file the script will start in the System Tray with the PowerShell host box hidden.

powershell.exe -windowstyle hidden -nologo -command .\NoSleep.ps1

Download the complete script here – NoSSaver.ps1

PowerShell Convert Text to Braille Image

Leave a comment

Braille characters are small rectangular blocks called cells that contain tiny palpable bumps called raised dots. The number and arrangement of these dots distinguish one character from another. This script calls a Web Service from webservicex.net which translates text into an image of the Braille cells.

Text2Braille

The images created are interesting. They contain randomness but with a certain pattern because of the limited number of cells that can be formed.

Text2Braille_20150104_164737

The script starts with defining the main WinForms Form.

Add-Type -AssemblyName System.Windows.Forms
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$global:outFile = ""

# Main Form 
$mainForm = New-Object System.Windows.Forms.Form
$mainForm.Location = "200, 200"
$mainForm.Font = "Comic Sans MS,9"
$mainForm.FormBorderStyle = "FixedDialog"
$mainForm.Text = " Convert Text to Braille Image"
$mainForm.ForeColor = "White"
$mainForm.BackColor = "Wheat"
$mainForm.Size = "610, 410"

Next is added the Input TextBox and its Label.

# Input Text Label
$inputTextLabel = New-Object System.Windows.Forms.Label
$inputTextLabel.Location = "100,8"
$inputTextLabel.ForeColor = "MediumBlue"
$inputTextLabel.Size = "50, 22"
$inputTextLabel.Text = "Text"
$mainForm.Controls.Add($inputTextLabel)

# Input Text TextBox
$inputTextTextBox = New-Object System.Windows.Forms.TextBox
$inputTextTextBox.Location = "20,30"
$inputTextTextBox.Size = "230,330"
$inputTextTextBox.MultiLine = $true
$inputTextTextBox.ForeColor = "MediumBlue"
$inputTextTextBox.BackColor = "White"
$inputTextTextBox.Text = ""
$mainForm.Controls.Add($inputTextTextBox)

The PictureBox for outputting the returned image is created.

# PictureBox
$pictureBox = New-Object System.Windows.Forms.PictureBox
$pictureBox.Location = "260, 30"
$pictureBox.ClientSize = "230, 330"
$pictureBox.BackColor = "Black"
$pictureBox.SizeMode = "StretchImage"
$mainForm.Controls.Add($pictureBox)

# PictureBox Label
$PictureBoxLabel = New-Object System.Windows.Forms.Label
$PictureBoxLabel.Location = "350,8"
$PictureBoxLabel.ForeColor = "MediumBlue"
$PictureBoxLabel.Size = "110, 22"
$PictureBoxLabel.Text = "Braille"
$mainForm.Controls.Add($PictureBoxLabel)

Another TextBox for the Font Size is setup. Font Size in this context is the size of the Braille cells.

# Font Size Label
$fontSizeLabel = New-Object System.Windows.Forms.Label
$fontSizeLabel.Location = "500,130"
$fontSizeLabel.ForeColor = "MediumBlue"
$fontSizeLabel.Size = "60, 22"
$fontSizeLabel.Text = "Font Size"
$mainForm.Controls.Add($fontSizeLabel)

# Font Size TextBox
$fontSizeTextBox = New-Object System.Windows.Forms.TextBox
$fontSizeTextBox.Location = "560,130"
$fontSizeTextBox.Size = "30,20"
$fontSizeTextBox.ForeColor = "MediumBlue"
$fontSizeTextBox.BackColor = "White"
$fontSizeTextBox.Text = "18"
$mainForm.Controls.Add($fontSizeTextBox)

Finally three Buttons are defined, one each for calling the Web Service function, displaying the image and exiting the script.

# Generate Button
$generateButton = New-Object System.Windows.Forms.Button 
$generateButton.Location = "510,30"
$generateButton.Size = "75,28"
$generateButton.ForeColor = "DarkBlue"
$generateButton.BackColor = "White"
$generateButton.Text = "Convert"
$generateButton.add_Click({GetBraille($scriptPath)})
$mainForm.Controls.Add($generateButton)

# Display Button
$displayButton = New-Object System.Windows.Forms.Button
$displayButton.Location = "510,60"
$displayButton.Size = "75, 28"
$displayButton.BackColor = "White"
$displayButton.ForeColor = "DarkBlue" 
$displayButton.Text = "Display"
$displayButton.add_Click({DisplayImage})
$mainForm.Controls.Add($displayButton)

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

The Function calling the Web Service starts with resetting the $error variable. I define the Web Service call using New-WebServiceProxy with the URI pointing to the services WSDL address. I receive the returned image by making the call, passing the text and font size. I set a unique output filename and create the file using Set-Content, inputting a byte stream. Lastly the $error variable s checked for a successful call, translate and file write with the result written.

Function GetBraille {
    $error.clear()
    $getBraille = New-WebServiceProxy -uri "http://www.webservicex.net/braille.asmx?WSDL"
    $returnedImage = $getBraille.BrailleText($inputTextTextBox.Text, $fontSizeTextBox.Text)
    $global:outFile = $scriptPath + "\"  + "Text2Braille_" + (Get-Date -UFormat %Y%m%d_%H%M%S) + ".jpg"
    Set-Content -Path $global:outFile -Value $returnedImage -Encoding Byte
    If($error.count -gt 0) {
        $completedLabel.Text = "Error!"
        $completedLabel.ForeColor = "Red"
    }
    Else {
        $completedLabel.Text = "Success!"
        $completedLabel.ForeColor = "Green"
    }
    PictureBox($global:outFile)
    $getBraille.Dispose()
}

The image is displayed in the PictureBox using the previously written file. If desired, the image can be displayed external of the script using the default system image viewer called with Invoke-Item.

Function PictureBox {
    $file = (Get-Item $global:outFile)
    $image = [System.Drawing.Image]::Fromfile($file)
    $pictureBox.Image = $image
}

Function DisplayImage {
    If ($global:outFile.Length -gt 0) {
        Invoke-Item $global:outFile
    }
}

Download the complete script here – Text2Braille.ps1

PowerShell Stock Quote with Web Services

Leave a comment

This script uses a WebServiceX.net hosted Web Service to fetch a stock quote for any symbol. The Web Service returns 16 different values as XML marked-up data.

StockQuote

I start by defining the Windows Form (WinForm) used for input and output GUI display. I added the Input TextBox and Label pair and well as one TextBox/Label pair for each output value.

Add-Type -AssemblyName System.Windows.Forms

# Main Form 
$mainForm = New-Object System.Windows.Forms.Form
$mainForm.Location = "200, 200"
$mainForm.Font = "Comic Sans MS,9"
$mainForm.FormBorderStyle = "FixedDialog"
$mainForm.Text = " Stock Quote"
$mainForm.ForeColor = "White"
$mainForm.BackColor = "SteelBlue"
$mainForm.Width = 460
$mainForm.Height = 550

# Quote Input Label
$quoteInputLabel = New-Object System.Windows.Forms.Label
$quoteInputLabel.Location = "45,10"
$quoteInputLabel.Height = 22
$quoteInputLabel.Width = 110
$quoteInputLabel.Text = "Stock Symbol"
$mainForm.Controls.Add($quoteInputLabel)

# Quote Input TextBox
$quoteTextBox = New-Object System.Windows.Forms.TextBox
$quoteTextBox.Location = "180,10"
$quoteTextBox.Size = "50,20"
$quoteTextBox.ForeColor = "MediumBlue"
$quoteTextBox.BackColor = "White"
$mainForm.Controls.Add($quoteTextBox)

# Name Output Label
$nameLabel = New-Object System.Windows.Forms.Label
$nameLabel.Location = "45,40"
$nameLabel.Height = 22
$nameLabel.Width = 110
$nameLabel.Text = "Name"
$mainForm.Controls.Add($nameLabel)

# Name Output TextBox
$nameTextBox = New-Object System.Windows.Forms.TextBox
$nameTextBox.Location = "180,40"
$nameTextBox.Size = "130,20"
$nameTextBox.ForeColor = "MediumBlue"
$nameTextBox.BackColor = "White"
$mainForm.Controls.Add($nameTextBox)

Next I add a Button used to call the QuoteLookup Function as well as an Exit Button.

# Lookup Button
$lookupButton = New-Object System.Windows.Forms.Button 
$lookupButton.Location = "350,10"
$lookupButton.Size = "75,28"
$lookupButton.ForeColor = "DarkBlue"
$lookupButton.BackColor = "White"
$lookupButton.Text = "Lookup"
$lookupButton.add_Click({QuoteLookup})
$mainForm.Controls.Add($lookupButton)

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

Finally added is the QuoteLookup Function to call the Web Service and process the returned XML Data. In that function I setup a New-WebServiceProxy for the WebserviceX.net URL pointing to the StockQuote WSDL schema. A SOAP call returns the XML which is then parsed with a Select statement. The previously initialized values are then set for display in the various TextBoxes.

Function QuoteLookup {
    $lastTextBox.Text = " "
    $dateTextBox.Text = " "
    $timeTextBox.Text = " "
    $changeTextBox.Text = " "
    $openTextBox.Text = " "
    $highTextBox.Text = " "
    $lowTextBox.Text = " "
    $volumeTextBox.Text = " "
    $mktCapTextBox.Text = " "
    $previousCloseTextBox.Text = " "
    $PercentageChangeTextBox.Text = " "
    $annRangeTextBox.Text = " "
    $earnsTextBox.Text = " "
    $PETextBox.Text = " "
    $nameTextBox.Text = " "
    $mainForm.Refresh()

    $stockQuote = New-WebServiceProxy -uri "http://www.webservicex.net/stockquote.asmx?WSDL"
    $quote = $stockQuote.GetQuote($quoteTextBox.Text)
    $quoteXML = [xml]$quote
    $selection  = $quoteXML.StockQuotes.Stock | Select Last, Date, Time, Change, Open, High, Low, Volume, MktCap, PreviousClose, PercentageChange, AnnRange, Earns, Name
    $PE = $quoteXML.StockQuotes.Stock | Select  -ExpandProperty P-E
    $lastTextBox.Text = $selection.Last
    $dateTextBox.Text = $selection.Date
    $timeTextBox.Text = $selection.Time
    $changeTextBox.Text = $selection.Change
    $openTextBox.Text = $selection.Open
    $highTextBox.Text = $selection.High
    $lowTextBox.Text = $selection.Low
    $volumeTextBox.Text = "{0:N0}" -f [int]$selection.Volume
    $mktCapTextBox.Text = $selection.MktCap
    $previousCloseTextBox.Text = $selection.PreviousClose
    $PercentageChangeTextBox.Text = $selection.PercentageChange
    $annRangeTextBox.Text = $selection.AnnRange
    $earnsTextBox.Text = $selection.Earns
    $PETextBox.Text = $PE
    $nameTextBox.Text = $selection.Name
}

This is an example of the returned XML from the Web Service.

<StockQuotes>
    <Stock>
        <Symbol>C</Symbol>
        <Last>48.89</Last>
        <Date>9/27/2013</Date>
        <Time>4:00pm</Time>
        <Change>-0.04</Change>
        <Open>48.76</Open>
        <High>49.20</High>
        <Low>48.69</Low>
        <Volume>20248714</Volume>
        <MktCap>148.7B</MktCap>
        <PreviousClose>48.93</PreviousClose>
        <PercentageChange>-0.08%</PercentageChange>
        <AnnRange>32.70 - 53.56</AnnRange>
        <Earns>3.113</Earns>
        <P-E>15.72</P-E>
        <Name>Citigroup</Name>
    </Stock>
</StockQuotes>

Download the complete script here – StockQuote.ps1

PowerShell FontDialog GUI v2

2 Comments

I thought I would update my FontDialog script to demonstrate the selected font and style. I discovered that combining multiple font styles is not very intuitive. After selecting the font and it’s characteristics, sample text is displayed with those styles.

FontDialog2

Here are the changes from the original script, excluding some minor rearrangement of the controls in the form.

There are five font styles. I set the style variables to $null, except for $setRegular which I assign the “Regular” FontStyle.

$setBold = $null
$setItalic = $null
$setRegular = [System.Drawing.FontStyle]::Regular
$setStrikeout = $null
$setUnderline = $null

After selecting a font and it’s styles, I check to see if a style has been selected (true) and if so set the FontStyle.

If ($font.Bold -eq "True") {
    $setBold = [System.Drawing.FontStyle]::Bold
}
If ($font.Italic -eq "True") {
    $setItalic = [System.Drawing.FontStyle]::Italic
}
If ($font.Strikeout -eq "True") {
    $setStrikeout = [System.Drawing.FontStyle]::Strikeout
}
If ($font.Underline -eq "True") {
    $setUnderline = [System.Drawing.FontStyle]::Underline
}

A single Font Style is set on a Font like –

$font = New-Object System.Drawing.Font("Comic Sans MS",17,[System.Drawing.FontStyle]::Regular)

For multiple FontStyles it is a bit trickier. FontStyle enumeration has a FlagsAttribute attribute consisting of a set of flags representing the styles. It is a a bitwise combination of the members values to set the attribute. You use -bor to join multiple FontStyle flag bits into the FlagsAttributes byte.

$newFont = New-Object System.Drawing.Font("Comic Sans MS",15,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold -bor [System.Drawing.FontStyle]::Underline))

Since any FontStyle other than Regular replaces Regular and you can -bor null value bits without affecting the byte, I used a single object to filter the styles. I first set Regular flag and then combine the other fours values, letting them fall out if not set. This allows none or any of the FontStyles to be used concurrently but will default to Regular if no addition styles are selected. The only property not set as from the Font Dialog selections is the size, which is fixed for display consistency.

$newFont = New-Object System.Drawing.Font($font.Name,17,[System.Drawing.FontStyle]($setRegular -bor $setBold -bor $setItalic -bor $setStrikeout -bor $setUnderline))

$titleLabel1.Font = $newFont
$titleLabel2.Font = $newFont

Download the complete script here – FontDialog2.ps1

PowerShell Image Converter – Grayscale, Negative, Sepia

Leave a comment

This script will take an Image file and convert it to Grayscale, Negative or Sepia. It features a dual display of the before and after images as well as a Progress Bar and Counter to show percentage done.

Grayscale Image –

ImageConverter4

Negative of an Image –

ImageConverter

Finally a Sepia tone image –

ImageConverter3

First I define the main Form and set some variables.

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

# Global Values
# Global Values
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$global:selectedObject = "GrayScale"
$global:outFile = ""

# WinForm Setup
$mainForm = New-Object System.Windows.Forms.Form
$mainForm.Location = "200, 200"
$mainForm.Font = "Comic Sans MS,9"
$mainForm.FormBorderStyle = "FixedDialog"
$mainForm.ForeColor = "Black"
$mainForm.BackColor = "Bisque"
$mainForm.Text = " PowerShell Image Converter"
$mainForm.Width = 1030
$mainForm.Height = 520

Next comes the three RadioButtons used to select the conversion mode.

# GrayScale RadioButton
$radioButtonGrayScale = New-Object System.Windows.Forms.RadioButton
$radioButtonGrayScale.Location = "40,15"
$radioButtonGrayScale.ForeColor = "Indigo"
$radioButtonGrayScale.Text = "Grayscale"
$radioButtonGrayScale.Checked = $true
$radioButtonGrayScale.add_Click({
    $global:selectedObject = "GrayScale"
})
$mainForm.Controls.Add($radioButtonGrayScale)

# Negative RadioButton
$radioButtonNegative = New-Object System.Windows.Forms.RadioButton
$radioButtonNegative.Location = "200,15"
$radioButtonNegative.ForeColor = "Indigo"
$radioButtonNegative.Text = "Negative"
$radioButtonNegative.add_Click({
    $global:selectedObject = "Negative"
})
$mainForm.Controls.Add($radioButtonNegative)

# Sepia RadioButton
$radioButtonSepia = New-Object System.Windows.Forms.RadioButton
$radioButtonSepia.Location = "360,15"
$radioButtonSepia.ForeColor = "Indigo"
$radioButtonSepia.Text = "Sepia"
$radioButtonSepia.add_Click({
    $global:selectedObject = "Sepia"
})
$mainForm.Controls.Add($radioButtonSepia)

The Input File selection uses the OpenFileDialog Control to select the file. The control is called with the Browse Button. The selected filename is displayed in a TextBox.

# Input Box
$textBoxIn = New-Object System.Windows.Forms.TextBox
$textBoxIn.Location = "15, 50"
$textBoxIn.Size = "460, 20"
$textBoxIn.Text = "Input Image File"
$mainForm.Controls.Add($textBoxIn)

# Browse Input Button
$buttonBrowse = New-Object System.Windows.Forms.Button
$buttonBrowse.Location = "480, 50"
$buttonBrowse.Size = "75, 23"
$buttonBrowse.ForeColor = "MediumBlue" 
$buttonBrowse.Text = "Browse"
$buttonBrowse.add_Click({selectFiles})
$mainForm.Controls.Add($buttonBrowse)

function selectFiles {
	$selectForm = New-Object System.Windows.Forms.OpenFileDialog
	$selectForm.Filter = "All Files (*.*)|*.*"
	$selectForm.InitialDirectory = ".\"
	$selectForm.Title = "Select a File to Process"
	$getKey = $selectForm.ShowDialog()
	If ($getKey -eq "OK") {
            $textBoxIn.Text = $selectForm.FileName
            $completeLabel.Text = "      "
            InputPicture($selectForm.FileName)
	}
} # End SelectFile

Three Buttons are used to start Processing, Display the output in a larger format and Exit the script.

# Process Button
$buttonProcess = New-Object System.Windows.Forms.Button
$buttonProcess.Location = "740,50"
$buttonProcess.Size = "75, 23"
$buttonProcess.ForeColor = "Green" 
$buttonProcess.Text = "Process"
$buttonProcess.add_Click({ProcessButton})
$mainForm.Controls.Add($buttonProcess)

# Display Button
$buttonDisplay = New-Object System.Windows.Forms.Button
$buttonDisplay.Location = "830,50"
$buttonDisplay.Size = "75, 23"
$buttonDisplay.ForeColor = "Green" 
$buttonDisplay.Text = "Display"
$buttonDisplay.add_Click({DisplayImage})
$mainForm.Controls.Add($buttonDisplay)

# Exit Button
$buttonExit = New-Object System.Windows.Forms.Button
$buttonExit.Location = "920,50"
$buttonExit.Size = "75, 23"
$buttonExit.ForeColor = "Red" 
$buttonExit.Text = "Exit"
$buttonExit.add_Click({$mainForm.close()})
$mainForm.Controls.Add($buttonExit)

Two Labels provide information. One states where the output file is being written and the other shows the status of the processing.

# Output Label
$outputLabel = New-Object System.Windows.Forms.Label
$outputLabel.Location = "15, 400"
$outputLabel.Size = "900, 20"
$outputLabel.ForeColor = "DarkBlue"
$outputLabel.Text = "Output Files Written to - " + $scriptPath
$mainForm.Controls.Add($outputLabel)

# Completion Label
$completeLabel = New-Object System.Windows.Forms.Label
$completeLabel.Location = "750, 30"
$completeLabel.Size = "60, 18"
$completeLabel.ForeColor = "Green"
$completeLabel.Text = ""
$mainForm.Controls.Add($completeLabel)

There are two PitcureBoxes displaying the input and output images.

# Input PictureBox
$inputPicture = New-Object System.Windows.Forms.PictureBox
$inputPicture.Location = "15, 90"
$inputPicture.ClientSize = "480, 300"
$inputPicture.BackColor = "Black"
$inputPicture.SizeMode = "Zoom"
$mainForm.Controls.Add($inputPicture)

# Final PictureBox
$finalPicture = New-Object System.Windows.Forms.PictureBox
$finalPicture.Location = "520, 90"
$finalPicture.ClientSize = "480, 300"
$finalPicture.BackColor = "Black"
$finalPicture.SizeMode = "Zoom"
$mainForm.Controls.Add($finalPicture)

I add a ProgressBar to graphically show the progress of the conversion process. I set various values including Style.

# Progress Bar
$ProgressBar = New-Object System.Windows.Forms.ProgressBar
$ProgressBar.Location = "8, 430"
$ProgressBar.Maximum = 1000
$ProgressBar.Minimum = 0
$ProgressBar.Size = "1000, 20"
$progressBar.Step = 10
$progressBar.Style = "Continuous"
$mainForm.Controls.Add($ProgressBar)

The final controls added to the form are the <a title="StatusStrip" href="http://msdn.microsoft.com/en-us/library/system.windows.forms.statusstrip(v=vs.110).aspx" target="_blank">StatusStrip</a> and its Label. I write the percentage complete value in the Label.


# Progress Bar Status Strip & Label
$statusStrip = New-Object System.Windows.Forms.StatusStrip
$statusStrip.BackColor = "Bisque"

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

When the Process Button is clicked the ProcessButton Function is called. After initializing some values the effect selected by the RadioButton, global:selectedObject, is called using Invoke-Express.

Function ProcessButton {
    $completeLabel.Text = "        "
    $progressBar.Value = 0
    $finalPicture.Image = $null
    $statusLabel.Text = "Ready"
    Start-Sleep -Milliseconds 200
    Invoke-Expression $global:selectedObject
}

In the Grayscale Function I first cleared $error variable and set the input filename. I define $image, the input image, and set it's width and height to the original image size. I define $bitmap, the output image, and also set it's dimensions to the original size.

Function GrayScale {
    $error.clear()
    # Input Image
    $filename = $textBoxIn.Text
    $image = New-Object System.Drawing.Bitmap $filename
    $total = $image.Width * $image.Height

    # Output Image
    $bitmap = New-Object System.Drawing.Bitmap($image.Width,$image.Height)

Next I setup the main loop and the ProgressBar. The main loop consist of two loops which process each pixel of the image. Inside a loop going across the width of the image an inner loop process each pixel of the height of the image. Each pixel is separated into it's Red, Green and Blue components and those are color values are processed using the Luminosity Method. That method accounts for the way the human eye works and multiples each value by a certain number so the result is each value represents the percentage of light absorbed of that color. You can also use the Average Method which just averages the combined values of the colors for that pixel. After being set each pixel is written to $bitmap.

I run the calculation for the percent done and compare that to a step counter, calling a ProgressBar.PerformStep to increment the ProgressBar if the percentage done is an integer (or just greater an that) resulting in 100 update of the ProgressBar during the run, one for each percent completed. I also update the StatusBar Label with the percent done.

    $percentStep = 0
    foreach($x in 0..($image.Width-1)) {
        foreach($y in 0..($image.Height-1)) {
            $percentDone = ((($x * $image.Height + $y) / $total ) * 100)
            If ([int]$percentDone -gt $percentStep) {
                $progressBar.PerformStep()
                $statusLabel.Text = "{0:N0}" -f $percentDone + "% Complete"
                $percentStep = $percentStep + 1
            }
            $pixelColor = $image.GetPixel($x, $y)
	        $red   = $pixelColor.R
	        $green = $pixelColor.G
	        $blue  = $pixelColor.B
	        # $gray  = ($red + $green + $blue) / 3  # Average Method
	        $gray = ($red*.2126)+($green*.7152)+($blue*.0722)  # Luminosity Method
	        $bitmap.SetPixel($x, $y, [System.Drawing.Color]::FromArgb($gray, $gray, $gray))
        }
    }

When the image has been fully processed it is written to a file with a name which includes date and time for uniqueness. That image file is then displayed in the final PictureBox. A check of $error makes sure there were no issues during the processing or file write.

    $global:outFile = $scriptPath + "\"  + "GrayScale_" + (Get-Date -UFormat %Y%m%d_%H%M%S) + ".bmp"
    $bitmap.Save($global:outFile)
    FinalPicture($Global:outFile)

    $bitmap.Dispose()
    If($error.count -gt 0) {
        $completeLabel.ForeColor = "Red"
        $completeLabel.Text = "Error!"
    } Else {
        $completeLabel.ForeColor = "Green"
        $completeLabel.Text = "Success!"
    }
} # End GrayScale Function

Negative images are processed by reversing the pixel color, done by subtracting the actual value from the max value.

$pixelColor = $image.GetPixel($x, $y)
$red   = $pixelColor.R
$green = $pixelColor.G
$blue  = $pixelColor.B
$newRed   = (255 - $red)
$newGreen = (255 - $green)
$newBlue  = (255 - $blue)
$bitmap.SetPixel($x, $y, [System.Drawing.Color]::FromArgb($newRed, $newGreen, $newBlue))

Sepia images are generated taking a different fixed percentage of each color value, ending with the tinted image. These specific values are the values for sepia tone that are recommended by Microsoft.

$pixelColor = $image.GetPixel($x, $y)
$red   = $pixelColor.R
$green = $pixelColor.G
$blue  = $pixelColor.B
$outRed   = ($red * 0.393) + ($green * 0.769) + ($blue * 0.189)
$outGreen = ($red * 0.349) + ($green * 0.686) + ($blue * 0.168)
$outBlue  = ($red * 0.272) + ($green * 0.534) + ($blue * 0.131)
If ($outRed   -gt 255) {$outred   = 255}
If ($outGreen -gt 255) {$outGreen = 255}
If ($outBlue  -gt 255) {$outBlue  = 255}
$bitmap.SetPixel($x, $y, [System.Drawing.Color]::FromArgb($outRed, $outGreen, $outBlue))

Download the complete script here - ImageConverter.ps1

PowerShell Random Geometric Patterns v2

1 Comment

This script is an update to the Random Geometric Patterns Image Maker script. The original script had 12 patterns while the new version has 33 different pattern/fill combos. There are new patterns like Points, Squares, Stars and Triangles. The patterns now have a variety of fill types including Solid, Gradient and Hatch.

Here are some examples starting with Ellipses filled with (Skewed) Gradient colors.

EllipsesGradient_20140810_141957

These are Pie Shapes filled with Hatches.

PiesHatch_20140810_141525

Here are Squares filled with a Gradient.

SquaresGradient_20140810_140839

These are Random Stars.

Stars_20140815_194426

And these are Random Triangles with thick lines on a transparent background.

Triangles_20140810_141258

Lastly are Polygons with a Gradient fill.

PolygonsGradient_20140714_195930

This script interface and core functions are the same as in the original script, just the pattern options available are expanded. The only other changes are expanding the output image size options and changing that size text parser to split instead of substring for more flexibility. Also the Squares and Rectangles max size is set to 25% of the image size instead of being fixed. I’ll only describe using the fills and the new patterns which are the major changes in version 2.

The Hatch fills use one of 53 available Hatch Styles. I start by filling the array $hatchStyle with all the Styles in Drawing.Drawing2D.HatchStyle.

# Select Hatch Style
$maxHatchStyle = 53
$hatchStyle = @()
For ($i = [Convert]::ToInt32([Drawing.Drawing2D.HatchStyle]::Min); $i -lt $maxHatchStyle; $i++) {
    $hatchStyle += ([Drawing.Drawing2D.HatchStyle] $i)
}

The Hatch Styles are used in the HatchBrush Class. I setup the Brush with a random style and random colors. The Brush is then used in the Fill calls, like $bitmapGraphics.FillEllipse.

# Setup Brush
$brush = New-Object System.Drawing.Drawing2D.HatchBrush( `
[System.Drawing.Drawing2D.HatchStyle] $hatchStyle[(Get-Random -minimum 0 -maximum 52)], `
[System.Drawing.Color]::FromArgb((Get-Random -minimum 0 -maximum 255),(Get-Random -minimum 0 -maximum 255),(Get-Random -minimum 0 -maximum 255)), `
[System.Drawing.Color]::FromArgb((Get-Random -minimum 0 -maximum 255),(Get-Random -minimum 0 -maximum 255),(Get-Random -minimum 0 -maximum 255)))
# Draw the Ellipse
$bitmapGraphics.FillEllipse($brush, $upperLeftX, $upperLeftY, $width, $height)

Gradient Fills use the Drawing.Drawing2D.LinearGradientBrush Brush. The Brush uses a Start and End Point of the linear gradient. The Start Point is the upper left corner and the End Point is random. Also defined are the Starting and Ending colors, which are randomized.

# Setup Brush
$pointStart = New-Object System.Drawing.Point
$pointStart.X = $upperLeftX
$pointStart.Y = $upperLeftY
$pointStop = New-Object System.Drawing.Point
$pointStop.X = $random1
$pointStop.Y = $random2
$brush = New-Object System.Drawing.Drawing2D.LinearGradientBrush($pointStart, $pointStop,  `
    [System.Drawing.Color]::FromArgb((Get-Random -minimum 0 -maximum 255),(Get-Random -minimum 0 -maximum 255),(Get-Random -minimum 0 -maximum 255)), `
    [System.Drawing.Color]::FromArgb((Get-Random -minimum 0 -maximum 255),(Get-Random -minimum 0 -maximum 255),(Get-Random -minimum 0 -maximum 255)))
# Draw the Ellipse
$bitmapGraphics.FillEllipse($brush, $upperLeftX, $upperLeftY, $width, $height)

The Solid Fills use a Brush defined with three colors, which are random.

$red   = (Get-Random -minimum 0 -maximum 255)
$green = (Get-Random -minimum 0 -maximum 255)
$blue  = (Get-Random -minimum 0 -maximum 255)

# Setup Brush
$brushColor = [System.Drawing.Color]::FromArgb($red, $green, $blue)
$brush = New-Object System.Drawing.SolidBrush $brushColor

Stars use some trigonometry to calculate the points which form the Star. I got the base algorithm from a blog discussion about drawing a 5 pointed star in Java.

# Randomize Position and Define the Stars Points
$x = (Get-Random -minimum -100 -maximum $global:imageWidth)
$y = (Get-Random -minimum -100 -maximum $global:imageHeight)
$width = (Get-Random -minimum 0 -maximum 100)
$height = $width
$ax = [int](($width/2) + $x)
$ay = [int]$y
$bx = [int]($width+$x)
$by = [int](.374*[double]($height)+[double]($y))
$cx = [int](.825*[double]($width)+[double]($x))
$cy = [int]($height+$y)
$dx = [int](.175*[double]($height)+[double]($x))
$dy = [int]($height+$y)
$ex = [int]$x
$ey = [int]$by
# Draw the Star
$bitmapGraphics.DrawLine($pen, $ax, $ay, $cx, $cy)
$bitmapGraphics.DrawLine($pen, $bx, $by, $dx, $dy)
$bitmapGraphics.DrawLine($pen, $cx, $cy, $ex, $ey)
$bitmapGraphics.DrawLine($pen, $dx, $dy, $ax, $ay)
$bitmapGraphics.DrawLine($pen, $ex, $ey, $bx, $by)

Triangles are drawn by defining  three random points and drawing a line from the first point to the second, then the third and back to the first.

# Randomize Position and Define the Triangle Points
$startX = (Get-Random -minimum 0 -maximum $global:imageWidth)
$startY = (Get-Random -minimum 0 -maximum $global:imageHeight)
$secondX = (Get-Random -minimum 0 -maximum $global:imageWidth)
$secondY = (Get-Random -minimum 0 -maximum $global:imageHeight)
$thirdX = (Get-Random -minimum 0 -maximum $global:imageWidth)
$thirdY = (Get-Random -minimum 0 -maximum $global:imageHeight)
$saveX = $startX
$saveY = $startY
# Draw the Triangle
$bitmapGraphics.DrawLine($pen, $startX, $startY, $secondX, $secondY)
$bitmapGraphics.DrawLine($pen, $secondX, $secondY, $thirdX, $thirdY)
$bitmapGraphics.DrawLine($pen, $thirdX, $thirdY, $saveX, $saveY)

GeometricPatterns2

Download the complete script here – GeometricPatterns2.ps1

Older Entries

%d bloggers like this: