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

Advertisements