Click here to Skip to main content
6,594,432 members and growing! (13,829 online)
Email Password   helpLost your password?
General Programming » Algorithms & Recipes » General     Intermediate License: The Code Project Open License (CPOL)

Using C# To Generate ASCII Art From An Image

By UsualDosage

How to generate several types of ASCII art from an image file.
C# 2.0, Windows, .NET 2.0, ASP.NET, GDI+, VS2005, Dev
Posted:11 Sep 2007
Views:29,487
Bookmarked:52 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
16 votes for this article.
Popularity: 5.45 Rating: 4.52 out of 5

1

2
2 votes, 12.5%
3
5 votes, 31.3%
4
9 votes, 56.3%
5

Screenshot - bronco1.gif

Introduction

This article is a "just for fun" how-to for some of those out there old enough to remember the heyday of BBS'es and MUDs. I was always fascinated by the talent of the folks good enough to make ASCII art that looked like real images. That in mind, years later (many), I decided to create an algorithm that would convert a real image into its ASCII representation. Even if you don't use this for that purpose, there should be some little tidbits of code that you might enjoy, even if you're not a fan of the early internet.

Background

There are a few kinds of ASCII art. I've tried to represent at least three of those styles here in this article. First, we'll do a single character ASCII drawing, and just set the colors of each character to the same colors as each pixel in the image. Next, we'll reduce the image to a grayscale drawing, then output the HTML in varying shades of gray, still using a single ASCII character. Finally, we'll reduce the image to grayscale, then, based on the shade of gray for each pixel, output different ASCII characters that have a different "shade". By shade, I'm simply talking about how dark a character will appear on a white background. So, for example, "#" appears to be much darker than ":" on a white background.

You can experiment with changing the character constants in the code to output different effects. I got particularly interesting results when I reversed the order of the output characters to get a "negative" effect.

Using the code

I've documented the code in the solution, but I will include my favorite method here for review. This method is the third type of ASCII art (as I mentioned above). It will take a posted file (via HTTP), read the pixels in, get the grayscale value of each pixel, then find the appropriate ASCII character to output.

public static string GrayscaleImageToASCII(System.Drawing.Image img)
{
    StringBuilder html = new StringBuilder();
    Bitmap bmp = null;

    try
    {
        // Create a bitmap from the image

        bmp = new Bitmap(img);

        // The text will be enclosed in a paragraph tag with the class

        // ascii_art so that we can apply CSS styles to it.

        html.Append("<br/&rt;");

        // Loop through each pixel in the bitmap

        for (int y = 0; y < bmp.Height; y++)
        {
            for (int x = 0; x < bmp.Width; x++)
            {
                // Get the color of the current pixel

                Color col = bmp.GetPixel(x, y);

                // To convert to grayscale, the easiest method is to add

                // the R+G+B colors and divide by three to get the gray

                // scaled color.

                col = Color.FromArgb((col.R + col.G + col.B) / 3,
                    (col.R + col.G + col.B) / 3,
                    (col.R + col.G + col.B) / 3);

                // Get the R(ed) value from the grayscale color,

                // parse to an int. Will be between 0-255.

                int rValue = int.Parse(col.R.ToString());

                // Append the "color" using various darknesses of ASCII

                // character.

                html.Append(getGrayShade(rValue));

                // If we're at the width, insert a line break

                if (x == bmp.Width - 1)
                    html.Append("&lt;br/&rt");
            }
        }

        // Close the paragraph tag, and return the html string.

        html.Append("&lt;/p&rt;");

        return html.ToString();
    }
    catch (Exception exc)
    {
        return exc.ToString();
    }
    finally
    {
        bmp.Dispose();
    }
}

This static method is called from the Default.aspx web form. The Default.aspx page holds our File Upload input, as well as three buttons that call the associated methods. In a nutshell, we're doing the following:

  • Convert the Image object into a Bitmap object
  • Enclose the output in HTML paragraph tags
  • Loop through each pixel in the bitmap, and obtain the color
  • Strip the color information from the pixel (see below)
  • Find the character to use based on the new shade (see below)
  • Aggregate all of the characters, then return the HTML

Converting to grayscale

The simplest way to convert a pixel to grayscale is by taking each pixel's Red, Green, and Blue components, dividing the summed value for each by three, and building a new color like so:

// Get the color of the current pixel

Color col = bmp.GetPixel(x, y);

// To convert to grayscale, the easiest method is to add

// the R+G+B colors and divide by three to get the gray

// scaled color.

