Linq to ASCII Art


Last night I was searching for an audio version of Painters and Hackers by Paul Graham.  Pretty soon I had completely forgotten about the book and found myself reading the Wikipedia article about Hackers.  Isn’t Internet search great?

Of all of the things in the article, the one thing that captured my attention was the ASCII picture of Adrian Lamo.  I immediately thought, “How cool is that!?”  So instead of popping off my current stack frame and returning to searching for Painters and Hackers, I began thinking about how to create such a picture.  I certainly did not want to create it by hand.  I didn’t even want to pick which characters or colors would be printed to the screen.  So accordingly, I began thinking of writing a program to do it for me.

I grabbed the latest bits and began writing code.  A few hours later, I had a working program with a few bells and whistles too.

ASCII Art

Here is an example of converting Gandalf from pixels to ASCII:

How to Convert and Image to ASCII Art

The process works like this:

Determine how big of an area will be used for displaying the ASCII art.  By default, the width of the image will be the width of the console’s buffer and the height will be computed so as the preserve the ratio of width to height from the original image.

Now divide the original image into as many rectangles as will be used to display the ASCII.  For each rectangle in the original picture determine its grayscale value by averaging the grayscale values of each pixel.  The grayscale value of a pixel is:

GrayScale = .3 * Red + .59 * Green + .11 * Blue

We then increase the contrast of each region by some amount (d is the luminosity of a region, contrast is any double but usually around 1.5):

public static double Contrast(this double d, double contrast)
{
  return (((d – .5) * contrast) + .5).Bound(0, 1);
}

Finally, for each region in the ASCII art we choose from an array of possible ASCII figures (sorted) by the grayscale value for that region.  The ASCII figure with the closest luminosity is picked.

The only point that I haven’t covered is how the array of ASCII figures was generated.  I could have done this by hand if I wanted to, but I didn’t want to.  Instead, I generate the grayscale figures each time.  Each figure consists of a character and a console color either ConsoleColor.DarkGray, ConsoleColor.Gray, or ConsoleColor.White.  The characters are one of the alphanumeric characters or the special symbols (things like %, $, @, …).  The program generates all of the combinations and then measures their grayscale value by drawing them to a hidden bitmap and then computing their average grayscale value.  The figures are sorted by this value and a final grayscale is built.

A Few Snippets

If you take a look at the source, you will notice that it is written rather different than most C# code.  A lot of the code resides in extension methods and there are a number of lambdas and queries.  In fact, loops are kind of rare in the code.  For example, here is a function that returns all of the pixels in a rectangular region of a bitmap.

public static IEnumerable<Color> GetPixels(this Bitmap image, Rectangle rect)
{
  return from y in rect.Top.To(rect.Bottom)
         from x in rect.Left.To(rect.Right)
         select image.GetPixel(x, y);
}

The To function is a helpful little function that I wrote.

public static IEnumerable<int> To(this int start, int end)
{
  var diff = end – start > 0 ? 1 : -1;
  for (var current = start; current != end; current += diff)
    yield return current;
}

Of course, I also could have written Iterate and then written To in terms of that.

public static IEnumerable<T> Iterate<T>(T initialValue, Func<T, bool> predicate, Func<T, T> next)
{
  for (var current = initialValue; predicate(current); current = next(current))
    yield return current;
}

public static IEnumerable<int> To(this int start, int end)
{
  var diff = end – start > 0 ? 1 : -1;
  return Iterate(start, x => x != end, x => x + diff);
}

Another interesting part is setting up the display and then restoring the original display properties of the console.

using (SetupConsole())

  …

Where SetupConsole is defined as:

static ActionDisposable SetupConsole()
{
  var originalBackgroundColor = Console.BackgroundColor;
  var originalForegroundColor = Console.ForegroundColor;
  return new ActionDisposable(() =>
    {
      Console.BackgroundColor = originalBackgroundColor;
      Console.ForegroundColor = originalForegroundColor;
    });
}

Notice that the original state of the console is captured only in the SetupConsole method and does not clutter the rest of the code.  The closure that is created contains all of the necessary information to restore the console to its original state.  This way we hide data even from private members of the same class.  If they don’t need to know, then they don’t need to know.  Instead only a means for restoring the original state is provided.

I’m sure that the program can be improved a lot.  So if you have any comments, suggestions, or bugs then just post a comment.  Enjoy!

Update:

I took the comments as well as some of my own ideas and improved the ASCII art generator.  I also modified the How to Convert an Image to ASCII Art section to reflect some changes.  Thank you to everyone that contributed.

Download the Source

File iconAsciiPictureSource.zip

File iconAsciiPictureSourcev1.1.zip

