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 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

PowerShell WinForms Menu

1 Comment

Most of the scripts I have written for this blog have custom interfaces for that script. But sometimes you need a Form laid out with the ‘Standard” Windows menu system. This script presents a WinForms Form layout with standard features including a pull-down menu supporting sub-menus, shortcut keys and icons. Also a toolbar with buttons representing menu options and a status bar on the bottom.
 
FormsMenu
 
I first add the .Net assemblies and set EnableVisualStyles. This setting allows the Form to use the colors, fonts and other visual elements that form an operating system theme. All the controls used in the script will utilize the desktop theme.
 

# Install .Net Assemblies
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

# Enable Visual Styles
[Windows.Forms.Application]::EnableVisualStyles()

 
The below image is FormsMenu running on a desktop with a High Contrast theme so show how the WinForm elements follow the OS desktop theme when setting EnableVisualStyles.
 
FormsMenuHC
 
The objects used in the main form are defined.
 

# Main Form .Net Objects
$mainForm         = New-Object System.Windows.Forms.Form
$menuMain         = New-Object System.Windows.Forms.MenuStrip
$menuFile         = New-Object System.Windows.Forms.ToolStripMenuItem
$menuView         = New-Object System.Windows.Forms.ToolStripMenuItem
$menuTools        = New-Object System.Windows.Forms.ToolStripMenuItem
$menuOpen         = New-Object System.Windows.Forms.ToolStripMenuItem
$menuSave         = New-Object System.Windows.Forms.ToolStripMenuItem
$menuSaveAs       = New-Object System.Windows.Forms.ToolStripMenuItem
$menuFullScr      = New-Object System.Windows.Forms.ToolStripMenuItem
$menuOptions      = New-Object System.Windows.Forms.ToolStripMenuItem
$menuOptions1     = New-Object System.Windows.Forms.ToolStripMenuItem
$menuOptions2     = New-Object System.Windows.Forms.ToolStripMenuItem
$menuExit         = New-Object System.Windows.Forms.ToolStripMenuItem
$menuHelp         = New-Object System.Windows.Forms.ToolStripMenuItem
$menuAbout        = New-Object System.Windows.Forms.ToolStripMenuItem
$mainToolStrip    = New-Object System.Windows.Forms.ToolStrip
$toolStripOpen    = New-Object System.Windows.Forms.ToolStripButton
$toolStripSave    = New-Object System.Windows.Forms.ToolStripButton
$toolStripSaveAs  = New-Object System.Windows.Forms.ToolStripButton
$toolStripFullScr = New-Object System.Windows.Forms.ToolStripButton
$toolStripAbout   = New-Object System.Windows.Forms.ToolStripButton
$toolStripExit    = New-Object System.Windows.Forms.ToolStripButton
$statusStrip      = New-Object System.Windows.Forms.StatusStrip
$statusLabel      = New-Object System.Windows.Forms.ToolStripStatusLabel

 
I use icons on the menu items, main form and the About form. These can acquired in a variety of ways, three of which I used here. First, for the icon on the main form I used the embedded icon from the PowerShell executable calling the System.Drawing.Icon.ExtractAssociatedIcon method.
 

$iconPS   = [Drawing.Icon]::ExtractAssociatedIcon((Get-Command powershell).Path)

 
There are a few icons that can be used in the SystemIcon Class. From this I used the Information icon on the About menu item.
 