col = Color.FromArgb((col.R + col.G + col.B) / 3,
    (col.R + col.G + col.B) / 3,
      (col.R + col.G + col.B) / 3);

Converting colors to characters

To do this required some experimenting. The values I've included with the demo code seem to work fairly well, but feel free to experiment with different character sets. In order to convert the gray shade to a character, we really only need one value. I chose to use the new Red value.

private static string getGrayShade(int redValue)
{
    string asciival = " ";

    if (redValue >= 230)
    {
        asciival = WHITE;
    }
    else if (redValue >= 200)
    {
        asciival = LIGHTGRAY;
    }
    else if (redValue >= 180)
    {
        asciival = SLATEGRAY;
    }
    else if (redValue >= 160)
    {
        asciival = GRAY;
    }
    else if (redValue >= 130)
    {
        asciival = MEDIUM;
    }
    else if (redValue >= 100)
    {
        asciival = MEDIUMGRAY;
    }
    else if (redValue >= 70)
    {
        asciival = DARKGRAY;
    }
    else if (redValue >= 50)
    {
        asciival = CHARCOAL;
    }
    else
    {
        asciival = BLACK;
    }

    return asciival;
}

You can use as few or as many constants as you like. 9 seemed to provide good results. The constant values that I chose are are as follows:

private const string BLACK = "@";
private const string CHARCOAL = "#";
private const string DARKGRAY = "8";
private const string MEDIUMGRAY = "&";
private const string MEDIUM = "o";
private const string GRAY = ":";
private const string SLATEGRAY = "*";
private const string LIGHTGRAY = ".";
private const string WHITE = " ";

So now, as we get the gray shade for each pixel, we just output the corresponding ASCII character. The logo of my favorite team (Denver Broncos) now appears as follows:

The Denver Broncos

Points of interest

Many of you will notice that I did not use an HTML Text Writer to build the HTML. Simply put, it was more overhead than I needed. StringBuilder seemed to work very well, and though it doesn't afford me the luxury of ensuring I have the right formatting, it is by far the fastest solution.

Styling is handled by a CSS file (included). For this reason, the "class='ascii_art'" bit was added to the opening paragraph tag. You may notice that the VS2005 IDE flags the "line-spacing" attribute in the CSS designer. Don't worry... both IE and Firefox know how to handle it. Line spacing keeps the characters close so there is not a lot of whitespace between the lines. Also, it is important to use a MONOTYPE font (Lucida Console, Courier New, Terminal, etc.) for the font, otherwise your image will be extremely skewed.

Currently, the solution just performs a Response.Write() of the HTML to the Default.aspx page on postback. This could obviously be modified to post to a separate page, but I chose not to for purposes of simplicity and illustration.

Happy ASCII'ing!

License

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

About the Author

UsualDosage


Member
I have been an ASP.NET/C# Programmer for about 7 years, specializing in business applications for financial institutions. I formerly wrote business applications for mortgage banking front-ends in C++ before switching to the .NET Framework, which I program in almost exclusively, now, except for my occasional contract dalliances in PHP and MySQL, which I really like. I especially enjoy graphic design, and web work.

In my spare time I run the local internet radio portal Jaxrockradio.com.

I have long moonlighted as an ANSI C programmer for several online MUDs (still a hobby of mine), and probably will continue to as long as they let me.

You can view my blog by visiting http://www.usualdosage.com.

My site design portfolio is located at http://design.usualdosage.com


Occupation: Web Developer
Location: United States United States

Other popular Algorithms & Recipes articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 7 of 7 (Total in Forum: 7) (Refresh)FirstPrevNext
GeneralPossibly re-inventing the wheel...there has been a similar article written prior to yours Pinmemberdaluu15:17 30 Dec '07  
GeneralRe: Possibly re-inventing the wheel...there has been a similar article written prior to yours PinmemberUsualDosage10:16 31 Dec '07  
GeneralGrayScale Pinmemberterrymohre12:02 17 Sep '07  
GeneralRe: GrayScale PinmemberUsualDosage10:09 19 Sep '07  
Generalcheck this out ... PinmemberDr Satan11:40 17 Sep '07  
GeneralRe: check this out ... PinmemberUsualDosage10:10 19 Sep '07  
Generalimages by block, not only pixel [modified] Pinmemberroberto galbiati0:21 12 Sep '07  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 11 Sep 2007
Editor: Smitha Vijayan
Copyright 2007 by UsualDosage
Everything else Copyright © CodeProject, 1999-2009
Web17 | Advertise on the Code Project