Comments (44)

  1. michael says:

    the link does seem to work. Is there any other place i could download the sample and analyze the interesting code?

  2. wesdyer says:

    Try it now (I was fiddling with it a few minutes ago), if it still doesn’t work for you then I’ll find a better place to put it tomorrow.

  3. wesdyer says:

    And make sure you refresh the page since it was probably cached (the link changed).

  4. Tom Kirby-Green says:

    Excellent :-) this made me smile when it popped up in my RSS Reader. Clearly Linq To ASCII Art should be baked into the BCL 😉

    Joking aside, I think articles like this will go a long way towards seeing off the idea that LINQ is just "embedded SQL", they’re also a great way of leading reluctant OO developers to the FP watering hole (so to speak). Someone should do a LINQ series for Microsoft’s Coding For Fun community microsite. Lots of obvious article ideas suggest themselves "LINQ To MP3 Metadata – Query Your Music Collection" etc.

  5. Artur Stepien says:

    Check the aalib project from back when and _especially_ their wonderful demo called bb.

    AAlib description: AAlib is an portable ascii art GFX library.

    Link: http://aa-project.sourceforge.net/aalib/

  6. Tom Kirby-Green says:

    Actually it struck me last night that there are parallels between LINQ as an enabler and Yahoo Pipes. LINQ has ‘Pipe-like qualities in that it enables the filtering, composition, unification and transformation of disperate data using a wonderfully small and coherent set of primitives. One could imagine a "Visual LINQ" design surface that was very ‘Pipe like in its presentation.

  7. wesdyer says:

    Tom:

    I love the idea of Linq to Music.  That is a fantastic idea.  I am planning on developing a query provider in a series of blog posts but I don’t want to talk the details just yet.

    It does sound like a visual designer could be made for Linq.  Hm…

    Artur:

    Thanks for the link.  Then definitely have a high quality library.  I remember one of the coolest things that I’ve seen was a demo using just the console window back in like 1994 or so.  They had things like voxel graphics and three dimensional dimensional graphics.  It was awesome.

  8. Samuel Jack says:

    I like your SetupConsole idea. But couldn’t you take it a step further and wrap it in an IDisposable. Something like

    public class ActionDisposable : IDisposable

    {

     Action _action

     public ActionDisposable(Action action)

     {

       _action = action;

     }

     public void Dispose()

     {

       _action();

     }

    }

    Then your code becomes:

    using (SetupConsole())

    {

    } // restore now called implicitly

    Where SetupConsole is defined as:

    static IDisposable SetupConsole()

    {

     var originalBackgroundColor = Console.BackgroundColor;

     var originalForegroundColor = Console.ForegroundColor;

     return new ActionDisposable(() =>

       {

         Console.BackgroundColor = originalBackgroundColor;

         Console.ForegroundColor = originalForegroundColor;

       });

    }

  9. wesdyer says:

    I like it.  RAII to the rescue!

  10. I made an image to ascii converter in C and made it into a site at http://www.asciiconvert.com

    Cheers!

  11. Sean says:

    Very interesting, but I have one [probably stupid] question.  What’s with the "this" keyword for the image parameter in the GetPixels method?

    GetPixels(this Bitmap image, Rectangle rect)

  12. Patrick Lioi says:

    Sean,

    This is one of the new language features in the upcoming C# 3.  Notice that the Bitmap class does not already have a method named GetPixels(Rectangle).  When you make a static method whose first parameter is preceded by ‘this’, you get to pretend that it is a method on that first parameter’s class.  The first parameter can even just be an interface, so you can define a helper method against IEnumerable<T> and then every single IEnumerable<T> type that you have can be used as if that new method were a member.

  13. Peter Ritchie says:

    I remember doing ASCII art 20 years ago…  I progressed to animated ASCII art; now that was cool.

  14. wesdyer says:

    Christian:

    Nice site.  I like the automatically generated gallery and you even have some of your pictures on wikipedia.

    Peter:

    I totally agree, ascii animation is awesome.

  15. Patrick Lioi says:

    In your code, I notice the new ?? operator.  I think it’s pretty clear what that one is doing, but I hadn’t heard of any new operators before now.  It’s hard to google for these things, but I found that there is also a !! operator that I don’t quite get (how is it different from ||, for instance?).  Are there other new operators?

  16. Jafar Husain says:

    Totally minor point but I notice that your To function is basically the same as Enumerable.Range(int start, finish).  Your way could be considered more readable though.  Nicely mimicks ranges in Ruby.

  17. derekslager says:

    Did you intentionally move "Painters" to the front of the title? A nod to the creative slant of your post, perhaps? Or perhaps I’m reading too far.

    I’m actually in the middle of Hackers & Painters right now. So far, it’s terrific. If you can’t find it in audio form, there’s at least one copy available at the library (and one more when I return mine).

    http://catalog.kcls.org/search?/Yhackers%20painters&searchscope=1&SORT=D/Yhackers%20painters&searchscope=1&SORT=D&SUBKEY=hackers%20painters/1%2C7%2C7%2CB/frameset&FF=Yhackers%20painters&searchscope=1&SORT=D&1%2C1%2C

  18. wesdyer says:

    Patrick:

    ?? is the only "new" operator that we have introduced since C# 1.1.  It actually first appears in C# 2.0 and is called the null coalescing operator.  If a programmer writes a ?? b then this is equivalent to a != null ? a : b.

    We don’t have a !! operator in C#.  Although a programmer could write !!a which means not (not a)) which is equilavent to writing a.  In C and C++ programmers will write !!a to convert some value to either zero or one because the not operator in those languages does not require a boolean operand.  In Haskell the prelude includes a !! operator that indexes into a list.  So [‘a’,’b’,’c’] !! 1 is equal to ‘b’.

    Jafar:

    Yes, it is very close to the Enumerable.Range function.  Except that To is an extension method that takes the start as the implicit receiver.  I like writing

     0.To(5)

    instead of

     Enumerable.Range(0, 5)

    Derek:

    Thanks for the heads up about the book.  I’ll probably check it out next week to read while I fly to a career fair.

  19. wesdyer says:

    Thanks for the link.  That article is hilarious.  I love it!! (notice the use of the just because operator)

  20. Sadek Drobi says:

    looks simpler as a code, good idea to try to clearify and delete the confusion that linked Linq to sql generation, i posted on my blog trieng to show another use of epression trees.

    u can check it out on

    Implementing Domain Friendly, Predicates-like Specifications with C# 3.0 Expression trees

    http://sadekdrobi.com/?p=30

    will be interested by your opinion.

  21. Welcome to the twenty-second Community Convergence, the March CTP issue. I’m Charlie Calvert, the C#

  22. RSS It All says:

    Welcome to the twenty-second Community Convergence, the March CTP issue. I&#39;m Charlie Calvert, the

  23. Duncan says:

    Hey, this is in regards to the general application of closures and debuggability.

    As you have demonstrated with your handling of console state closures can be a very succinct way of encapsulating state that it may be reinstated later. It seems to me though that in doing so you are effectively transferring ownership of said state to some anonymous type. If I stopped the program, would this state be readily available? I think not.

    That is not to say that it is not a valid technique, just a heads up that overuse could make you state more opaque, especially to those less versed in functional techniques.

  24. wesdyer says:

    Duncan:

    That is a great point to bring up.  You can in fact inspect the state just by expanding the delegate in ActionDisposable.  Since every delegate that is closed over some variables will have a Target field which will in turn contain all of the hoisted variables (transitively).  Perhaps we should consider adding a debugger feature to automatically show the captured locals of a delegate when inspecting the delegate.  Very good point indeed…

  25. Harry@Ardimedia.Com says:

    I had some compile issues (using March CTP):

    in EnumerableExtensions.cs:

    The type or namespace name ‘Func’ could not be found (are you missing a using directive or an assembly reference?)

    –> Adding "using System.Linq;" does solve it.

    ActionDisposable.cs:

    Using the generic type ‘System.Action<T>’ requires ‘1’ type arguments

    –> Not sure how to fix this? Any comments on this?

    Thankx, Harry

  26. wesdyer says:

    Harry:

    The CTPs are actually about 2-3 months behind what we have actually done.  In the current code, all of the Funcs are in System not System.Linq and System also includes Action delegates (not just the one that takes one type parameter).  To fix it for the March CTP simply add the "using System.Linq" and then add the following delegate:

    delegate void Action();

  27. Duncan says:

    "Perhaps we should consider adding a debugger feature to automatically show the captured locals of a delegate when inspecting the delegate" – I am sold already. Whatever can be done to surface state during debugging is valuble work indeed.

  28. De entre la avalancha de vídeos, documentos y enlaces que con los que nos abrumó Charlie Calvert simultáneamente

  29. Mark Probst says:

    One feature I’ve always wanted to see in ASCII art programs is sub-character matching.  What I mean by this is to take the actual shape of the character into account.  The characters "@" and "#" for example are close to uniformly white, whereas "I" and "1" are white in the vertical middle and black left and right, and "_" and "," are white at the bottom and the rest is black.

    It’s easy to implement this kind of matching by cutting the character bitmaps up into, say, a 5 by 5 matrix, and computing the brightness for each piece.  They you do the same thing on the part of the image you want to match and find the character that fits best in a least squares sense.

    That’s actually how photomosaic programs work.  Incidentally, <a href="http://www.complang.tuwien.ac.at/schani/metapixel/">I wrote one</a> :-)

  30. wesdyer says:

    Mark:

    Yes, that is a great idea and will implement it when I find the time.  If anyone gets to it first please post the source.

  31. Surya says:

    Hi there,

    I was just wondering if anyone can help me to extract the grayscale values of the different characters from the program, like printing the character and its grayscale value to the console or similar. I am trying to create a simple ASCII converter in Common Lisp and I don’t really understand C#.

    Thank you in advance.

    Surya

  32. wesdyer says:

    Have you tried displaying the characters to a hidden buffer and then sampling from that?

  33. Surya says:

    You mean in Common Lisp? I was thinking to store them in a static array or hash map and use it to retrieve the value whenever a grayscale is given as argument. I thought that would be easier to start with. And I tried to compile the C# source code but I get a lot of errors.

    Sorry for the trouble.

    Surya

  34. Judah says:

    posted my playing experience of playing with your little library here and VS Orcas March:

    http://judahgabriel.blogspot.com/2007/03/fun-with-c-3-programming.html

  35. wesdyer says:

    Surya:

    That works.  What kind of errors are you getting?  It works fine on our side (the code is actually a QA test now).

    Judah:

    Very cool.  Nice family photos.  Glad you are having fun with it.

  36. Surya says:

    Hi wesdyer,

    I found another way to get the grayscale values of the characters. I think I used the wrong program to compile. Does it need the newest VS Orcas? Sorry for all the trouble, I’m new to the C# concept.

    Thanks again for all the help.

    Surya

  37. wesdyer says:

    It is possible that you are using the wrong program to compile it.  Use Visual Studio Orcas to compile it.  Specifically try using the March CTP or Beta 1 when it comes out.  The May 2006 CTP, may (no pun intended) not work.

  38. Craig says:

    Hey, i’m sorry, im a total noob, but your program looks amazing. When you download the code what do you do with it to get it working?

    Craig

  39. metofabrik says:

    SPIRAL CLASSIFIER MANUFACTURERS INDIA, SCREW CLASSIFIER MANUFACTURERS INDIA, DE WATERING SCREEN MANUFACTURERS INDIA, VIBRATING PAN FEEDER MANUFACTURERS INDIA, VIBRO PAN FEEDER MANUFACTURERS INDIA, VIBRATING GRIZZLY FEEDER MANUFACTURERS INDIA, VIBRO GRIZZLY FEEDER MANUFACTURERS INDIA, DRUM SCRUBBER MANUFACTURERS INDIA, IRON ORE BENEFICIATION PLANT INDIA, MINERAL PROCESSING PLANT INDIA, IRON ORE PROCESSING PLANT INDIA, SAND WASHING PLANT INDIA, SILICA WASHING PLANT INDIA

    <a href="http:/www.metofabrik.com/">DE WATERING SCREEN</a>

  40. metofabrik says:

    SPIRAL CLASSIFIER MANUFACTURERS INDIA, SCREW CLASSIFIER MANUFACTURERS INDIA, DE WATERING SCREEN MANUFACTURERS INDIA, VIBRATING PAN FEEDER MANUFACTURERS INDIA, VIBRO PAN FEEDER MANUFACTURERS INDIA, VIBRATING GRIZZLY FEEDER MANUFACTURERS INDIA, VIBRO GRIZZLY FEEDER MANUFACTURERS INDIA, DRUM SCRUBBER MANUFACTURERS INDIA, IRON ORE BENEFICIATION PLANT INDIA, MINERAL PROCESSING PLANT INDIA, IRON ORE PROCESSING PLANT INDIA, SAND WASHING PLANT INDIA, SILICA WASHING PLANT INDIA

    <a href="http:/www.metofabrik.com/">DE WATERING SCREEN</a>

  41. SPIRAL CLASSIFIER MANUFACTURERS INDIA, SCREW CLASSIFIER MANUFACTURERS INDIA, DE WATERING SCREEN MANUFACTURERS INDIA, VIBRATING PAN FEEDER MANUFACTURERS INDIA, VIBRO PAN FEEDER MANUFACTURERS INDIA, VIBRATING GRIZZLY FEEDER MANUFACTURERS INDIA, VIBRO GRIZZLY FEEDER MANUFACTURERS INDIA, DRUM SCRUBBER MANUFACTURERS INDIA, IRON ORE BENEFICIATION PLANT INDIA, MINERAL PROCESSING PLANT INDIA, IRON ORE PROCESSING PLANT INDIA, SAND WASHING PLANT INDIA, SILICA WASHING PLANT INDIA

  42. Bob says:

    I was amazed by this, was looking for something to get as a background and thought this would be great, sadly when i used the source it just stopped after drawing the pic, so adding Console.ReadKey(); on line 40 in the program file makes it able to see the art after its done