[System.Drawing.SystemIcons]::Information

 
The rest of the icons I pull out of shell32.dll, one of many Windows system files which contain icons. Without a direct call from PowerShell, I used the below code comes from the PowerShell Forum in the TechNet Script Center (http://social.technet.microsoft.com/Forums/scriptcenter/en-US/16444c7a-ad61-44a7-8c6f-b8d619381a27/using-icons-in-powershell-scripts?forum=winserverpowershell). After loading the assembly, you have a new Method to extract icons from DLL’s called IconExtractor.
 

# Create Icon Extractor Assembly
$code = @"
using System;
using System.Drawing;
using System.Runtime.InteropServices;

namespace System
{
	public class IconExtractor
	{

	 public static Icon Extract(string file, int number, bool largeIcon)
	 {
	  IntPtr large;
	  IntPtr small;
	  ExtractIconEx(file, number, out large, out small, 1);
	  try
	  {
	   return Icon.FromHandle(largeIcon ? large : small);
	  }
	  catch
	  {
	   return null;
	  }

	 }
	 [DllImport(&quot;Shell32.dll&quot;, EntryPoint = &quot;ExtractIconExW&quot;, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
	 private static extern int ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons);

	}
}
"@
Add-Type -TypeDefinition $code -ReferencedAssemblies System.Drawing

 
I setup the Main Form defining the Menu and Icon and centering in the screen.
 

# Main Form
$mainForm.Height          = 400
$mainForm.Icon            = $iconPS
$mainForm.MainMenuStrip   = $menuMain
$mainForm.Width           = 800
$mainForm.StartPosition   = "CenterScreen"
$mainForm.Text            = " WinForms Menu Demo"
$mainForm.Controls.Add($menuMain)

 
To build the Menu I first add the ToolStrip then the MenuStrip control to the Main Form. ToolsStrip is the base class for MenuStrip and StatusStrip, see the Control Architecture document. I then add the first (Top Level) ToolStripMenuItem, ‘File’, to the Main Menu Items list.
 

# Main ToolStrip
[void]$mainForm.Controls.Add($mainToolStrip)

# Main Menu Bar
[void]$mainForm.Controls.Add($menuMain)

# Menu Options - File
$menuFile.Text = "File"
[void]$menuMain.Items.Add($menuFile)

 
The Drop Down items are then added. The icon is added using the IconExtractor. The numeric is the icon number used to reference it. Shortcut Keys are defined and the text added. The MenuItem is then added as a DropDownItem under the ‘File’ Main menu item.
 

# Menu Options - File / Open
$menuOpen.Image        = [System.IconExtractor]::Extract(&quot;shell32.dll&quot;, 4, $true)
$menuOpen.ShortcutKeys = "Control, O"
$menuOpen.Text         = "Open"
$menuOpen.Add_Click({OpenFile})
[void]$menuFile.DropDownItems.Add($menuOpen)

# Menu Options - File / Save
$menuSave.Image        = [System.IconExtractor]::Extract(&quot;shell32.dll&quot;, 36, $true)
$menuSave.ShortcutKeys = "F2"
$menuSave.Text         = "Save"
$menuSave.Add_Click({SaveFile})
[void]$menuFile.DropDownItems.Add($menuSave)

# Menu Options - File / Save As
$menuSaveAs.Image        = [System.IconExtractor]::Extract(&quot;shell32.dll&quot;, 45, $true)
$menuSaveAs.ShortcutKeys = "Control, S"
$menuSaveAs.Text         = "Save As"
$menuSaveAs.Add_Click({SaveAs})
[void]$menuFile.DropDownItems.Add($menuSaveAs)

# Menu Options - File / Exit
$menuExit.Image        = [System.IconExtractor]::Extract(&quot;shell32.dll&quot;, 10, $true)
$menuExit.ShortcutKeys = "Control, X"
$menuExit.Text         = "Exit"
$menuExit.Add_Click({$mainForm.Close()})
[void]$menuFile.DropDownItems.Add($menuExit)

 
Nesting a Drop Down Menu Item under another is a matter of just adding it to the upper Drop Down Item, as under ‘Options’.
 

# Menu Options - Tools
$menuTools.Text      = "Tools"
[void]$menuMain.Items.Add($menuTools)

# Menu Options - Tools / Options
$menuOptions.Image     = [System.IconExtractor]::Extract(&quot;shell32.dll&quot;, 21, $true)
$menuOptions.Text      = "Options"
[void]$menuTools.DropDownItems.Add($menuOptions)

# Menu Options - Tools / Options / Options 1
$menuOptions1.Image     = [System.IconExtractor]::Extract(&quot;shell32.dll&quot;, 33, $true)
$menuOptions1.Text      = "Options 1"
$menuOptions1.Add_Click({Options1})
[void]$menuOptions.DropDownItems.Add($menuOptions1)

# Menu Options - Tools / Options / Options 2
$menuOptions2.Image     = [System.IconExtractor]::Extract(&quot;shell32.dll&quot;, 35, $true)
$menuOptions2.Text      = "Options 2"
$menuOptions2.Add_Click({Options2})
[void]$menuOptions.DropDownItems.Add($menuOptions2)

 
The Buttons in the toolbar are made with a ToolStripButton. Each Button uses the icon from the Menu Items and calls the same Functions.
 

# ToolStripButton - Open
$toolStripOpen.ToolTipText  = "Open"
$toolStripOpen.Image = $menuOpen.Image
$toolStripOpen.Add_Click({OpenFile})
[void]$mainToolStrip.Items.Add($toolStripOpen)

 
The Status bar is created by the StatusStrip Class control. The text is created by using a ToolStripStatusLabel Class control.
 

# Status Bar Label
[void]$statusStrip.Items.Add($statusLabel)
$statusLabel.AutoSize  = $true
$statusLabel.Text      = "Ready"
$mainForm.Controls.Add($statusStrip)

 
Most of the Menu Items do nothing, however there are three options with working functions. The first two are “File Open” and “Save As”, which call OpenFileDialog and SaveFileDialog controls. You select what to look for, where to start and what to say. I set a variable with the filename when you make the selection.
 

function OpenFile {
    $statusLabel.Text = &quot;Open File&quot;
    $selectOpenForm = New-Object System.Windows.Forms.OpenFileDialog
    $selectOpenForm.Filter = "All Files (*.*)|*.*"
    $selectOpenForm.InitialDirectory = ".\"
    $selectOpenForm.Title = "Select a File to Open"
    $getKey = $selectOpenForm.ShowDialog()
    If ($getKey -eq "OK") {
        $inputFileName = $selectOpenForm.FileName
    }
    $statusLabel.Text = "Ready"
}

function SaveAs {
    $statusLabel.Text = &quot;Save As&quot;
    $selectSaveAsForm = New-Object System.Windows.Forms.SaveFileDialog
    $selectSaveAsForm.Filter = "All Files (*.*)|*.*"
    $selectSaveAsForm.InitialDirectory = ".\"
    $selectSaveAsForm.Title = "Select a File to Save"
    $getKey = $selectSaveAsForm.ShowDialog()
    If ($getKey -eq "OK") {
        $outputFileName = $selectSaveAsForm.FileName
    }
    $statusLabel.Text = "Ready"
}

 
The final working menu selection is “About”. A new Form is created containing the same PowerShell icon from the main form in a PictureBox as well as a comment.
 
FormsMenuAbout
 

function About {
    $statusLabel.Text = "About"
    # About Form Objects
    $aboutForm          = New-Object System.Windows.Forms.Form
    $aboutFormExit      = New-Object System.Windows.Forms.Button
    $aboutFormImage     = New-Object System.Windows.Forms.PictureBox
    $aboutFormNameLabel = New-Object System.Windows.Forms.Label
    $aboutFormText      = New-Object System.Windows.Forms.Label

    # About Form
    $aboutForm.AcceptButton  = $aboutFormExit
    $aboutForm.CancelButton  = $aboutFormExit
    $aboutForm.ClientSize    = "350, 110"
    $aboutForm.ControlBox    = $false
    $aboutForm.ShowInTaskBar = $false
    $aboutForm.StartPosition = "CenterParent"
    $aboutForm.Text          = "About FormsMenu.ps1"
    $aboutForm.Add_Load($aboutForm_Load)

    # About PictureBox
    $aboutFormImage.Image    = $iconPS.ToBitmap()
    $aboutFormImage.Location = "55, 15"
    $aboutFormImage.Size     = "32, 32"
    $aboutFormImage.SizeMode = "StretchImage"
    $aboutForm.Controls.Add($aboutFormImage)

    # About Name Label
    $aboutFormNameLabel.Font     = New-Object Drawing.Font("Microsoft Sans Serif", 9, [System.Drawing.FontStyle]::Bold)
    $aboutFormNameLabel.Location = "110, 20"
    $aboutFormNameLabel.Size     = "200, 18"
    $aboutFormNameLabel.Text     = "WinForms Menu Demo"
    $aboutForm.Controls.Add($aboutFormNameLabel)

    # About Text Label
    $aboutFormText.Location = "100, 40"
    $aboutFormText.Size     = "300, 30"
    $aboutFormText.Text     = "          Wayne Lindimore `n`r AdminsCache.WordPress.com"
    $aboutForm.Controls.Add($aboutFormText)

    # About Exit Button
    $aboutFormExit.Location = "135, 70"
    $aboutFormExit.Text     = "OK"
    $aboutForm.Controls.Add($aboutFormExit)

    [void]$aboutForm.ShowDialog()
    $statusLabel.Text = "Ready"
} # End About

 
 
Download the complete script here – FormsMenu.ps1

PowerShell Collage Framed Images

1 Comment

This script is an update to my Single Image Collage Maker script. After populating the new image with a collage of smaller images a larger central image, with optional border, is painted. The resultant effect is a picture framed by many smaller pictures. You can select the number of collage images and their size along with the size and border on the central image.
 

Here are a few examples. First the frame is fully populated with the collage and the central image has a thick border. The Collage images are at 10% size on all the examples.
 
FramedImages_20140719_180457
 
In this one the central image is smaller so more of the collage shows.
 
FramedImages_20140718_185231
 
The one has an sparsely populated collage frame and a transparent background.
 
FramedImages_20140719_181227
 
This last one is just a more abstract image with a complete Collage Frame and a Border.
 
CollageFramedImages_20140719_193156
 
I started with the Single Image Collage script and added a TrackBar for the central image size.
 

# Central Image Reduction TrackBar
$centralReductionTrackBar = New-Object Windows.Forms.TrackBar
$centralReductionTrackBar.Location = "190,170"
$centralReductionTrackBar.Orientation = "Horizontal"
$centralReductionTrackBar.Width = 295
$centralReductionTrackBar.Height = 40
$centralReductionTrackBar.LargeChange = 20
$centralReductionTrackBar.SmallChange = 1
$centralReductionTrackBar.TickFrequency = 5
$centralReductionTrackBar.TickStyle = "TopLeft"
$centralReductionTrackBar.SetRange(1, 100)
$centralReductionTrackBar.Value = 80
$centralReductionTrackBarValue = 80
$centralReductionTrackBar.add_ValueChanged({
    $centralReductionTrackBarValue = $centralReductionTrackBar.Value
    $centralReductionTrackBarLabel.Text = "Central Image Size ($centralReductionTrackBarValue%)"
    $global:centralReductionPercent = $centralReductionTrackBarValue
})
$mainForm.Controls.add($centralReductionTrackBar)

# Central Image Reduction Label
$centralReductionTrackBarLabel = New-Object System.Windows.Forms.Label 
$centralReductionTrackBarLabel.Location = "15,170"
$centralReductionTrackBarLabel.Size = "180,23"
$centralReductionTrackBarLabel.ForeColor = "MediumBlue"
$centralReductionTrackBarLabel.Text = "Central Image Size ($centralReductionTrackBarValue%)"
$mainForm.Controls.Add($centralReductionTrackBarLabel)

 
I removed the Output Image Size ComboBox since the final image is the same size as the input image. In it’s place I added a ComboBox for the Border Size of the central image.
 

# Border Width ComboBox
$borderComboBox = New-Object System.Windows.Forms.ComboBox
$borderComboBox.Location = "870,30"
$borderComboBox.Size = "30,20"
$borderComboBox.ForeColor = "Indigo"
$borderComboBox.BackColor = "White"
[void]$borderComboBox.items.add("0")
[void]$borderComboBox.items.add("1")
[void]$borderComboBox.items.add("2")
[void]$borderComboBox.items.add("3")
[void]$borderComboBox.items.add("4")
[void]$borderComboBox.items.add("5")
[void]$borderComboBox.items.add("6")  
[void]$borderComboBox.items.add("7")  
[void]$borderComboBox.items.add("8")             
$borderComboBox.SelectedIndex = 2
$mainForm.Controls.Add($borderComboBox)

# Border Width ComboBox Label
$borderComboBoxLabel = New-Object System.Windows.Forms.Label 
$borderComboBoxLabel.Location = "770,30"
$borderComboBoxLabel.Size = "90,23"
$borderComboBoxLabel.ForeColor = "MediumBlue"
$borderComboBoxLabel.Text = "Border Width"
$mainForm.Controls.Add($borderComboBoxLabel)

 
For the central image I define the image size and added the Brush for the Border, if selected.
 

    $image = [System.Drawing.Image]::FromFile($textBoxIn.Text)
    $brush = New-Object System.Drawing.TextureBrush($image, "Tile")
    $brushBlack = New-Object System.Drawing.SolidBrush("Black")
    $global:imageWidth  = $image.Width
    $global:imageHeight = $image.Height

 
After the Collage is created I paint the central image. If a Border is selected then I do a DrawRectangle to draw it. The size of the Rectangle is set to be larger than the central image by the border width. The central image is then centered and drawn.
 

    # Paint central image
    If ([int]$borderWidth -ge 1) {
        $bitmapGraphics.FillRectangle($brushBlack, `
            (([int]$global:imageWidth-($global:imageWidth*$centralReduction))/2)-$borderWidth, `
            (([int]$global:imageHeight-($global:imageHeight*$centralReduction))/2)-$borderWidth, `
            ($global:imagewidth*$centralReduction)+([int]$borderWidth*2), `
            ($global:imageheight*$centralReduction)+([int]$borderWidth*2))
    }
    $pointX = (([int]$global:imageWidth-($global:imageWidth*$centralReduction))/2)
    $pointY = (([int]$global:imageHeight-($global:imageHeight*$centralReduction))/2)
    $height = ([int]$global:imageHeight * $centralReduction)
    $width  = ([int]$global:imageWidth * $centralReduction)
    $bitmapGraphics.DrawImage($imageFile, $pointX, $pointY, $width, $height)

 
CollageFramedImage
 
Download the complete script here – CollageFramedImage.ps1

PowerShell Mosaic Pattern Framed Images

1 Comment

This script starts with my previous blog on Mosaic Style Images but adds a ‘clear’ image in the center which appears to be framed by the pattern. You control the size the center image as well as all the properties from the Mosaic Images script.
 
Here are a couple examples. First is one framed by FilledCurves at about 80% of the image.
 
MosaicFramedCurves_20140702_164104
 
This one is FilledRectangles sized at 80% with a Border.
 
MosaicFramedSquares_20140702_164704
 
And this one is a few FilledPolygons with a transparent background with a Border.
 
MosaicFramedPolygons_20140702_165044
 
FilledCurves with a 20% center image and a Transparent Background without a Border.
 
MosaicFramedCurves_20140711_184044
 
The is another example with 80% sized FilledPolygons and a Transparent Background and a Border.
 
MosaicFramedPolygons_20140704_114839
 
Staring with the Mosaic Images script, I added an additional TrackBar to select the percentage of the final centered image relative to the starting image. Select 90% and you get a very thin frame, select 50% and it’s half and half.
 

# Size of Center Image TrackBar
$centerImageTrackBar = New-Object Windows.Forms.TrackBar
$centerImageTrackBar.Location = "160,160"
$centerImageTrackBar.Orientation = "Horizontal"
$centerImageTrackBar.Width = 325
$centerImageTrackBar.Height = 40
$centerImageTrackBar.LargeChange = 10
$centerImageTrackBar.SmallChange = 1
$centerImageTrackBar.TickFrequency = 10
$centerImageTrackBar.TickStyle = "TopLeft"
$centerImageTrackBar.SetRange(1, 100)
$centerImageTrackBar.Value = 80
$centerImageTrackBarValue = 80
$centerImageTrackBar.add_ValueChanged({
    $centerImageTrackBarValue = $centerImageTrackBar.Value
    $centerImageTrackBarLabel.Text = "Center Frame Size % ($centerImageTrackBarValue)"
    $global:reductionSize = $centerImageTrackBarValue
})
$mainForm.Controls.add($centerImageTrackBar)

# Size of Center Image TrackBar Label
$centerImageTrackBarLabel = New-Object System.Windows.Forms.Label 
$centerImageTrackBarLabel.Location = "15,160"
$centerImageTrackBarLabel.Size = "160,23"
$centerImageTrackBarLabel.ForeColor = "MediumBlue"
$centerImageTrackBarLabel.Text = "Center Frame Size % ($centerImageTrackBarValue)"
$mainForm.Controls.Add($centerImageTrackBarLabel)

 
After a Mosaic pattern has been drawn, as in Mosaic Image script, a final centered pattern is drawn at the size specified. Here is the FilledRectangle which is used in the Square, Rectangle and Polygon Patterns. The FilledEllipse is used in Ellipse and Curves Patterns. The final centered pattern size is the selected percentage of the original image size. The central pattern is centered from the upper left corner of the pattern by subtracting the pattern length and width from the original image size and dividing by 2.
 

    $reduction = $global:reductionSize*.01
    # Paint central image
    If ([int]$borderWidth -ge 1) {
        $bitmapGraphics.FillRectangle($brushBlack, `
            (([int]$image.width-($image.width*$reduction))/2)-$borderWidth, `
            (([int]$image.height-($image.height*$reduction))/2)-$borderWidth, `
            ($image.width*$reduction)+([int]$borderWidth*2), `
            ($image.height*$reduction)+([int]$borderWidth*2))
    }
    $bitmapGraphics.FillRectangle($brush, `
            (([int]$image.width-($image.width*$reduction))/2), `
            (([int]$image.height-($image.height*$reduction))/2), `
            $image.width*$reduction, `
            $image.height*$reduction)

 
MosaicFramedImages
 
Download the complete script here – MosaicFramedImages.ps1
 
 
(Thanks to my friend Kevin Bell for providing the simple centering formula while I was lost wondering around in overcomplexity land.)

PowerShell Create Mosaic Style Images

1 Comment

This script uses Graphics.Fill methods with a Brush containing pieces of an image to repaint that image. Adding a Border you get a Mosaic style image. You get different effects using Graphics.FillRectangle, Graphics.FillEllipse, Graphics.FillClosedCurve and Graphics.FillPolygon. The image the Brush is filling with and the new image have the same dimensions so if you painted with enough random fills to cover the entire image you would have the original image back again, providing you have no border. You pick an image and then select the style and number of random image pieces to paint as well as border and background characteristics.
 
Below are some examples. The first is an image fully covered by random squares with a thin border.
 
ImageSquares_20140126_142121
 
This image is fully covered by Ellipses with a thicker border.
 
MosaicEllipses_20140622_142705
 
This image had fewer squares painted on it so the new image is not fully covered. The background was random and a thin border.
 
MosaicSquares_20140622_143637
 
This image is similar to the above except with a transparent background color.
 
MosaicSquares_20140622_143627
 
This image has a few Curves selected and transparent background. ClosedCurves (and Polygons) are made from a list of points and filled and so the below image is made from a single object.
 
MosaicCurves_20140622_153121
 
The image was created by a large amount of Polygons with a black background.
 
MosaicCurves_20140622_143757
 
I started by defining a WinForms Form and setting some Global Variables. I added the Form Title.
 

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

# Global Values
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$global:numberObjects = 1000
$global:backgroundText = "Black"
$global:outFile = ""

# WinForm Setup
$mainForm = New-Object System.Windows.Forms.Form
$mainForm.Location = "200, 200"
$mainForm.Font = "Comic Sans MS,8.5"
$mainForm.FormBorderStyle = "FixedDialog"
$mainForm.ForeColor = "Black"
$mainForm.BackColor = "Cornsilk"
$mainForm.Text = " Mosaic Image Patterns"
$mainForm.Width = 1030
$mainForm.Height = 500

#Title Label
$titleLabel = New-Object System.Windows.Forms.Label
$titleLabel.Location = "400, 5"
$titleLabel.Font = "Comic Sans MS,11"
$titleLabel.Size = "300, 23"
$titleLabel.ForeColor = "DarkRed" 
$titleLabel.Text = "Mosaic Image Patterns"
$mainForm.Controls.Add($titleLabel)

 
The Brush Style is collected with RadioButtons. The default is Square, where I set the .Checked = $true value.
 

# Curves Object
$radioButtonCurves = New-Object System.Windows.Forms.RadioButton
$radioButtonCurves.Location = "15,35"
$radioButtonCurves.ForeColor = "Indigo"
$radioButtonCurves.Size = "80,23"
$radioButtonCurves.Text = "Curves"
$radioButtonCurves.add_Click({
    $global:selectedObject = "Curves"
})
$mainForm.Controls.Add($radioButtonCurves)

 
Next is gathered the Input Image filename. I add a TextBox to display the result. A Label and a Button to initiate the selectFiles Function. Lastly I use System.Windows.Forms.OpenFileDialog Control to open a File Browse dialog to select the file.
 

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

# Input Box Label
$inputLabel = New-Object System.Windows.Forms.Label
$inputLabel.Location = "15, 67"
$inputLabel.Size = "300, 23"
$inputLabel.ForeColor = "MediumBlue" 
$inputLabel.Text = "Input Image File"
$mainForm.Controls.Add($inputLabel)

# Browse Input Button
$buttonBrowse = New-Object System.Windows.Forms.Button
$buttonBrowse.Location = "560, 80"
$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

 
The input image is displayed in a PictureBox.
 

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

Function InputPicture {
    $file = (Get-Item $selectForm.FileName)
    $image = [System.Drawing.Image]::Fromfile($file)
    $inputPicture.Image = $image
}

 
I get the number of images to be painted with a TrackBar Control. I set label parameters and finally increment number is displayed in the Label.
 

# Number of Objects TrackBar
$numberTrackBar = New-Object Windows.Forms.TrackBar
$numberTrackBar.Location = "160,140"
$numberTrackBar.Orientation = "Horizontal"
$numberTrackBar.Width = 325
$numberTrackBar.Height = 40
$numberTrackBar.LargeChange = 500
$numberTrackBar.SmallChange = 1
$numberTrackBar.TickFrequency = 500
$numberTrackBar.TickStyle = "TopLeft"
$numberTrackBar.SetRange(1, 5000)
$numberTrackBar.Value = 1000
$numberTrackBarValue = 1000
$numberTrackBar.add_ValueChanged({
    $numberTrackBarValue = $numberTrackBar.Value
    $numberTrackBarLabel.Text = "Number Images ($numberTrackBarValue)"
    $global:numberObjects = $numberTrackBarValue
})
$mainForm.Controls.add($numberTrackBar)

# Object Number Label
$numberTrackBarLabel = New-Object System.Windows.Forms.Label 
$numberTrackBarLabel.Location = "15,140"
$numberTrackBarLabel.Size = "160,23"
$numberTrackBarLabel.ForeColor = "MediumBlue"
$numberTrackBarLabel.Text = "Number Images ($numberTrackBarValue)"
$mainForm.Controls.Add($numberTrackBarLabel)

 
Background Color and Border Width are gathered with ComboBoxes.
 

# Background ComboBox
$backgroundComboBox = New-Object System.Windows.Forms.ComboBox
$backgroundComboBox.Location = "590,30"
$backgroundComboBox.Size = "90,20"
$backgroundComboBox.ForeColor = "Indigo"
$backgroundComboBox.BackColor = "White"
[void]$backgroundComboBox.items.add("Black")
[void]$backgroundComboBox.items.add("White")
[void]$backgroundComboBox.items.add("Random")
[void]$backgroundComboBox.items.add("Transparent")         
$backgroundComboBox.SelectedIndex = 0
$mainForm.Controls.Add($backgroundComboBox)

# Background ComboBox Label
$backgroundComboBoxLabel = New-Object System.Windows.Forms.Label 
$backgroundComboBoxLabel.Location = "520,30"
$backgroundComboBoxLabel.Size = "80,20"
$backgroundComboBoxLabel.ForeColor = "MediumBlue"
$backgroundComboBoxLabel.Text = "Background"
$mainForm.Controls.Add($backgroundComboBoxLabel)

 
The Output Image file is written to the same folder where the script resides.
 

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

 
The last Form Controls are the Process, Display and Exit Buttons.
 

# Process Button
$buttonProcess = New-Object System.Windows.Forms.Button
$buttonProcess.Location = "665,80"
$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 = "770,80"
$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 = "870,80"
$buttonExit.Size = "75, 23"
$buttonExit.ForeColor = "Red" 
$buttonExit.Text = "Exit"
$buttonExit.add_Click({$mainForm.close()})
$mainForm.Controls.Add($buttonExit)

 
In the ProcessButton Function I first initialize $error and the $CompleteLabel values and set default colors. I define the Brush using the input image file as the Texture. I set the Brush to paint the image borders as Black. The output image is defined as the same size as the input image. The Graphics class object for the image is set. The image Background Color is set. Finally the selected function is called using Invoke-Expression.
 

Function ProcessButton {
    $error.clear()
    $completeLabel.Text = "        " 

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

    # Image Brush
    $image = [System.Drawing.Image]::FromFile($textBoxIn.Text)
    $brush = New-Object System.Drawing.TextureBrush($image, "Tile")
    $brushBlack = New-Object System.Drawing.SolidBrush("Black")

    # Create Image
    $bitmap = New-Object System.Drawing.Bitmap($image.Width,$image.Height)
    $bitmapGraphics = [System.Drawing.Graphics]::FromImage($bitmap)

    # Image Background Color
    If ($backgroundComboBox.Text -eq "Transparent") {
        $bitmap.MakeTransparent()
    } Else {
        If ($backgroundComboBox.Text -eq "Random") {
            $red   = (Get-Random -minimum 0 -maximum 255)
            $green = (Get-Random -minimum 0 -maximum 255)
            $blue  = (Get-Random -minimum 0 -maximum 255)
            $backColor = [System.Drawing.Color]::FromArgb($red, $green, $blue)
            $bitmapGraphics.Clear($backColor)
        } Else {
            $bitmapGraphics.Clear($backgroundComboBox.Text)
        }
    }

    # Call Selected Fill
    Invoke-Expression $global:selectedObject
} # End ProcessButton

 
As an example of the Brushes used to fill the image here is the Squares function. The image we are filling with and the output image are the same size so the position and size of the selected fill are in their original location. Within an iteration the size of the number of selected objects, the X, Y, Height and Width values are randomized. Since this is a square I simply make the height and width values the same. If a border has been selected (width > 0) then a black border is simulated by filling a rectangle in the same location but larger by the border size value. The fill image will overwrite all but the simulated border. The fill uses the FillRectangle method. After the fill iteration has completed the file is written to disk in the same folder as the script with a unique name containing the date, time and brush style. An error check confirms the process success and write a completed status label.
 

Function Squares {
    $i = 0
    While ($i -le $global:numberObjects) { $i++
        $pointX = (Get-Random -minimum -100 -maximum ([int]$image.width + 100))
        $pointY = (Get-Random -minimum -100 -maximum ([int]$image.height + 100))
        $width = (Get-Random -minimum 1 -maximum ([int]$image.height * .25))
        $height = $width
        If ([int]$frameComboBox.Text -ge 1) {
            $bitmapGraphics.FillRectangle($brushBlack, $pointX-$borderComboBox.Text, $pointY-$borderComboBox.Text, $width+([int]$borderComboBox.Text*2), $height+([int]$borderComboBox.Text*2))
        }
        $bitmapGraphics.FillRectangle($brush, $pointX, $pointY, $width, $height)
    }

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

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

 
Polygons (and Curves) use a list of points to define the object to be filled. The list is built and then the object created and filled at once instead of creating a lot of individual fill objects.
 

Function Polygons {
    $i = 0; $polygon = @()
    While ($i -le $global:numberObjects) { $i++
        $polygonNext = New-Object System.Drawing.Point `
            ((Get-Random -minimum 0 -maximum 1023), (Get-Random -minimum 0 -maximum 767)((Get-Random -minimum 0 -maximum $image.width), (Get-Random -minimum 0 -maximum $image.height)))
        $polygon += $polygonNext
    }
    $bitmapGraphics.FillPolygon($brush, $polygon)

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

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

 
The output image is displayed in a PictureBox.
 

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

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

 
The image can be displayed in a full sized viewer. This will invoke the system default image viewer and open the output file.
 

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

 
 
MosaicImagePatterns
 
Download the complete script here – MosaicImagePatterns.ps1

PowerShell Draw Random Characters

Leave a comment

This script uses the Graphics.DrawString Method to randomly draw characters in an image. The characters are randomly placed, using a randomly selected font and a random color and size. You can also use only the WingDings and WebDings fonts in the image. The background can be black, white, random or transparent.
 
Here are a couple examples from the script –
 
RandomCharacters_20140322_161815
 
RandomCharacters_20140325_195955
 
And “Dings” only –
 
RandomCharacters_20140330_202136
 
I defined a WinForms Form and set some values like script path.
 

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

# Main Form
$mainForm = New-Object Windows.Forms.Form
$mainForm.BackColor = "White"
$mainForm.Font = “Comic Sans MS,8.25"
$mainForm.Text = "Draw Random Characters"
$mainForm.size = "580,210"

# Global Values
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$global:numberValue = 1000

 
I defined a TrackBar and its Label to collect the Number of Characters.
 

# Number of Characters TrackBar
$numberTrackBar = New-Object Windows.Forms.TrackBar
$numberTrackBar.Location = "180,20"
$numberTrackBar.Orientation = "Horizontal"
$numberTrackBar.Width = 350
$numberTrackBar.Height = 40
$numberTrackBar.LargeChange = 500
$numberTrackBar.SmallChange = 10
$numberTrackBar.TickFrequency = 500
$numberTrackBar.TickStyle = "TopLeft"
$numberTrackBar.SetRange(1, 5000)
$numberTrackBar.Value = 1000
$numberTrackBarValue = 1000
$numberTrackBar.add_ValueChanged({
    $numberTrackBarValue = $numberTrackBar.Value
    $numberTrackBarLabel.Text = "Number Characters ($numberTrackBarValue)"
    $global:numberValue = $numberTrackBarValue
})
$mainForm.Controls.add($numberTrackBar)

# Object Number Label
$numberTrackBarLabel = New-Object System.Windows.Forms.Label 
$numberTrackBarLabel.Location = "10,20"
$numberTrackBarLabel.Size = "160,23"
$numberTrackBarLabel.ForeColor = "MediumBlue"
$numberTrackBarLabel.Text = "Number Characters ($numberTrackBarValue)"
$mainForm.Controls.Add($numberTrackBarLabel)

 
I setup two ComboBoxes with Labels to collect the Character Type and Background Color.
 

# Type ComboBox
$typeComboBox = New-Object System.Windows.Forms.ComboBox
$typeComboBox.Location = "320,80"
$typeComboBox.Size = "120,20"
$typeComboBox.ForeColor = "Indigo"
$typeComboBox.BackColor = "White"
[void]$typeComboBox.items.add("All Fonts")
[void]$typeComboBox.items.add("Dings Only")        
$typeComboBox.SelectedIndex = 0
$mainForm.Controls.Add($typeComboBox)

# Type ComboBox Label
$typeComboBoxLabel = New-Object System.Windows.Forms.Label 
$typeComboBoxLabel.Location = "200,80"
$typeComboBoxLabel.Size = "100,23"
$typeComboBoxLabel.ForeColor = "MediumBlue"
$typeComboBoxLabel.Text = "Character Type"
$mainForm.Controls.Add($typeComboBoxLabel)

# Background ComboBox
$backgroundComboBox = New-Object System.Windows.Forms.ComboBox
$backgroundComboBox.Location = "320,120"
$backgroundComboBox.Size = "120,20"
$backgroundComboBox.ForeColor = "Indigo"
$backgroundComboBox.BackColor = "White"
[void]$backgroundComboBox.items.add("Black")
[void]$backgroundComboBox.items.add("White")
[void]$backgroundComboBox.items.add("Random")
[void]$backgroundComboBox.items.add("Transparent")               
$backgroundComboBox.SelectedIndex = 0
$mainForm.Controls.Add($backgroundComboBox)

# Background ComboBox Label
$backgroundComboBoxLabel = New-Object System.Windows.Forms.Label 
$backgroundComboBoxLabel.Location = "200,120"
$backgroundComboBoxLabel.Size = "100,23"
$backgroundComboBoxLabel.ForeColor = "MediumBlue"
$backgroundComboBoxLabel.Text = "Background Color"
$mainForm.Controls.Add($backgroundComboBoxLabel)

 
Lastly are a couple Buttons for Execute and Exit.
 

# Draw Button
$drawButton = New-Object System.Windows.Forms.Button 
$drawButton.Location = "40,80"
$drawButton.Size = "75,23"
$drawButton.ForeColor = "SeaGreen"
$drawButton.BackColor = "White"
$drawButton.Text = "Draw"
$drawButton.add_Click({DrawIt})
$mainForm.Controls.Add($drawButton)

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

 
In the Drawing Function first I define the Output Image and set the background color as selected.
 

# Draw the Object
Function DrawIt {
    $Global:outFile = ""
    # Setup Image
    $bitmap = new-object System.Drawing.Bitmap 1024,768
    $bitmapGraphics = [System.Drawing.Graphics]::FromImage($bitmap) 
    # Image Background Color
    If ($backgroundComboBox.Text -eq "Transparent") {
        $bitmap.MakeTransparent()
    } Else {
        If ($backgroundComboBox.Text -eq "Random") {
            $red   = (Get-Random -minimum 0 -maximum 255)
            $green = (Get-Random -minimum 0 -maximum 255)
            $blue  = (Get-Random -minimum 0 -maximum 255)
            $backColor = [System.Drawing.Color]::FromArgb($red, $green, $blue)
            $bitmapGraphics.Clear($backColor)
        } Else {
            $bitmapGraphics.Clear($backgroundComboBox.Text)
        }
    }

 
I load all the installed font names into an array. If Dings Only is selected then I load only them.
 

    # Add All Installed Font Names into an Array
    $fontFamilies = @()
    $fontsCollection = New-Object System.Drawing.Text.InstalledFontCollection
    If ($typeComboBox.Text -eq "All Fonts") {
        $fontFamilies = $fontsCollection.Families
    } Else {
        If ($typeComboBox.Text -eq "Dings Only") {
            For($i=0; $i -le $fontsCollection.Families.length-1) {
                If ($fontsCollection.Families[$i].Name -like "Wingdings") {
                    $fontFamilies += $fontsCollection.Families[$i].Name
                }
                If ($fontsCollection.Families[$i].Name -like "Webdings") {
                    $fontFamilies += $fontsCollection.Families[$i].Name
                }
                $i++
            }
        }
    }

 
Inside the loop which write the character to the image I randomize and load a Font. Then I randomize the font color and location. Finally the character is drawn.
 

    $i = 0
    While ($i -le $global:numberValue) { $i++
        # Randomize Font Name and Size
        $fontSelector = (Get-Random -minimum 0 -maximum $fontFamilies.Length)
        $fontName = $fontFamilies[$fontSelector]
        $size = (Get-Random -minimum 4 -maximum 60)
        Try {
        $font = new-object System.Drawing.Font $fontName,$size
        }
        Catch {
        Write-Host "Unable to load Font -" $fontName
        }
        # Randomize Foreground Color
        $red  = (Get-Random -minimum 1 -maximum 255)
        $green = (Get-Random -minimum 1 -maximum 255)
        $blue  = (Get-Random -minimum 1 -maximum 255)
        $brushColorFg = [System.Drawing.Color]::FromArgb($red, $green, $blue)
        $brushFg = New-Object System.Drawing.SolidBrush $brushColorFg
        # Randomize Position
        $pointX = (Get-Random -minimum -10 -maximum 1034)
        $pointY = (Get-Random -minimum -10 -maximum 778)
        $letter = (Get-Random -minimum 33 -maximum 136)
        # Draw the Letter
        $bitmapGraphics.DrawString(([char]($letter)),$font,$brushFg,$pointX,$pointY)
    }

 
The image file is written to disk with a random name where the script is located. The image is displayed using the system default image viewer. Lastly the image is disposed of, ready for the next run.
 

    # Display Image
    $Global:outFile = $scriptPath + "\"  + "RandomCharacters_" + (Get-Date -UFormat %Y%m%d_%H%M%S) + ".bmp"
    $bitmap.Save($Global:outFile)
    Invoke-Item $Global:outFile
    # Cleanup
    $bitmapGraphics.Dispose()  
}

 
DrawStringRandom
 
Download the complete script here – DrawStringRandom.ps1

PowerShell Draw Cartoon Face

Leave a comment

This script will allow you to create a simple cartoon face controlling a few parameters like the pupil size, nose size and level of smile. Used to create the cartoon face are System.Drawing.Graphics controls like FillEllipse, DrawCurve and DrawLine. The background color is somewhat selectable while the object colors are random.
 
Here are a couple examples from the script –
 
CartoonFace_20140322_170532
 
CartoonFace_20140314_184605
 
In a WinForm Form I defined the Form and set some default values.
 

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

# Main Form
$mainForm = New-Object Windows.Forms.Form
$mainForm.Location = "200,200"
$mainForm.FormBorderStyle = "FixedDialog"
$mainForm.BackColor = "White"
$mainForm.Font = “Comic Sans MS,8.25"
$mainForm.Text = "Draw Cartoon Face"
$mainForm.size = "520,280"

# Global Values
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$global:pupilValue = 5
$global:eyebrowValue = 5
$global:noseValue = 5
$global:mouthValue = 8
$global:backgroundText = "Black"

 
I collected the object parameters with four TrackBar controls. The TrackBar is setup wiyh a Label to show the name and the values as the TrackBar is moved. I added Plus/Minus symbols to make it easy to see in what direction your changing the values.
 

# Pupil TrackBar
$pupilTrackBar = New-Object Windows.Forms.TrackBar
$pupilTrackBar.Location = "140,20"
$pupilTrackBar.Orientation = "Horizontal"
$pupilTrackBar.Width = 350
$pupilTrackBar.Height = 40
$pupilTrackBar.LargeChange = 5
$pupilTrackBar.SmallChange = 1
$pupilTrackBar.TickFrequency = 1
$pupilTrackBar.TickStyle = "TopLeft"
$pupilTrackBar.SetRange(1, 8)
$pupilTrackBar.Value = 3
$pupilTrackBarValue = 3
$pupilTrackBar.add_ValueChanged({
    $pupilTrackBarValue = $pupilTrackBar.Value
    $pupilTrackBarLabel.Text = "Pupil Size ($pupilTrackBarValue)"
    $global:pupilValue = $pupilTrackBarValue
})
$mainForm.Controls.add($pupilTrackBar)

# Pupil Labels
$pupilTrackBarLabel = New-Object System.Windows.Forms.Label 
$pupilTrackBarLabel.Location = "20,20"
$pupilTrackBarLabel.Size = "100,23"
$pupilTrackBarLabel.ForeColor = "MediumBlue"
$pupilTrackBarLabel.Text = "Pupil Size ($pupilTrackBarValue)"
$mainForm.Controls.Add($pupilTrackBarLabel)
$pupilTrackBarPlus = New-Object System.Windows.Forms.Label 
$pupilTrackBarPlus.Location = "490,20"
$pupilTrackBarPlus.Size = "15,23"
$pupilTrackBarPlus.ForeColor = "Red"
$pupilTrackBarPlus.Text = " +"
$mainForm.Controls.Add($pupilTrackBarPlus)
$pupilTrackBarMinus = New-Object System.Windows.Forms.Label 
$pupilTrackBarMinus.Location = "130,20"
$pupilTrackBarMinus.Size = "15,23"
$pupilTrackBarMinus.ForeColor = "Red"
$pupilTrackBarMinus.Text = "- "
$mainForm.Controls.Add($pupilTrackBarMinus)

 
A ComboBox control provides the selections for the background color.
 

# Background ComboBox
$backgroundComboBox = New-Object System.Windows.Forms.ComboBox
$backgroundComboBox.Location = "145,200"
$backgroundComboBox.Size = "120,20"
$backgroundComboBox.ForeColor = "Indigo"
$backgroundComboBox.BackColor = "White"
[void]$backgroundComboBox.items.add("Black")
[void]$backgroundComboBox.items.add("White")
[void]$backgroundComboBox.items.add("Random")         
$backgroundComboBox.SelectedIndex = 0
$mainForm.Controls.Add($backgroundComboBox)

# Background ComboBox Label
$backgroundComboBoxLabel = New-Object System.Windows.Forms.Label 
$backgroundComboBoxLabel.Location = "20,200"
$backgroundComboBoxLabel.Size = "100,23"
$backgroundComboBoxLabel.ForeColor = "MediumBlue"
$backgroundComboBoxLabel.Text = "Background Color"
$mainForm.Controls.Add($backgroundComboBoxLabel)

 
To complete the Form a couple buttons for the Draw and Exit actions are provided.
 

# Draw Button
$drawButton = New-Object System.Windows.Forms.Button 
$drawButton.Location = "320,200"
$drawButton.Size = "75,23"
$drawButton.ForeColor = "SeaGreen"
$drawButton.BackColor = "White"
$drawButton.Text = "Draw"
$drawButton.add_Click({DrawIt})
$mainForm.Controls.Add($drawButton)

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

 
The Draw button calls the main drawing routine. I setup the output image and configure the background color depending on the selection made.
 

# Draw the Object
Function DrawIt {
$bitmap = New-Object System.Drawing.Bitmap(1024, 768)
$bitmapGraphics = [System.Drawing.Graphics]::FromImage($bitmap) 
$pen = New-Object Drawing.Pen "white"
# Image Background Color
If ($backgroundComboBox.Text -eq "Transparent") {
    $bitmap.MakeTransparent()
} Else {
    If ($backgroundComboBox.Text -eq "Random") {
        $red   = (Get-Random -minimum 0 -maximum 255)
        $green = (Get-Random -minimum 0 -maximum 255)
        $blue  = (Get-Random -minimum 0 -maximum 255)
        $backColor = [System.Drawing.Color]::FromArgb($red, $green, $blue)
        $bitmapGraphics.Clear($backColor)
    } Else {
        $bitmapGraphics.Clear($backgroundComboBox.Text)
    }
}

 
First the head is drawn. I set the Brush to a random color and draw it positioned relative to the outside edge. The individual objects are then drawn in the head. The selected value is replaced with the divisor used to position the object.
 

Function DrawHead {
    $red = (Get-Random -minimum 0 -maximum 255)
    $green = (Get-Random -minimum 0 -maximum 255)
    $blue = (Get-Random -minimum 0 -maximum 255)
    $brushColor = [System.Drawing.Color]::FromArgb($red, $green, $blue)
    $brush = New-Object System.Drawing.SolidBrush $brushColor
    $x = 1024/10
    $y = 768/10
    $width = 1024 - (1024/4)
    $height = 768 - (768/7)
    $bitmapGraphics.FillEllipse($brush, $x, $y, $width, $height)
}

Function DrawPupil {
    If ([int]$global:pupilValue -eq 1) {$value = 90}
    If ([int]$global:pupilValue -eq 2) {$value = 80}
    If ([int]$global:pupilValue -eq 3) {$value = 70}
    If ([int]$global:pupilValue -eq 4) {$value = 60}
    If ([int]$global:pupilValue -eq 5) {$value = 50}
    If ([int]$global:pupilValue -eq 6) {$value = 40}
    If ([int]$global:pupilValue -eq 7) {$value = 30}
    If ([int]$global:pupilValue -eq 8) {$value = 20}
    $red = (Get-Random -minimum 0 -maximum 255)
    $green = (Get-Random -minimum 0 -maximum 255)
    $blue = (Get-Random -minimum 0 -maximum 255)
    $brushColor = [System.Drawing.Color]::FromArgb($red, $green, $blue)
    $brush = New-Object System.Drawing.SolidBrush $brushColor
    $x1 = 1024/2.9
    $x2 = 1024/1.7
    $y = 768/2.7
    $width = 1024/$value
    $height = 768/$value
    $bitmapGraphics.FillEllipse($brush, $x1, $y, $width, $height)
    $bitmapGraphics.FillEllipse($brush, $x2, $y, $width, $height)
}

Function DrawNose {
    If ([int]$global:noseValue -eq 1)  {$value = 1.9}
    If ([int]$global:noseValue -eq 2)  {$value = 1.85}
    If ([int]$global:noseValue -eq 3)  {$value = 1.8}
    If ([int]$global:noseValue -eq 4)  {$value = 1.75}
    If ([int]$global:noseValue -eq 5)  {$value = 1.7}
    If ([int]$global:noseValue -eq 6)  {$value = 1.65}
    If ([int]$global:noseValue -eq 7)  {$value = 1.6}
    If ([int]$global:noseValue -eq 8)  {$value = 1.55}
    If ([int]$global:noseValue -eq 9)  {$value = 1.5}
    $red = (Get-Random -minimum 0 -maximum 255)
    $green = (Get-Random -minimum 0 -maximum 255)
    $blue = (Get-Random -minimum 0 -maximum 255)
    $penColor = [System.Drawing.Color]::FromArgb($red, $green, $blue)
    $pen = New-Object Drawing.Pen $penColor
    $pen.Width = 6
    $nose = @()
    $x1 = 1024/2.1
    $y1 = 768/2.3
    $x2 = 1024/$value
    $y2 = 768/1.6
    $x3 = 1024/2.2
    $y3 = 768/1.5
    $noseNext = New-Object System.Drawing.Point($x1, $y1)
    $nose += $noseNext
    $noseNext = New-Object System.Drawing.Point($x2, $y2)
    $nose += $noseNext
    $noseNext = New-Object System.Drawing.Point($x3, $y3)
    $nose += $noseNext
    $bitmapGraphics.DrawCurve($pen, $nose)

 
Finally the image is written to disk and displayed with the default image viewer.
 

$outFile = $scriptPath + "\"  + "CartoonFace_" + (Get-Date -UFormat %Y%m%d_%H%M%S) + ".bmp"
$bitmap.Save($outFile)
Invoke-Item $outFile

 
The objects are positioned within the Form relative placement to the Form edge. After working with this method I think greater flexibility and control would be derived by positioning the objects relative to the head boundaries. Maybe following the arc of the head ellipse and using a position along a radius. The head could then be repositioned or resized and keep the other objects relative to it. The current method allows the image to be resized but not the head individually.
 
CartoonFace
 
Download the complete script here – CartoonFace.ps1

PowerShell Single Image Collage Maker

1 Comment

This script is very similar to the Collage Maker of the previous post. Instead of a folder full of images this script will randomly place a single image around in the Collage Image. Underlying is the same process but you get quite different results.
 
Here are a couple samples from this script –
 
ImageCollage1
 
And another with fewer, larger images –
 
ImageCollage2
 
I’m only going to review the couple of changes from the previous post.
 
Instead of using the FolderBrowserDialog control to select a folder I used the OpenFileDialog control to allow for file selection. The Browse Button calls the selectFiles Function for selecting the input file. After the image is selected the inputPicture Function is called to display the image in the form.
 

# Browse Input Button
$buttonBrowse = New-Object System.Windows.Forms.Button
$buttonBrowse.Location = "560, 80"
$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

 
SelectFile
 
Since only a single image is being used to generate the collage I wanted to display it as a preview. The input image is displayed with a PictureBox, the same as is used to show the final assembled collage. I used the Zoom SizeMode to fully fit the image within the PictureBox boundaries.
 

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

Function InputPicture {
    $file = (Get-Item $selectForm.FileName)
    $image = [System.Drawing.Image]::Fromfile($file)
    $inputPicture.Image = $image
}

 
The interface is a bit different from the previous post, mainly to accommodate two PictureBoxes.
 
ImageRandomizer
 
Download the complete script here – SingleImageCollage.ps1
 
Updated 7/5/14 – Added additional output images sizes and changed image size text parser from SubString to Split.
 

PowerShell Image Collage Maker

Leave a comment

This script will read in all the images from a folder, reduce them and randomly place them around in a new image. You control the size and number of the replicated images, the backgrounds color, whether they get a border and it’s color and width.
 
Here are some results from the script –
 
You can greatly reduce and replicate many times a lot of images so the final image is covered –
 
Collage_20140127_193941
 
Or just replicate a few images which are not so reduced for a different effect –
 
Collage_20140129_190200
 
The background can also be made transparent –
 
Collage_20140201_101539
 
In a WinForms Form I add the needed .Net namespaces, defined the basic form, set some global variables and get the scripts path.
 

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

# Global Values
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$global:reductionPercent = 10
$global:backgroundText = "Black"
$global:imageWidth = 1024
$global:imageHeight = 768
$global:imageBorder = 2
$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 = "Cornsilk"
$mainForm.Text = " Image Collage Maker"
$mainForm.Width = 1030
$mainForm.Height = 380

 
I then add the Input Image Filename TextBox and Label. To make filling in the name effective I added a Browse Button which calls a Function which uses the FolderBrowserDialog control to allow you to browse for the images folder.
 

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

# Input Box Label
$ProcessLabel = New-Object System.Windows.Forms.Label
$ProcessLabel.Location = "15, 12"
$ProcessLabel.Size = "300, 23"
$ProcessLabel.ForeColor = "MediumBlue" 
$ProcessLabel.Text = "Input Images Folder"
$mainForm.Controls.Add($ProcessLabel)

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

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

 
FolderBroswer
 
Next came the TrackBar control for the Number of Images and the Amount of Image Reduction. If you select 10% you end up images reduced to 10% of their original size.
 

# Image Reduction TrackBar
$reductionTrackBar = New-Object System.Windows.Forms.TrackBar
$reductionTrackBar.Location = "200,140"
$reductionTrackBar.Orientation = "Horizontal"
$reductionTrackBar.Width = 280
$reductionTrackBar.Height = 40
$reductionTrackBar.LargeChange = 20
$reductionTrackBar.SmallChange = 1
$reductionTrackBar.TickFrequency = 5
$reductionTrackBar.TickStyle = "TopLeft"
$reductionTrackBar.SetRange(1, 100)
$reductionTrackBar.Value = 10
$reductionTrackBarValue = 10
$reductionTrackBar.add_ValueChanged({
    $reductionTrackBarValue = $reductionTrackBar.Value
    $reductionTrackBarLabel.Text = "Image Reduced Size ($reductionTrackBarValue%)"
    $global:reductionPercent = $reductionTrackBarValue
})
$mainForm.Controls.add($reductionTrackBar)

# Image Reduction Label
$reductionTrackBarLabel = New-Object System.Windows.Forms.Label 
$reductionTrackBarLabel.Location = "15,140"
$reductionTrackBarLabel.Size = "180,23"
$reductionTrackBarLabel.ForeColor = "MediumBlue"
$reductionTrackBarLabel.Text = "Images Reduction Size ($reductionTrackBarValue%)"
$mainForm.Controls.Add($reductionTrackBarLabel)

 
TrackBar
 
ComboBoxes provide the selections for Background Color, Output Image Size, Border Color and Width.
 

# Background ComboBox
$backgroundComboBox = New-Object System.Windows.Forms.ComboBox
$backgroundComboBox.Location = "120,200"
$backgroundComboBox.Size = "120,20"
$backgroundComboBox.ForeColor = "Indigo"
$backgroundComboBox.BackColor = "White"
[void]$backgroundComboBox.items.add("Black")
[void]$backgroundComboBox.items.add("White")
[void]$backgroundComboBox.items.add("Random")
[void]$backgroundComboBox.items.add("Transparent")        
$backgroundComboBox.SelectedIndex = 0
$mainForm.Controls.Add($backgroundComboBox)

# Background ComboBox Label
$backgroundComboBoxLabel = New-Object System.Windows.Forms.Label 
$backgroundComboBoxLabel.Location = "15,200"
$backgroundComboBoxLabel.Size = "120,20"
$backgroundComboBoxLabel.ForeColor = "MediumBlue"
$backgroundComboBoxLabel.Text = "Background Color"
$mainForm.Controls.Add($backgroundComboBoxLabel)

 
ComboBoxes
 
After the collage has been configured you click the Process Button to generate the collage. The button calls the ProcessButton function first sets multiple values and a default color.
 

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

Function ProcessImage {
    $global:imageWidth  = [int]$imageComboBox.Text.Split("x")[0]
    $global:imageHeight = [int]$imageComboBox.Text.Split("x")[1]
    $global:imageBorder = [int]$borderComboBox.Text
    $completeLabel.Text = ""
    # Default Random Color
    $red   = (Get-Random -minimum 0 -maximum 255)
    $green = (Get-Random -minimum 0 -maximum 255)
    $blue  = (Get-Random -minimum 0 -maximum 255)

 
The collage Bitmap image object is defined as well as it’s Graphics Properties object and the Brush used for the borders. The Image Background color selection is processed. If Transparent is selected I used the MakeTransparent method. If Random then I set it to a randomly selected color. If neither them I set the color to be Black or White as selected. Next is the Image Border color. Either a Random color or the specific color selected in the ComboBox is used.
 

    # Create Image
    $bitmap = New-Object System.Drawing.Bitmap($global:imageWidth,$global:imageHeight)
    $bitmapGraphics = [System.Drawing.Graphics]::FromImage($bitmap)
    $brushBorder = New-Object System.Drawing.SolidBrush("Black")

    # Image Background Color
    If ($backgroundComboBox.Text -eq "Transparent") {
        $bitmap.MakeTransparent()
    } Else {
        If ($backgroundComboBox.Text -eq "Random") {
            $red   = (Get-Random -minimum 0 -maximum 255)
            $green = (Get-Random -minimum 0 -maximum 255)
            $blue  = (Get-Random -minimum 0 -maximum 255)
            $backColor = [System.Drawing.Color]::FromArgb($red, $green, $blue)
            $bitmapGraphics.Clear($backColor)
        } Else {
            $bitmapGraphics.Clear($backgroundComboBox.Text)
        }
    }

    # Image Border Color
    If ($borderColorComboBox.Text -eq "Random") {
        $red   = (Get-Random -minimum 0 -maximum 255)
        $green = (Get-Random -minimum 0 -maximum 255)
        $blue  = (Get-Random -minimum 0 -maximum 255)
        $borderColor = [System.Drawing.Color]::FromArgb($red, $green, $blue)
        $brushBorder = New-Object System.Drawing.SolidBrush($borderColor)
    } Else {
        $brushBorder = New-Object System.Drawing.SolidBrush($borderColorComboBox.Text)
    }

 
The main processing loop then runs. I fill the $files array with all the filename collected form the selected folder. Next I loop through the file for the slected number of interations. The file are loaded into a variable using the System.Drawing.Image Class FromFile method. I enclosed that method call in a Try/Catch error checking construct because non-image files will error. I found it more efficient to just let them error, throw the error away, and then let processing continue than to try and pretest for image files. A random starting point for the images is selected. The height and width is reduced to the selected percentage. If an image border was selected then a rectangle of the appropriate color is painted behind where the image will be placed but oversized by the selected value. There is no border method in the Image class so painting a rectangle behind makes the image appear to have a border. The image is then draw from the selected point over the border image. Finally the file is written using a filename structure including the data and time for uniqueness. The $error variable is checked to make sure we got a good write, the collage image displayed in a PictureBox and the COM objects disposed of.
 

    $i = 0; $folderName = $textBoxIn.Text
    $files = @()
    $files = Get-ChildItem $folderName
    While ($files.Length-2 -le $numberTrackbar.Value) {
        $files += Get-ChildItem $folderName  
    }
    While ($i -le $numberTrackBar.Value) { $i++
        $fileName = $folderName + "\" + $files[$i]
        Try {
            $imageFile = [System.Drawing.Image]::FromFile($fileName)
        }
        Catch {
        }
        $pointX = (Get-Random -minimum -100 -maximum ([int]$global:imageWidth + 100))
        $pointY = (Get-Random -minimum -100 -maximum ([int]$global:imageHeight + 100))
        $height = $imageFile.Height * ([int]$global:reductionPercent * .01)
        $width = $imageFile.Width * ([int]$global:reductionPercent * .01)
        If ($global:imageBorder -gt 0) {
	        $bitmapGraphics.FillRectangle($brushBorder, `
                $pointX-$global:imageBorder, `
                $pointY-$global:imageBorder, `
                $width +($global:imageBorder*2), `
                $height+($global:imageBorder*2))
        }
        $bitmapGraphics.DrawImage($imageFile, $pointX, $pointY, $width, $height)
    }

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

    $bitmap.Dispose()
    $bitmapGraphics.Dispose()
    If($error.count -gt 0) {
        $completeLabel.ForeColor = "Red"
        $completeLabel.Text = "Error!"
    }
} # End ProcessImage

 
The output Collage image is displayed in the Form using PictureBox Class. I use the Zoom SizeMode to center the image within the PictureBox.
 

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

Function FinalPicture {
    $file = (Get-Item $Global:outFile)
    $image = [System.Drawing.Image]::Fromfile($file)
    $finalPicture.Image = $image
}

 
The Display Button calls the default system viewer with Invoke-Item to display the image.
 

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

 
CollageMaker
 
Download the complete script here – CollageMaker.ps1
 
Updated 7/5/14 – Added additional output images sizes and changed image size text parser from SubString to Split.
 

PowerShell Random Geometric Patterns Image Maker

Leave a comment

I find that randomly displayed geometric objects like lines, circles, rectangles and Beziers make real interesting images. I enjoy the cool designs and like to find pattern in randomness. I put together a script which lets you create an image and randomly fill it with geometric objects. You can control the type and number of objects, image background color, output image size as well as drawing pen size and color. The image is saved to disk and displayed.
 
Here are a few examples generated from this script starting with Bezier Splines
 
Beziers1
 
Beziers2
 
Beziers3
 
and Connected Beziers
 
ConnectedBeziers1
 
ConnectedBeziers2
 
Lines
 
Lines1
 
Lines2
 
Connected Lines
 
ConnectedLines1
 
ConnectedLines2
 
Cornerbusrt Lines
 
LineCornerburst1
 
Starburst Lines
 
StarburstLine1
 
Rectangles
 
Rectangles1
 
Rectangles2
 
Ellipses
 
Ellipses1
 
Arcs
 
Arcs1
 
Pie Shapes
 
Pies1
 
Curves
 
Curves1
 
I setup a WinForms Form to collect the objects and pattern info. I defined some global values to be used in later functions.
 

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

# Main Form
$mainForm = New-Object Windows.Forms.Form
$mainForm.Location = "200,200"
$mainForm.FormBorderStyle = "FixedDialog"
$mainForm.BackColor = "Cornsilk"
$mainForm.Font = “Comic Sans MS,8.25"
$mainForm.Text = "Draw Random Geometric Patterns"
$mainForm.Size = "580,550"

# Global Values
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$global:numberObjects = 100
$global:penWidth = 1
$global:colorText = "Multi(Random)"
$global:backgroundText = "Black"
$global:selectedObject = "Beziers"
$global:imageWidth = 1024
$global:imageHeight = 768

 
I setup 12 RadioButtons to select the geometric object with.
 
GeometricPatterns1
 

# Arcs Object
$radioButtonArcs = New-Object System.Windows.Forms.RadioButton
$radioButtonArcs.Location = "80,30"
$radioButtonArcs.ForeColor = "Indigo"
$radioButtonArcs.Text = "Arcs"
$radioButtonArcs.add_Click({
    $global:selectedObject = "Arcs"
})
$mainForm.Controls.Add($radioButtonArcs)

# Beziers Object
$radioButtonBeziers = New-Object System.Windows.Forms.RadioButton
$radioButtonBeziers.Location = "80,60"
$radioButtonBeziers.ForeColor = "Indigo"
$radioButtonBeziers.Text = "Beziers"
$radioButtonBeziers.add_Click({
    $global:selectedObject = "Beziers"
})
$mainForm.Controls.Add($radioButtonBeziers)

 
I next added two TrackBar Controls with Labels to select the Number of Objects and Pen Size.
 
GeometricPatterns2
 

# Number of Objects TrackBar
$numberTrackBar = New-Object Windows.Forms.TrackBar
$numberTrackBar.Location = "180,280"
$numberTrackBar.Orientation = "Horizontal"
$numberTrackBar.Width = 350
$numberTrackBar.Height = 40
$numberTrackBar.LargeChange = 500
$numberTrackBar.SmallChange = 10
$numberTrackBar.TickFrequency = 500
$numberTrackBar.TickStyle = "TopLeft"
$numberTrackBar.SetRange(1, 5000)
$numberTrackBar.Value = 200
$numberTrackBarValue = 200
$numberTrackBar.add_ValueChanged({
    $numberTrackBarValue = $numberTrackBar.Value
    $numberTrackBarLabel.Text = "Number Objects ($numberTrackBarValue)"
    $global:numberObjects = $numberTrackBarValue
})
$mainForm.Controls.add($numberTrackBar)

# Object Number Label
$numberTrackBarLabel = New-Object System.Windows.Forms.Label 
$numberTrackBarLabel.Location = "10,280"
$numberTrackBarLabel.Size = "160,23"
$numberTrackBarLabel.ForeColor = "MediumBlue"
$numberTrackBarLabel.Text = "Number Objects ($numberTrackBarValue)"
$mainForm.Controls.Add($numberTrackBarLabel)

 
Three ComboBoxes Controls with Labels are added for Object and Background Color and Output Image Size.
 
GeometricPatterns3
 

# Background ComboBox
$backgroundComboBox = New-Object System.Windows.Forms.ComboBox
$backgroundComboBox.Location = "350,420"
$backgroundComboBox.Size = "120,20"
$backgroundComboBox.ForeColor = "Indigo"
$backgroundComboBox.BackColor = "White"
[void]$backgroundComboBox.items.add("Black")
[void]$backgroundComboBox.items.add("White")
[void]$backgroundComboBox.items.add("Random")         
$backgroundComboBox.SelectedIndex = 0
$mainForm.Controls.Add($backgroundComboBox)

# Background ComboBox Label
$backgroundComboBoxLabel = New-Object System.Windows.Forms.Label 
$backgroundComboBoxLabel.Location = "200,420"
$backgroundComboBoxLabel.Size = "100,23"
$backgroundComboBoxLabel.ForeColor = "MediumBlue"
$backgroundComboBoxLabel.Text = "Background Color"
$mainForm.Controls.Add($backgroundComboBoxLabel)

 
To comlete the Form I added a Draw and Exit button. Draw will call the appropriate function to draw the selected object. Exit closes the Form.
 
GeometricPatterns4
 

# Draw Button
$drawButton = New-Object System.Windows.Forms.Button 
$drawButton.Location = "40,420"
$drawButton.Size = "75,23"
$drawButton.ForeColor = "Green"
$drawButton.BackColor = "White"
$drawButton.Text = "Draw"
$drawButton.add_Click({DrawIt})
$mainForm.Controls.Add($drawButton)

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

 
As an example drawing function I selected Beziers. The Draw Button calls the DrawIt Function where the output image Width and Height variables are set from the ComboBox values. The appropriate drawing function is called from the value selected with the RadioButtons using Invoke-Expression.
 

Function DrawIt {
    $global:imageWidth = $imageComboBox.Text.Substring(0,4)
    $global:imageHeight = $imageComboBox.Text.Substring(5,4)
    Invoke-Expression $global:selectedObject
}

 
A default random color is set. The bitmap image is defined using System.Drawing.Bitmap and that images graphic properties exposed with System.Drawing.Graphics. The images background color is set depending on the ComboBox selection.
 

Function Beziers {
    # Default Random Color
    $red   = (Get-Random -minimum 0 -maximum 255)
    $green = (Get-Random -minimum 0 -maximum 255)
    $blue  = (Get-Random -minimum 0 -maximum 255)
    # Create Image
    $bitmap = New-Object System.Drawing.Bitmap([int]$global:imageWidth, [int]$global:imageHeight)
    $bitmapGraphics = [System.Drawing.Graphics]::FromImage($bitmap)
    # Image Background Color
    If ($backgroundComboBox.Text -eq "Random") {
        $red   = (Get-Random -minimum 0 -maximum 255)
        $green = (Get-Random -minimum 0 -maximum 255)
        $blue  = (Get-Random -minimum 0 -maximum 255)
        $backColor = [System.Drawing.Color]::FromArgb($red, $green, $blue)
        $bitmapGraphics.Clear($backColor)
    } Else {
        $bitmapGraphics.Clear($backgroundComboBox.Text)
    }

 
Next is a loop with an iteration equal to the number of objects. A drawing pen color is set by using the default random color or a new random color for each loop iteration. The pen is defined with using the selected color and width. Next the four x/y coordinates defining the Bezier spline are set using randomly selected points. Finally the Bezier is drawn onto the image with the DrawBezier method.
 

    # Main Loop
    $i = 0
    While ($i -le $global:numberObjects) { $i++
        # Get Random Color if Selected
        If ($colorsComboBox.Text -eq "MultiColored") {
            $red   = (Get-Random -minimum 0 -maximum 255)
            $green = (Get-Random -minimum 0 -maximum 255)
            $blue  = (Get-Random -minimum 0 -maximum 255)
        }
        # Setup Pen
        $penColor = [System.Drawing.Color]::FromArgb($red, $green, $blue)
        $pen = New-Object Drawing.Pen $penColor
        $pen.Width = $global:penWidth
        # Randomize All 4 Points of the Bezier
        $point1X = (Get-Random -minimum 0 -maximum $global:imageWidth)
        $point1Y = (Get-Random -minimum 0 -maximum $global:imageHeight)
        $point2X = (Get-Random -minimum 0 -maximum $global:imageWidth)
        $point2Y = (Get-Random -minimum 0 -maximum $global:imageHeight)
        $point3X = (Get-Random -minimum 0 -maximum $global:imageWidth)
        $point3Y = (Get-Random -minimum 0 -maximum $global:imageHeight)
        $point4X = (Get-Random -minimum 0 -maximum $global:imageWidth)
        $point4Y = (Get-Random -minimum 0 -maximum $global:imageHeight)
        # Draw Bezier Curve
        $bitmapGraphics.DrawBezier($pen, $point1X, $point1Y, $point2X, $point2Y, $point3X, $point3Y, $point4X, $point4Y)
    }

 
When the drawing loop has completed the image is saved to disk and displayed with the default Windows image viewer. The file name is made unique by post pending the date and time. The image files are written to the same folder where the script resides. When all processing has completed the image COM object is disposed of.
 

   
# Save Image to File and Display
    $outFile = $scriptPath + "\"  + "Beziers_" + (Get-Date -UFormat %Y%m%d_%H%M%S) + ".bmp"
    $bitmap.Save($outFile)
    Invoke-Item $outFile

    # Dispose of the image
    $bitmap.Dispose()
}

 
All the drawing functions are quite similar. They all use System.Drawing.Graphics methods like DrawLine, DrawEllipse, DrawCurve, DrawRectangle, DrawArc, DrawPie, etc. They vary by the way the points are defined, how many points there are and how the are positioned. The Connected objects, Lines and Beziers, are obviously not totally random as one end of each object connects to the previously drawn object. Polygons are the same as Connected Lines except they are randomized building the input array instead of by each line end point and since they are drawn in one continuous processing of the array, you only get a single drawing color. Curves are similar to Connected Beziers in the same way. The Starburst and Cornerburst Lines are also not totally random as they have a fixed point as well as a random point.
 
Some images I felt looked best with the all the objects being drawn within the image boundaries, such as both the Beziers and Connected Lines. Other images I thought were best looking with the drawing range of the objects expanding beyond the image boundaries like Arcs, Ellipses, Lines and Rectangles. The partial objects extending within the image edges adds to the randomness and pattern, I think. While with Connected Lines and Beziers, I think the sharp angles and continuousness were important and so I keep the objects within the image. With Arcs and Pie Shapes I put limits on the shapes so that Arcs and Pies are distinctly shaped and not just almost circles.
 
GeometricPatterns
 
Download the complete script here – GeometricPatterns.ps1

There is a new Version 2 of Geometric Patterns Maker here.

PowerShell Draw and Save Tiled Pattern Images

Leave a comment

This script uses a mathematical algorithm to define the X and Y coordinates for each pixel in the image. The result is a complex pattern which is repeated like tiles on a floor for an interesting final image. These patterns are fractal like insomuch as they have repeating patterns but lack a fractal dimension and are static.
 
I will credit the author of the original algorithm when I figure out who it was. I got the outline of the process from a QuickBasic program I wrote long, long ago. I didn’t have the credit in the code but I think it was from some fractal books I have and hope to find again one day.
 
Here is an example Tiled Pattern image –
 
TiledPatternExample
 
And a closeup of the pattern –
TiledPatternExampleCloseUp
 
Here are a few other examples from this Tiled Patterns script-
 
TiledPatterns_20131120_192034
 
TiledPatterns_20131127_213942
 
TiledPatterns30131121_184757
 
The are five variables which are used to control the pattern, so in a WinForms Form I defined five TrackBar controls for Alpha, Beta1, Beta2, Gamma and Modulus. I also added the Labels for each TrackBar and set the initial variable values and gathered the script path.
 

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

# Main Form
$mainForm = New-Object Windows.Forms.Form
$mainForm.BackColor = "White"
$mainForm.Font = “Comic Sans MS,8.25"
$mainForm.Text = "Tiled Pattern"
$mainForm.size = "500,350"

# Global Values
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$global:alphaValue = 2
$global:beta1Value = 4
$global:beta2Value = 4
$global:gammaValue = 134
$global:modulusValue = 2

# Alpha TrackBar
$alphaTrackBar = New-Object Windows.Forms.TrackBar
$alphaTrackBar.Location = "100,20"
$alphaTrackBar.Orientation = "Horizontal"
$alphaTrackBar.Width = 350
$alphaTrackBar.Height = 40
$alphaTrackBar.LargeChange = 5
$alphaTrackBar.SmallChange = 1
$alphaTrackBar.TickFrequency = 1
$alphaTrackBar.TickStyle = "TopLeft"
$alphaTrackBar.SetRange(1, 10)
$alphaTrackBar.Value = 4
$alphaTrackBarValue = 4
#Alpha TrackBar Event Handler
$alphaTrackBar.add_ValueChanged({
    $alphaTrackBarValue = $alphaTrackBar.Value
    $alphaLabel.Text = "Alpha ($alphaTrackBarValue)"
    $global:alphaValue = $alphaTrackBarValue
})
$mainForm.Controls.add($alphaTrackBar)

# Alpha Label
$alphaLabel = New-Object System.Windows.Forms.Label 
$alphaLabel.Location = "10,20"
$alphaLabel.Size = "120,23"
$alphaLabel.Text = "Alpha ($alphaTrackBarValue)"
$mainForm.Controls.Add($alphaLabel)

 
Next was added the two Buttons. One to call the DrawIt function to plot the pattern and one to close the Form.
 

# Draw Button
$DrawButton = New-Object System.Windows.Forms.Button 
$DrawButton.Location = "160,270"
$DrawButton.Size = "75,23"
$DrawButton.Text = "Draw"
$DrawButton.add_Click({DrawIt})
$mainForm.Controls.Add($DrawButton)

# Exit Button
$exitButton = New-Object System.Windows.Forms.Button
$exitButton.Location = "280,270"
$exitButton.Size = "75,23"
$exitButton.Text = "Exit"
$exitButton.add_Click({$mainForm.close()})
$mainForm.Controls.Add($exitButton)

 
In the main plotting function I first output a line on the host console to show processing has begun. Next I setup the output image file with a black background. I then get the values of the variable from the TrackBar selection (or initial values if not selected from TrackBar). The loop counters are initialized. The first processing steps percentage is set and a random color selected. During this function for each X position a Y is calculated. So in a X loop I first check for percent of processing done by looking for every one 100 pixels completed(this assumes the 1023 width so each 100 is close to 10% complete) and output to the host console. I then start a loop to plot the Y position. I make sure the pixel is positioned within the image and then apply the mathematical algorithm to get the Y position. Many values are generated but only the ones which have the modulus selected are used. Once Y is determined the pixel is written to the image using the previously selected random color. Lastly, after the image is fully created it is written to disk in the folder where the script resides. To keep the file names unique I concatenate the file name with the date and time. The newly written file is then displayed with the system default image viewer for that file type and a completion line is written to the host console.
 

# Draw the Pattern
Function DrawIt {
    Write-Host "Running . . ."

    # Output Bitmap Image
    $bitmap = New-Object System.Drawing.Bitmap(1024, 768)
    $bitmapGraphics = [System.Drawing.Graphics]::FromImage($bitmap) 
    $bitmapGraphics.Clear("black")

    $alpha = $global:alphaValue
    $beta1 = $global:beta1Value
    $beta2 = $global:beta2Value
    $gamma = $global:gammaValue
    $modulus  = $global:modulusValue

    $xPixel = 0; $yPixel = 0
    $percent = 10
    $totalPixels = 1023
    $red =   (Get-Random -minimum 0 -maximum 255)
    $green = (Get-Random -minimum 0 -maximum 255)
    $blue =  (Get-Random -minimum 0 -maximum 255)

    while ($xPixel -le $totalPixels) {
        $xPixel++ ; $yPixel = 1
        if (($xPixel % 100) -eq 0) {
            Write-Host "Processing" $percent"%"
            $percent = $percent + 10
        }
        while ($yPixel -le $totalPixels) { 
            $yPixel++
            if ($xPixel -ge 0) {
                if ($yPixel -ge 0) {
                    if ($xPixel -le 1023) {       
                        if ($yPixel -le 767) { 
                            $x=$beta1+($gamma*$xPixel)
                            $y=$beta2+($gamma*$yPixel)
                            $z=($alpha*([math]::sin($x))+[math]::sin($y))
                            $c=[math]::truncate($z)
                            if ($c % $modulus -eq 0) {
	                            $bitmap.SetPixel($xPixel, $yPixel, [System.Drawing.Color]::FromArgb($red, $green, $blue))
                            }
                        }
                    }
                }
            }
        }
    }

    $outFile = $scriptPath + "\"  + "TiledPatterns_" + (Get-Date -UFormat %Y%m%d_%H%M%S) + ".bmp"
    $bitmap.Save($outFile)
    Invoke-Item $outFile
    $bitmap.Dispose()
    $bitmapGraphics.Dispose()
    Write-Host "Complete"
}

 
TitledPatterns
 
Download – TiledPatterns.ps1

PowerShell Plot and Save Mira Orbital Fractal

Leave a comment

Fractals are infinitely self-similar, iterated and detailed mathematical constructs. Plotting them into an image can produce fantastic results. One type of Fractal is an Orbital Fractal or an Escape-Time Fractal, in which each point in space is calculated from a formula. One type of Orbital Fractal is the Mira Fractal. The Mira equation was developed at the CERN research centre in 1980 by I. Gumowski and C. Mira. It was not designed to create fractals, but to calculate the trajectories of sub-atomic particles. The algorithm used in this script originally came from ‘Fractals’ by Hans Lauwerier.

Here are some images generated from the below script –

Mira_20131123_172513

Mira_20131119_202343

Mira_20131123_165657

Mira_20131120_173238

Mira_20131122_175334

There are two values which control how the fractal is generated. I made a WinForms Form with TrackBar controls to provide those options. I setup the Main Form and defined some Global Values.

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

# Main Form
$mainForm = New-Object Windows.Forms.Form
$mainForm.BackColor = "White"
$mainForm.Font = “Comic Sans MS,8.25"
$mainForm.Text = "Mira Orbital Fractal"
$mainForm.size = "500,260"

# Global Values
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$global:colorsValue = 0
$global:iterationsValue = 0

I next setup the TrackBars defining thier orientation, location, size and tick properties. Moving the TrackBar feeds the counter on the Label using the valueChanged Event. The LargeChange and SmallChange properties move the pointer along the TrackBar like a tab when clicking on the bar. I then set the inital values for the TrackBar. The other properties and the Event Handler I discussed in a previous WinForms TrackBar blog.

# Iterations TrackBar
$iterationsTrackBar = New-Object Windows.Forms.TrackBar
$iterationsTrackBar.Location = "70,15"
$iterationsTrackBar.Orientation = "Horizontal"
$iterationsTrackBar.Width = 350
$iterationsTrackBar.Height = 40
$iterationsTrackBar.LargeChange = 25000
$iterationsTrackBar.SmallChange = 5000
$iterationsTrackBar.TickFrequency = 5000
$iterationsTrackBar.TickStyle = "TopLeft"
$iterationsTrackBar.SetRange(5000, 150000)
$iterationsTrackBar.Value = 30000
$iterTrackBarValue = 30000
#Iterations TrackBar Event Handler
$iterationsTrackBar.add_ValueChanged({
    $iterTrackBarValue = $iterationsTrackBar.Value
    $iterationsLabel.Text = "Iterations ($iterTrackBarValue)"
    $global:iterationsValue = $iterTrackBarValue
})
$mainForm.Controls.add($iterationsTrackBar)

# Colors Change TrackBar
$colorsTrackBar = New-Object Windows.Forms.TrackBar
$colorsTrackBar.Location = "70,130"
$colorsTrackBar.Orientation = "Horizontal"
$colorsTrackBar.Width = 350
$colorsTrackBar.Height = 40
$colorsTrackBar.LargeChange = 5000
$colorsTrackBar.SmallChange = 1000
$colorsTrackBar.TickFrequency = 1000
$colorsTrackBar.TickStyle = "BottomRight"
$colorsTrackBar.SetRange(1000, 20000)
$colorsTrackBarValue = 3000
$colorsTrackBar.Value = 3000
#Colors TrackBar Event Handler
$colorsTrackBar.add_ValueChanged({
$colorsTrackBarValue = $colorsTrackBar.Value
    $colorsLabel.Text = "Color Change ($colorsTrackBarValue)"
    $global:colorsValue = $colorsTrackBarValue
})
$mainForm.Controls.add($colorsTrackBar)

I then setup the Labels which provide a title for the TrackBars and show the counters.

# Iterations Label
$iterationsLabel = New-Object System.Windows.Forms.Label
$iterationsLabel.Location = "170,60"
$iterationsLabel.Size = "120,23"
$iterationsLabel.Text = "Iterations ($iterTrackBarValue)"
$mainForm.Controls.Add($iterationsLabel)

# Colors Label
$colorsLabel = New-Object System.Windows.Forms.Label
$colorsLabel.Location = "170,100"
$colorsLabel.Size = "160,23"
$colorsLabel.Text = "Color Change Point ($colorsTrackBarValue)"
$mainForm.Controls.Add($colorsLabel)

# Line Label
$lineLabel = New-Object System.Windows.Forms.Label
$lineLabel.Location = "170,75"
$lineLabel.Size = "150,20"
$lineLabel.Text = "______________________________________"
$mainForm.Controls.Add($lineLabel)

The “Plot” Button calls the PlotIt Function which will draw the image.

# Plot Button
$plotButton = New-Object System.Windows.Forms.Button
$plotButton.Location = "120,185"
$plotButton.Size = "75,23"
$plotButton.Text = "Plot"
$plotButton.add_Click({PlotIt})
$mainForm.Controls.Add($plotButton)

To plot the fractal I first define the bitmap image. I set the backgroup of that image to black. I write a line to the host to show processing has started.

# Plot the Fractal
Function PlotIt {
    Write-Host "Running . . ."

    # Output Bitmap Image
    $bitmap = New-Object System.Drawing.Bitmap(1024, 768)
    $bitmapGraphics = [System.Drawing.Graphics]::FromImage($bitmap)
    $bitmapGraphics.Clear("black")

The values used in the fractal formula are initialized. A Random component is setup so all the fractals will be unique.

    $a = (Get-Random -minimum .0001 -maximum .9999)
    $b = .9998
    $c = 2-(2*$a)
    $x = 0
    $y = 12.1
    $w = (($a*$x)+($c*$x*$x))/(1+($x*$x))

The values which control the appearance are setup. The Multiplier resizes the image from the tiny format the formula generates. At a 25 $multiplier most fractals are full size within the image but some will span the image boundary and so be partially cut off. The $colorChange value controls at which interation the color value changes during processing. When the colors change, a proessing status line is written to the host. The $iterations value control how many cycles are ran. The image is generated from the outside to the inside so using a very high number of iterations will result in more “solid center(s)” in the fractal. After much over 150k there isn’t any significant change in the image since those few pixels have all been colored. You can keep running higher iterations and more detail will be generated, however at this resolution it is not visible. There is not a limit to the iterations, which gives Mira it’s Fractal Dimension. A small number will result in more “airy” images without “solid” sections. The below image was generated at 15k interations changing colors every 1000 cycles to show the difference.

    $multiplier = 25
    $colorChange = $global:colorsValue
    $maxIterations = $global:iterationsValue

Mira_20131123_185325

I initatize the counter and setup the centering values. The original formula uses a Cartesian coordinates system for plotting the points. That places point 0,0 in the center of the screen. But for this bitmap image 0,0 is the upper left corner. So I add half the screen, minus the original size, back to each value on each side to (generally) center it. I select random values for the first color.

    $counter = 1
    $xCentering = 470
    $yCentering = 380
    $red =   (Get-Random -minimum 0 -maximum 255)
    $green = (Get-Random -minimum 0 -maximum 255)
    $blue =  (Get-Random -minimum 0 -maximum 255)

I initiate the While loop, incrementing the $counter and testing for $colorChange. If $colorChange then I write a line to the host and set a random color for the next cycles.
The pixels are then expanded with $multiplier and centered (relatively) within the bitmp. The pixels are tested to fit within the bitmap and the pixel is drawn with SetPixel Method of the System.Drawing.Bitmap Class. After the pixel has been painted the next round of fractal calculation is processed and the While loop continues.

    while ($counter -le $maxIterations) { $counter++
        if (($counter % $colorChange) -eq 0) {
            Write-Host $counter "Cycles"
            $red =   (Get-Random -minimum 0 -maximum 255)
            $green = (Get-Random -minimum 0 -maximum 255)
            $blue =  (Get-Random -minimum 0 -maximum 255)
        }
        $xPixel = ($x * $multiplier) + $xCentering
        $yPixel = ($y * $multiplier) + $yCentering
        if ($xPixel -ge 0) {
            if ($yPixel -ge 0) {
                if ($xPixel -le 1023) {
                    if ($yPixel -le 767) {
                        $bitmap.SetPixel($xPixel, $yPixel, [System.Drawing.Color]::FromArgb($red, $green, $blue))
                    }
                }
            }
        }
        $z = $x
        $x = ($b*$y)+$w
        $u = $x*$x
        $w = ($a*$x)+($c*$u)/(1+$u)
        $y = $w-$z
    }

When the fractal has been fully plotted the file is written to the disk. I select as the file location the same folder where the script resides. The file name is made unique by postpending the date and time to the name “Mira”.
The bitmap is displayed from the newly created file using the default image viewer on the system by calling Invoke-Item.
I then dispose of the $bitmap COM objects to clear them and write a completion line to the host.

    $outFile = $scriptPath + "\"  + "Mira_" + (Get-Date -UFormat %Y%m%d_%H%M%S) + ".bmp"
    $bitmap.Save($outFile)
    Invoke-Item $outFile
    $bitmap.Dispose()
    $bitmapGraphics.Dispose()
    Write-Host "Complete"
}

The last step is an Exit Button to close the Main Form.

# Exit Button
$exitButton = New-Object System.Windows.Forms.Button
$exitButton.Location = "300,185"
$exitButton.Size = "75,23"
$exitButton.Text = "Exit"
$exitButton.add_Click({$mainForm.close()})
$mainForm.Controls.Add($exitButton)

The Main Form –

Mira<

Download – Mira.ps1

PowerShell QR Code Creator

Leave a comment

A QR Code is an abbreviation of Quick Response Code, a two-dimensional barcode containing text data. Originally developed by the auto industry in Japan for inventory control, today they commonly contain URL’s for immediate access to a website. They are on everything from billboards to cereal boxes to business cards.

AdminsCache

This QR Code contains the text – http://AdminsCache.Wordpress.com.
Scanning with the barcode scanner of a smart camera will allow you to be redirected to the site.

A company called SYN Systems GmbH has very graciously provide a public API to their QR Code generation service called “create-qr-code“. It is a nice API providing many functions to customize your QR Code including fore and background color, ECC depth, different file formats and image and quiet zone sizing. You can squeeze up to 900 characters in the code, depending on quiet zone size and other factors. The Quiet Zone is blank space around the barcode which allows the scanner to recognize the object as a barcode, separating it from the background.

In a WinForms Form I setup the Labels and TextBoxes for gathering the parameters to send to the QR Code generation service. I center the form in the display and select the colors and size. For some input I provide a Help Label to show valid parameters.

Add-Type -AssemblyName System.Windows.Forms
# Main Form
$mainForm = New-Object System.Windows.Forms.Form
$mainForm.Font = "Comic Sans MS,10"
$mainForm.Text = " Generate QR Code Image File"
$mainForm.StartPosition = "centerscreen"
$mainForm.ForeColor = "White"
$mainForm.BackColor = "Wheat"
$mainForm.Size = "460, 460"
$mainForm.MinimumSize = "460, 460"

# Size Label
$sizeLabel = New-Object System.Windows.Forms.Label
$sizeLabel.Location = "40,40"
$sizeLabel.ForeColor = "MediumBlue"
$sizeLabel.Size = "80, 22"
$sizeLabel.Text = "Image Size"
$mainForm.Controls.Add($sizeLabel)

# Size Help Label
$sizeHelpLabel = New-Object System.Windows.Forms.Label
$sizeHelpLabel.Location = "230,40"
$sizeHelpLabel.ForeColor = "DarkOrange"
$sizeHelpLabel.Size = "200, 22"
$sizeHelpLabel.Text = "10x10 - 1000x1000  (Square)"
$mainForm.Controls.Add($sizeHelpLabel)

# Size TextBox
$sizeTextBox = New-Object System.Windows.Forms.TextBox
$sizeTextBox.Location = "120,40"
$sizeTextBox.Size = "100,20"
$sizeTextBox.ForeColor = "MediumBlue"
$sizeTextBox.BackColor = "White"
$sizeTextBox.Text = "250x250"
$mainForm.Controls.Add($sizeTextBox)

For convenience I use a ComboBox control to offer the valid File Format selections. Since there were only three options I just plugged them in directly instead of using an Array or Here-String to hold the values. EPS and SVG are also supported by the API but the memory used and files sizes get very large fast and so I didn’t include them.

# Format Label
$fileFormatLabel = New-Object System.Windows.Forms.Label
$fileFormatLabel.Location = "40,100"
$fileFormatLabel.ForeColor = "MediumBlue"
$fileFormatLabel.Size = "80, 22"
$fileFormatLabel.Text = "File Format"
$mainForm.Controls.Add($fileFormatLabel)

# Format ComboBox
$fileFormatComboBox = New-Object System.Windows.Forms.ComboBox
$fileFormatComboBox.Location = "120,100"
$fileFormatComboBox.Size = "60,20"
$fileFormatComboBox.ForeColor = "MediumBlue"
$fileFormatComboBox.BackColor = "White"
$fileFormatComboBox.Items.Add("gif")
$fileFormatComboBox.Items.Add("jpg")
$fileFormatComboBox.Items.Add("png")
$fileFormatComboBox.Text = "jpg"
$mainForm.Controls.Add($fileFormatComboBox)

After completing the parameter selection you press the “Generate” button which calls the GenerateQRCode Function. In that Function I first reset the error checking variables. I define the path to the QR Code image file as the location of the script file.

$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition

The file name consist of “QRCode” followed by the Date and the Time all underscore separated. The Extension is selectable. This will allow for all files generated to be uniquely named.

QRCodeFileNames

I then define the WebClient Class object. I use this to download the newly created image file from the website of the generation service. So I call the DownLoadFile Method of the WebClient which takes two parameters, URI and local path. The URI is the URL of the code generation site calling the API while passing the collected configuration parameters.

I validate no errors during the call or file write and write the results code to the Form. Next the PictureBox Function is called to display the created QR Code, dispose of the WebClient object and exit the Function.

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

Function GenerateQRcode {
    $error.clear()
	$path = $scriptPath + "\QRCode_" + (Get-Date -UFormat %Y%m%d_%H%M%S) + "." + $fileFormatComboBox.Text
    $WebClient = new-object System.Net.WebClient
    $WebClient.DownloadFile(("http://api.qrserver.com/v1/create-qr-code/?data=" + $encodeTextBox.Text + `
        "&ecc=" + $eccTextBox.Text +`
        "&size=" + $sizeTextBox.Text +`
        "&qzone=" + $quietZoneTextBox.Text + `
        "&color=" + $foreColorTextBox.Text + `
        "&bgcolor=" + $backColorTextBox.Text + `
        "&format=" + $fileFormatComboBox.Text), $path)
    If($error.count -gt 0) {
        $completedLabel.Text = "Error!"
        $completedLabel.ForeColor = "Red"
    }
    Else {
        $completedLabel.Text = "Success!"
        $completedLabel.ForeColor = "Green"
    }
    PictureBox($path)
    $WebClient.Dispose()
}

In the PictureBox Function I read the QR Code image file back in and display it in a PictureBox Control. I use the Zoom SizeMode Property to keep the image sized to fit within the defined ClientSize Property boundary.

# PictureBox
$pictureBox = New-Object System.Windows.Forms.PictureBox
$pictureBox.Location = "120, 150"
$pictureBox.ClientSize = "250, 250"
$pictureBox.SizeMode = "Zoom"
$mainForm.Controls.Add($pictureBox)

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

Lastly I have an Exit button to close the Form and end the Session.

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

QRCode

Download – QRCode.ps1

Two Calendar Controls for PowerShell

Leave a comment

Often you need to collect a date as a parameter for a script. There are several ways to display a GUI calendar to select the date from. I made a demo script for two of the calendar controls. Both are part of the System.Windows.Forms namespace. The MonthCalendar Class displays in a monthly view. You can move forward or back through the months or click into it and change to a less and less granular view. The DateTimePicker Class defaults to a single line display which can open into the monthly view.

In a simple WinForms form –

Add-Type -AssemblyName System.Windows.Forms
# Main Form
$mainForm = New-Object System.Windows.Forms.Form
$mainForm.Font = "Comic Sans MS,8.25"
$mainForm.Text = " Calendar Controls Demo"
$mainForm.ForeColor = "White"
$mainForm.BackColor = "DarkOliveGreen"
$mainForm.Width = 400
$mainForm.Height = 30

I added the MonthCalendar control. I set several properties including minimum and maximum dates displayed, how many dates can be concurrently selected, which day of the week in the first column and several others. This control has a lot of properties to manage it’s presentation.

# MonthCalendar
$monthCal = New-Object System.Windows.Forms.MonthCalendar
$monthCal.Location = "8,24"
$monthCal.MinDate = New-Object System.DateTime(2013, 1, 1)
$monthCal.MinDate = "01/01/2012"    # Minimum Date Dispalyed
$monthCal.MaxDate = "12/31/2013"    # Maximum Date Dispalyed
$monthCal.MaxSelectionCount = 1     # Max number of days that can be selected
$monthCal.ShowToday = $false        # Show the Today Banner at bottom
$monthCal.ShowTodayCircle = $true   # Circle Todays Date
$monthCal.FirstDayOfWeek = "Sunday" # Which Day of the Week in the First Column
$monthCal.ScrollChange = 1          # Move number of months at a time with arrows
$monthCal.ShowWeekNumbers = $false  # Show week numbers to the left of each week
$mainForm.Controls.Add($monthCal)

I then added the DateTimePicker control. I set the min and max dates to match the MonthCalendar controls limitations. Not all of the MonthCalendar Properties are available with the DateTimePicker.

# DateTimePicker
$datePicker = New-Object System.Windows.Forms.DateTimePicker
$datePicker.Location = "18,220"
$datePicker.MinDate = "01/01/2013" # Minimum Date Dispalyed
$datePicker.MaxDate = "12/31/2013" # Maximum Date Dispalyed
$mainForm.Controls.Add($datePicker)

The Select button will add the selected date to the TextBox for that calendar on the click event. The MonthCalendar and DateTimePicker controls work slightly differently. With MonthCalendar you can select a date range so to get a single date you use the SelectionStart property. While DateTimePicker supports only a single date and so you use it’s Value property.

# Select Button 1
$dateTimePickerButton = New-Object System.Windows.Forms.Button
$dateTimePickerButton.Location = "270,150"
$dateTimePickerButton.Size = "75,23"
$dateTimePickerButton.ForeColor = "DarkBlue"
$dateTimePickerButton.BackColor = "White"
$dateTimePickerButton.Text = "<<<-- Select"
$dateTimePickerButton.add_Click({
    $textBox.Text = $monthCal.SelectionStart
    $textBox.Text = $textBox.Text.Substring(0,10)
})
$mainForm.Controls.Add($dateTimePickerButton)

 

# Select Button 2
$monthlyCalendarButton = New-Object System.Windows.Forms.Button
$monthlyCalendarButton.Location = "270,220"
$monthlyCalendarButton.Size = "75,23"
$monthlyCalendarButton.ForeColor = "DarkBlue"
$monthlyCalendarButton.BackColor = "White"
$monthlyCalendarButton.Text = "<<<-- Select"
$monthlyCalendarButton.add_Click({
    $textBox.Text = $datePicker.Value
    $textBox.Text = $textBox.Text.Substring(0,10)
})
$mainForm.Controls.Add($monthlyCalendarButton)

CalendarsDemo

CalendarsDemo.ps1

PowerShell Base64 Encoding of Images

14 Comments

It is possible to convert the binary data in an image file to plain ASCII text so it can included directly in your PowerShell script, without the need for separate files. This is accomplished with Base64 Encoding. Base64 Encoding basically takes 3 binary bytes (24 bits) and converts them into 4 ASCII characters. I’m going to take my pervious post of NotifyIcon and embed the two icon files into the script text.

To facilitate this I put together a utility to do the encoding and spilt the file into 80 byte length records so it is easy to use in a script. You browse out to an image file and select it. The default output file is placed in the same folder with the txt extension. You can manually change those values. Then click convert and you have a text file of your image. I break the output into 80 byte chunks because I find lines potentially thousands of bytes long inconvenient in an editor or IDE.

To create the utility called Image2Bas64, in a WinForms window I added two TextBox objects for the input and output files.

Add-Type -AssemblyName System.Windows.Forms
# WinForm Setup
$mainForm = New-Object System.Windows.Forms.Form
$mainForm.Font = "Comic Sans MS,9"
$mainForm.ForeColor = "Black"
$mainForm.BackColor = "PaleGoldenrod"
$mainForm.Text = " Image2Base64 Converter"
$mainForm.Width = 570
$mainForm.Height = 200

# Output Box
$textBoxOut = New-Object System.Windows.Forms.TextBox
$textBoxOut.Location = "35, 85"
$textBoxOut.Size = "415, 20"
$mainForm.Controls.Add($textBoxOut)

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

Added were three buttons; Browse, Convert and Exit. The Browse button calls a function using the OpenFileDialog class object to open a file dialog. I open at the current folder, showing all files. It is the usual Windows Open File dialog so it is normal to use. After selecting an image file I set the input file and output file values and they are displayed. I assume a 3 digit file extensions (jpg, gif, ico, etc) and substitute ‘txt’ for whatever extension was there. I lastly clear the completeLabel so we’re ready for the next convert.

# Browse Input Button
$buttonBrowse = New-Object System.Windows.Forms.Button
$buttonBrowse.Location = "460, 25"
$buttonBrowse.Size = "75, 23"
$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
        	$work = $selectForm.FileName
        	$work = $work.SubString(0, ($work.Length -3)) + "txt"
        	$textBoxOut.Text = $work
        	$completeLabel.Text = "      "
	}
} # End SelectFile

The Convert button uses the Convert class to encode into Base64. After encoding the file into an array, it is broken into 80 byte chunks by inserting a carriage return and line feed and writing the text file. I added a basic error check to insure the file open/close was successful.

Function convertButton {
    $error.clear()
    $inFileName = $textBoxIn.Text
    $outFileName = $textBoxOut.Text
    $encodedImage = [convert]::ToBase64String((get-content $inFileName -encoding byte))
    $encodedImage -replace ".{80}" , "$&`r`n" | set-content $outFileName
    If ($error.count -gt 0) {
        $completeLabel.Text = "Error!"
    }
    Else {
        $completeLabel.Text = "Done!"
    }
} # End convertButton

Add some labels and the exit button and display the form.

Image2Base64

Image2Base64.ps1

____________________________________________________________________________

After encoding the two images into text files with Image2Base64, I insert them into the CapsLockMonitor script. The text block of the encoded image is inserted in a Here-String.

Decode and Set Icons
[string]$upIcon64=@"
AAABAAMAGBgQAAEABADoAQAANgAAABgYAAABAAgAyAYAAAYEAAAYGAAAAQAgAIgJAACWEQAAKAAAABgA
AAAwAAAAAQAEAAAAAAAgAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIAAAACAgACAAAAAgACAAICA
AACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</span>

... more lines ...

/2sTvf9IE73/NxO8/w0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A/gB/APgA
HwDwAA8A4AAHAOAAAwDAAAMAwAABAIAAAQCAAAEAgAABAIAAAQCAAAEAgAABAIAAAQCAAAEAwAADAMAA
AwDgAAcA8AAPAPgAHwD8AD8A/wD/AA==
"@

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($upIcon64)
$iconBmp=[System.Drawing.Bitmap][System.Drawing.Image]::FromStream($iconStream)
$iconHandle=$iconBmp.GetHicon()
$upIcon=[System.Drawing.Icon]::FromHandle($iconHandle)

Lastly, I change the icon calls in the original script to use the string instead of the path. Since it was not longer needed I removed the Get-ScriptDirectory function used to locate the icon files and it’s associated variables.

# Before
$notifyIcon.Icon = $global:callPath + "\up.ico"

# After
$notifyIcon.Icon = $upIcon

The end result is not too unwieldy as the files I encoded were small. A large file or many files can add thousands or even tens of thousands of lines, which can be a bit much. NotifyIcons are a good use of this as system tray icons are small.

You can encode any file type, it doesn’t have to be image to text files. The source could be a pfd, docx or even a another text file.

CapsLockMonitor

CapsLockMonitor.ps1

NotifyIcon Monitors CapsLock

Leave a comment

One of the most interesting WinForms classes is the NotifyIcon. With it your can create an icon in the System Tray area of the TaskBar. Combined with a Timer class object to countdown and raise events you can monitor and alert. The following PowerShell script monitors for a CapsLock change and alerts with a balloon from the system tray icon and a voice notification. At any time you can mouse over the icon for the current status. The icon also changes according to the CapsLock status with an up or down arrow for a visual reference.

First I setup the main form. It has no properties and is then hidden. I call a function to get the path from where the script is executing so I can find the icons.

function Get-ScriptDirectory { 
    $Invocation = (Get-Variable MyInvocation -Scope 1).Value
    $global:callPath = Split-Path $Invocation.MyCommand.Path
} # End Function Get-ScriptDirectory

# Main Form and Setup
$mainForm = New-Object System.Windows.Forms.form
$global:lastCapsLock = [System.Console]::CapsLock
$global:callPath = “”
Get-ScriptDirectory
# Hide Main Form
$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 CapsLock status change function. The interval is in milliseconds. The function call in add_Tick is required to be in brackets.

# Timer Object
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 2000
$timer.add_Tick({CapsLockCheck})
$timer.start()

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.Visible = $true
$menuItem1 = New-Object System.Windows.Forms.MenuItem -ArgumentList “Exit”
$contextMenu.MenuItems.Add($menuItem1)

# Add Exit MenuItem
$menuItem1.add_Click({
     $notifyIcon.Visible = $False
     $timer.stop()
     $mainForm.close()
})

I do an initial load of the values the NotifyIcon will use. I get the current CapsLock status and load up the correct text and icon.

# Inital Load
$capsLock = [System.Console]::CapsLock
if ($capsLock -eq $false) {
     $notifyIcon.Icon = $global:callPath + “\down.ico”
     $notifyIcon.Text = “CapsLock is Off”
}
if ($capsLock -eq $true) {
     $notifyIcon.Icon = $global:callPath + “\up.ico”
     $notifyIcon.Text = “CapsLock is On”
}

Next I setup the function checking for a CapsLock status change and popping up the alerts. I check the CapsLock status and compare it to a saved value from the previous check. If change I go to the appropriate routine and set the icon and text. I then set the balloons title, text, duration and icon. The icon can be one of a few standardized ToolTip icons like – info, warning, or error. I then set the text to be spoken and call the VoiceNotifcation function.

Function CapsLockCheck {
     $capsLock = [System.Console]::CapsLock
     if ($capsLock -eq $false) {
         if ($capsLock -ne $global:lastCapsLock) {
            $notifyIcon.Icon = $global:callPath + “\down.ico”
            $notifyIcon.Text = “CapsLock is Off”
            $notifyIcon.BalloonTipText = “CapsLock is Off”
            $notifyIcon.BalloonTipTitle = “CapsLock has Changed”
            $notifyIcon.BalloonTipIcon = “Info”
            $notifyIcon.ShowBalloonTip(10000)
            $spokenText = “Caps Lock is Off”
            $global:lastCapsLock = $capsLock
            VoiceNotification
         }
     }
     if ($capsLock -eq $true) {
         if ($capsLock -ne $global:lastCapsLock) {
            $notifyIcon.Icon = $global:callPath + “\up.ico”
            $notifyIcon.Text = “CapsLock is On”
            $notifyIcon.BalloonTipText = “CapsLock is On”
            $notifyIcon.BalloonTipTitle = “CapsLock has Changed”
            $notifyIcon.BalloonTipIcon = “Info”
            $notifyIcon.ShowBalloonTip(10000)
            $spokenText = “Caps Lock is On”
            $global:lastCapsLock = $capsLock
            VoiceNotification
         }
     }
} # End Function CapsLockCheck

The voice notification process just takes the passed text, and using the text to speech class, speaks it.

Function VoiceNotification {
     $Voice = New-Object System.Speech.Synthesis.SpeechSynthesizer
     $voice.rate = -1
     $voice.volume = 100
     [void] $voice.Speak($spokenText)
} # End Function VoiceNotification

Finally I start the NotifyIcon running using the WinForms Application class Run method.

[System.Windows.Forms.Application]::Run($mainForm)

Included in the zip file is also a command file to facilitate executing the script. It will start the script with a hidden PowerShell command window. There is also the two icon files. All the files should be in the same folder to run properly.

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

CapsLockMonitor

CapsLockMonitor.zip

FYI – In the next blog I encode and embed the icons directly into the script file.

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

Older Entries

%d bloggers like this: