Colorful ASCII-Art from Images

In the previous tip we showed you how you can take any image or photo and turn it into a black&white ASCII art. Today, we have a revised Convert-ImageToAsciiArt function for you: it takes an image and converts it to colored ASCII art!

The pixel brightness is turned into an appropriate ASCII character, and the pixel color is applied to that character. The ASCII art is written to a HTML file since HTML is the easiest file format for colored text.

function Convert-ImageToAsciiArt
{
  param(
    [Parameter(Mandatory)][String]
    $ImagePath,
    
    [Parameter(Mandatory)][String]
    $OutputHtmlPath,
    
    [ValidateRange(20,20000)]
    [int]$MaxWidth=80
  )

  ,
    
  # character height:width ratio
  [float]$ratio = 1.5
  
  # load drawing functionality
  Add-Type -AssemblyName System.Drawing
  
  # characters from dark to light
  $characters = '$#[email protected]*+;:-,. '.ToCharArray() 
  $c = $characters.count
  
  # load image and get image size
  $image = [Drawing.Image]::FromFile($ImagePath)
  [int]$maxheight = $image.Height / ($image.Width / $maxwidth) / $ratio
  
  # paint image on a bitmap with the desired size
  $bitmap = new-object Drawing.Bitmap($image,$maxwidth,$maxheight)
  
  
  # use a string builder to store the characters
  [System.Text.StringBuilder]$sb = ""
  
  
  # take each pixel line...
  for ([int]$y=0; $y -lt $bitmap.Height; $y++) {
    # take each pixel column...
    $null = $sb.Append("")
    for ([int]$x=0; $x -lt $bitmap.Width; $x++) {
      # examine pixel
      $color = $bitmap.GetPixel($x,$y)
      $brightness = $color.GetBrightness()
      # choose the character that best matches the
      # pixel brightness
      [int]$offset = [Math]::Floor($brightness*$c)
      $ch = $characters[$offset]
      if (-not $ch) { $ch = $characters[-1] }
      $col = "#{0:x2}{1:x2}{2:x2}" -f $color.r, $color.g, $color.b
      if ($ch -eq ' ') { $ch = " "}
      $null = $sb.Append( "$ch")
    }
    # add a new line
    $null = $sb.AppendLine("
") } # close html document $null = $sb.AppendLine("") # clean up and return string $image.Dispose() Set-Content -Path $OutputHtmlPath -Value $sb.ToString() -Encoding UTF8 }

And this is how you can turn an image into a beautiful set of ASCII art, display it in your browser, and even print it to your color printer:

$ImagePath = "C:someInputPicture.jpg"
$OutPath = "$homedesktopASCIIArt.htm"

Convert-ImageToAsciiArt -ImagePath $ImagePath -OutputHtml $OutPath -MaxWidth 150 
Invoke-Item -Path $OutPath

By adjusting -MaxWidth you can control the details. If you increase the width, you should also play with the font size and decrease the characters. For smaller characters, you might adjust this line:

[System.Text.StringBuilder]$sb = ""

Change it to this line, for example:

   [System.Text.StringBuilder]$sb = ""


psconf.eu – PowerShell Conference EU 2019 – June 4-7, Hannover Germany – visit www.psconf.eu There aren’t too many trainings around for experienced PowerShell scripters where you really still learn something new. But there’s one place you don’t want to miss: PowerShell Conference EU – with 40 renown international speakers including PowerShell team members and MVPs, plus 350 professional and creative PowerShell scripters. Registration is open at www.psconf.eu, and the full 3-track 4-days agenda becomes available soon. Once a year it’s just a smart move to come together, update know-how, learn about security and mitigations, and bring home fresh ideas and authoritative guidance. We’d sure love to see and hear from you!

Twitter This Tip! ReTweet this Tip!

Testing Raw Data with Checksums – A Real-World Example

With PowerShell entering the IoT world, sometimes it is necessary to deal with binary sensor data and ancient checksum models to verify data integrity.

Here is a real-world example how PowerShell can take sensor data and validate it using a checksum. The example described here is a specific use case, however the techniques employed may well be useful for similar scenarios.

In this example, PowerShell receives a series of hex data ($data). The checksum is the last byte (3A). It is defined as the sum of all bytes in the data package. From this sum, the least significant byte represents the checksum, however only when its bits are inverted. Sounds strange but makes sense. This way, the checksum is always just one byte.

Now here is how PowerShell can handle these requirements, compare the checksum with the calculated checksum, and test the data integrity:

$data = '00030028401D2E8D4022C0EE4022C0E64022C0E6418B4ACD419FE7B641A05F0E41A060D041A061C23F0A7CDA3A'

# checksum is last byte
$checksum = [Convert]::ToByte($data.Substring($data.Length-2,2), 16)

# remove checksum from data
$data = $data.Substring(0, $data.Length -2)

# sum up all bytes
$sum = $data -split '(?<=G.{2})(?=.)' | 
  Foreach-Object {$c = 0}{$c+=[Convert]::ToByte($_,16)}{ $c }
  
# get the least significant byte
$lsb = ([Net.IPAddress]$sum).GetAddressBytes()[0]

# invert bits
$checksumReal = $lsb -bxor 0xFF

# compare
if ($checksum -ne $checksumReal)
{
  throw "Checksum does not match"
}
else
{
  Write-Warning "Checksum ok"
}

Your learning points:

  • Use [Convert]::ToByte($number, 16) to convert hex strings to numbers
  • With regular expressions, you can easily split a character stream (i.e. hex pairs) into a list of pairs so you can calculate the sum
  • By converting a number to an IPAddress, you get easy access to the most and least significant bytes (LSB, MSB)
  • Use -bxor to invert bits

psconf.eu – PowerShell Conference EU 2019 – June 4-7, Hannover Germany – visit www.psconf.eu There aren’t too many trainings around for experienced PowerShell scripters where you really still learn something new. But there’s one place you don’t want to miss: PowerShell Conference EU – with 40 renown international speakers including PowerShell team members and MVPs, plus 350 professional and creative PowerShell scripters. Registration is open at www.psconf.eu, and the full 3-track 4-days agenda becomes available soon. Once a year it’s just a smart move to come together, update know-how, learn about security and mitigations, and bring home fresh ideas and authoritative guidance. We’d sure love to see and hear from you!

Twitter This Tip! ReTweet this Tip!

Inverting Bits

Occasionally it is necessary to invert the bits for a number. Most often, this is part of custom algorithms or checksum calculations. It raises the general question: what’s the easiest way to do this?

“Bit-Flipping” can be done with the -bnot operator like this:

$number = 76
[Convert]::ToString($number,2)

$newnumber = -bnot $number
[Convert]::ToString($newnumber,2)

The result shows one caveat, though:

 
1001100
11111111111111111111111110110011 
 

The operator always acts on signed 64-bit numbers. A better approach may be to use the -bxor operator and provide your own bit mask that you want to flip. For a byte, the bit mask would be 0xFF, and for an Int32, it would be 0xFFFFFFFF. Here is an example to flip the bits for a byte. We padded the string representation to 8 characters to make leading zeroes visible:

$number = 76
[Convert]::ToString([byte]$number,2).PadLeft(8, '0')

$newnumber = $number -bxor 0xFF
[Convert]::ToString($newnumber,2).PadLeft(8, '0')

The result matches:

 
01001100
10110011 
 

Your learning points:

  • PowerShell comes with a rich set of binary operators that all start with -b…
  • To invert (flip) bits, you can use -bnot. To flip only some bits, use -bxor with your bit mask

psconf.eu – PowerShell Conference EU 2019 – June 4-7, Hannover Germany – visit www.psconf.eu There aren’t too many trainings around for experienced PowerShell scripters where you really still learn something new. But there’s one place you don’t want to miss: PowerShell Conference EU – with 40 renown international speakers including PowerShell team members and MVPs, plus 350 professional and creative PowerShell scripters. Registration is open at www.psconf.eu, and the full 3-track 4-days agenda becomes available soon. Once a year it’s just a smart move to come together, update know-how, learn about security and mitigations, and bring home fresh ideas and authoritative guidance. We’d sure love to see and hear from you!

Twitter This Tip! ReTweet this Tip!

Calculating Most and Least Significant Byte

Numbers are stored internally as bytes. An Int32 value, for example, uses four bytes. Sometimes it is required to split up the number into its byte parts, for example, to calculate checksums with the least significant byte.

We have created a quick walk-through that you can also use as a basic number handling tutorial. It illustrates how numbers correspond to bits, how to split numbers to bytes, how to calculate least significant byte (LSB) and most significant byte (MSB), and more:

function Show-Header([Parameter(ValueFromRemainingArguments)][string]$Text)
{
  $Width=80
  $padLeft = [int]($width / 2) + ($text.Length / 2)
  ''
  $text.PadLeft($padLeft, "=").PadRight($Width, "=")
  ''
}


Show-Header Starting with this number:
$number = 26443007
$number

Show-Header Display the bits for this number:
$bits = [Convert]::ToString($number,2)
$bits 

Show-Header Add missing leading zeroes:
# pad the string to its full bit range (32 bits)
$bitsAligned = $bits.PadLeft(32, '0') 
$bitsAligned

Show-Header Display the four byte groups
$bitsAligned -split '(?<=G.{8})(?=.)' -join '-'

Show-Header Get the bytes by conversion to IPAddress object:
$bytes = ([Net.IPAddress]$number).GetAddressBytes()
$bytes

Show-Header Display the bits for the IPAddress bytes:
$bitbytes = $bytes | ForEach-Object { [Convert]::ToString($_, 2).PadLeft(8,'0')}
$bitbytes -join '-'

Show-Header Show the Least Significant Byte LSB:
$bytes[0]

Show-Header Show LSB by turning the 8 bits to the right into a number to verify:
$bits = [Convert]::ToString($number, 2)
# take least significant bits
[Convert]::toByte($bits.Substring($bits.Length-8),2)

Show-Header Show the Most Significant Byte MSB:
$bytes[3]

As you can see, there are many ways to get there. One especially clever way is converting a number to an IPAddress. IPAddress objects have a handy GetAddressBytes() method that splits the number easily into its bytes for you.

The result looks like this:

 
===========================Starting with this number:===========================

26443007

=======================Display the bits for this number:========================

1100100110111110011111111

===========================Add missing leading Zeroes:==========================

00000001100100110111110011111111

==========================Display the four byte groups==========================

00000001-10010011-01111100-11111111

================Get the bytes by conversion to IPAddress object:================

255
124
147
1

===================Display the bits for the IPAddress bytes:====================

11111111-01111100-10010011-00000001

======================Show the Least Significant Byte LSB:======================

255

======Show LSB by turning the 8 bits to the right into a number to verify:======

255

=======================Show the Most Significant Byte MSB:======================

1
 

psconf.eu – PowerShell Conference EU 2019 – June 4-7, Hannover Germany – visit www.psconf.eu There aren’t too many trainings around for experienced PowerShell scripters where you really still learn something new. But there’s one place you don’t want to miss: PowerShell Conference EU – with 40 renown international speakers including PowerShell team members and MVPs, plus 350 professional and creative PowerShell scripters. Registration is open at www.psconf.eu, and the full 3-track 4-days agenda becomes available soon. Once a year it’s just a smart move to come together, update know-how, learn about security and mitigations, and bring home fresh ideas and authoritative guidance. We’d sure love to see and hear from you!

Twitter This Tip! ReTweet this Tip!

Accepting Parameters without Quotes

In the previous tip, we introduced a function that generates nicely centered headers and accepted a single string. Here is the function and its results:

function Show-Header($Text)
{
  $Width=80
  $padLeft = [int]($width / 2) + ($text.Length / 2)
  $text.PadLeft($padLeft, "=").PadRight($Width, "=")
}
 
PS> Show-Header Starting
====================================Starting====================================

PS> Show-Header "Processing Input Values"
=============================Processing Input Values============================
 

As you can see, the function works beautifully, but the user needs to quote the string once it contains spaces or other special characters. Wouldn’t it be nice if the function accepted strings without quotes as well, and treated all input as one argument?

That’s easily possible, provided:

  • the parameter is declared a clear data type, i.e. [string], so PowerShell knows what to do with your arguments
  • you are just using a single parameter, so PowerShell knows that all input needs to go into that parameter

Here is the revised function:

function Show-Header([Parameter(ValueFromRemainingArguments)][string]$Text)
{
  $Width=80
  $padLeft = [int]($width / 2) + ($text.Length / 2)
  $text.PadLeft($padLeft, "=").PadRight($Width, "=")
}

The magic is done by the ValueFromRemainingArguments attribute. Now, the user can simply type text and won’t need to use quotes:

 
PS> Show-Header Starting
====================================Starting====================================

PS> Show-Header Processing Input Values
=============================Processing Input Values============================ 
 

However, there is one caveat: any special characters such as parenthesis and quotes are interpreted and can still interfere. In these cases, you have to quote the string like before.

Your learning points:

  • use the ValueFromRemainingArguments attribute to allow PowerShell to assign all unbound (extra) arguments to that parameter
  • use distinct data types for parameters so PowerShell knows how to convert ambiguous data. Without a [string] data type, for example, PowerShell would have created a string array from your input values when a string contained spaces

psconf.eu – PowerShell Conference EU 2019 – June 4-7, Hannover Germany – visit www.psconf.eu There aren’t too many trainings around for experienced PowerShell scripters where you really still learn something new. But there’s one place you don’t want to miss: PowerShell Conference EU – with 40 renown international speakers including PowerShell team members and MVPs, plus 350 professional and creative PowerShell scripters. Registration is open at www.psconf.eu, and the full 3-track 4-days agenda becomes available soon. Once a year it’s just a smart move to come together, update know-how, learn about security and mitigations, and bring home fresh ideas and authoritative guidance. We’d sure love to see and hear from you!

Twitter This Tip! ReTweet this Tip!