Click here to Skip to main content
15,885,141 members
Articles / Programming Languages / Visual Basic

LINQ to ASCII Art

Rate me:
Please Sign up or sign in to vote.
4.81/5 (33 votes)
2 Jul 2013CPOL3 min read 105.6K   853   60   13
Convert image to ASCII art using LINQ technology.

Introduction

I saw many articles explaining how to generate ASCII Art from image, but I didn't see any article that achieves the same stuff in LINQy style. With that, I 'll explain how to use LINQ technology to compose a couple of functions to achieve our goal.

Background

ASCII Art in a nutshell refers to text-based visual art. So we can create cool arts using ASCII codes from simple things such ^_^ :) to little advance things such as:

For more information, I advice you to check out this link.

As we know, Language Integrated Query (LINQ) is a powerful technology for composing, querying, transformation different data source, all of that inside your code.

I think of ASCII Art as composing and transforming a couple of functions to generate the beauty stuff.

Using the Code

The LINQ to ASCII Art is very straight forward. In this section, I'll explain every function in detail.

Get Pixels

This main purpose of this function is to get the actual pixels for a specified image, so it accepts a bitmap, iterates through all its pixels horizontally and vertically using the width and the height of that image, and uses the GetPixel function to get a pixel with specific coordinates. Then, the return value is the actual pixels as IEnumerable(Of Color) to make it easy to play with those pixels using LINQ later on. Its code looks like this:

VB.NET
Public Function GetPixels(bmp As Bitmap) As IEnumerable(Of Color)
        Return From x In Enumerable.Range(0, bmp.Width - 1)
               From y In Enumerable.Range(0, bmp.Height - 1)
               Select bmp.GetPixel(y, x)
End Function 

Gray Scale Pixels

This main purpose of this function is to convert the actual pixels to gray scale, so it accepts the bunch of pixels, iterates them and applies the following formula:

Gray Scale = 0.3 * Red + 0.59 * Green + 0.11 * Blue 

For more information about gray scale, please check out this link.

Then the return value is the grayed scale pixels as IEnumerable(Of Color) to make it easy to play with those pixels using LINQ later on. Its code looks like this:

VB.NET
Public Function GrayScalePixles_
(pixels As IEnumerable(Of Color)) As IEnumerable(Of Color)
        Return From p In pixels
               Let g = 0.3 * p.R + 0.59 * p.G + 0.11 * p.B
               Select p = Color.FromArgb(g, g, g)
End Function

Get Characters

This main purpose of this function is to get the character corresponding to the value of gray scale pixel. By the way, I want to thank someone whose name I don't remember:) for his experiment to get the underneath characters in the code, but you can use any suitable set. So the idea behind that is check the brightness of the grayed color which is value of anyone of the RGB for that pixel. For example, if the value is 230 means the pixel goes to white, if it's less than 50 means the pixel goes to black, so the idea is simple from lightness to darkness. Then return value is the character (ASCIIs) they should print out, they 're IEnumerable(Of Char) to make it easy to play with those characters using LINQ later on. It's code looks like this:

VB.NET
Public Function GetChars_
(pixels As IEnumerable(Of Color)) As IEnumerable(Of Char)
        Return From p In pixels
        Select If(p.R >= 230, " "c, If(p.R >= 200, _
        "."c, If(p.R >= 180, "*"c, If(p.R >= 160, _
        ":"c, If(p.R >= 130, "o"c, If(p.R >= 100, _
        "&"c, If(p.R >= 70, "8"c, _
        If(p.R >= 50, "#"c, "@"c))))))))

End Function

The last thing is composing them using LINQ as follows:

VB.NET
Dim img As Image = Image.FromFile("Hisham.jpg")
Dim bmp As New Bitmap(img, 150, 150)
Dim parts = GetChars(GrayScalePixles(GetPixels(bmp))).Chunk(bmp.Width _
  - 1).Select(Function(part) String.Join("", part))
Dim data = String.Join(vbCrLf, parts)
File.WriteAllText("test.txt", data)

Last I want to show you that we can go further a little bit and compose them all in a single giant LINQ query as follows:

VB.NET
Dim data = String.Join(vbCrLf, (From x In Enumerable.Range(0, bmp.Width - 1)
     From y In Enumerable.Range(0, bmp.Height - 1)
     Let p = bmp.GetPixel(y, x)
     Let g = 0.3 * p.R + 0.59 * p.G + 0.11 * p.B
     Let v = Color.FromArgb(g, g, g)
     Select If(v.R >= 230, " ", If(v.R >= 200, ".", _
       If(v.R >= 180, "*", If(v.R >= 160, ":", _
       If(v.R >= 130, "o", If(v.R >= 100, "&", _
       If(v.R >= 70, "8", If(v.R >= 50, "#", _
       "@"))))))))).Chunk(bmp.Width - 1).Select(Function(part) _
               String.Join("", part)))

Finally, I want to mention that some of you guys said the last query is expensive and takes much time. Of course I agree with you because there is a lot of processing for many many pixels, anyway we can improve the performance using AsParallel() method, also we can use new async modifier to eliminate the thread blocking.

Points of Interest

Amazingly, we have seen how we can generate ASCII art in LINQy style, which is something beautiful :) and open mind for many further things such image processing. We saw how we GrayScale an image with simple LINQ query, and we can do more than that, inverting, flipping, ... etc.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



Comments and Discussions

 
GeneralMy vote of 5 Pin
Mohamed BinOthman27-Jul-13 14:10
Mohamed BinOthman27-Jul-13 14:10 
GeneralMy vote of 5 Pin
Nabil Mosali27-Jul-13 3:37
Nabil Mosali27-Jul-13 3:37 
GeneralMy vote of 5 Pin
Ștefan-Mihai MOGA13-Jul-13 20:50
professionalȘtefan-Mihai MOGA13-Jul-13 20:50 
GeneralMy vote of 5 Pin
Renju Vinod9-Jul-13 21:20
professionalRenju Vinod9-Jul-13 21:20 
GeneralMy vote of 5 Pin
thanh_bkhn4-Jul-13 23:01
professionalthanh_bkhn4-Jul-13 23:01 
GeneralMy vote of 4 Pin
sthotakura1-Jul-13 21:40
sthotakura1-Jul-13 21:40 
GeneralMy vote of 5 Pin
Ibrahim Hebish1-Jul-13 21:37
Ibrahim Hebish1-Jul-13 21:37 
QuestionGetChar Pin
James Curran1-Jul-13 5:45
James Curran1-Jul-13 5:45 
AnswerRe: GetChar Pin
Hisham Abdullah Bin Ateya1-Jul-13 23:55
Hisham Abdullah Bin Ateya1-Jul-13 23:55 
QuestionGreat..! Pin
Archimedes241-Jul-13 4:09
professionalArchimedes241-Jul-13 4:09 
GeneralMy vote of 5 Pin
Carlos19071-Jul-13 0:45
professionalCarlos19071-Jul-13 0:45 
GeneralMy vote of 5 Pin
Amit Developer30-Jun-13 20:27
Amit Developer30-Jun-13 20:27 
GeneralMy vote of 5 Pin
Prasad Khandekar30-Jun-13 19:53
professionalPrasad Khandekar30-Jun-13 19:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.