Click here to Skip to main content
15,860,861 members
Articles / Web Development / ASP.NET
Article

Using C# To Generate ASCII Art From An Image

Rate me:
Please Sign up or sign in to vote.
4.64/5 (24 votes)
11 Sep 2007CPOL4 min read 122.5K   2.7K   71   14
How to generate several types of ASCII art from an image file.

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.

C#
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:

C#
// 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.

C#
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:

C#
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)


Written By
Software Developer (Senior) CentralReach
United States United States
I have worked professionally in IT since 2004, and as a software architect since 2008, specializing in user interface design and experience, something I am still extremely passionate about. In 2013 I moved into management, and since then I've held positions as Director of Product Development, Director of Engineering, and Practice Director.

Comments and Discussions

 
GeneralI find many pair of characters and this set is the best. Pin
WingYin Tang20-Oct-15 23:30
WingYin Tang20-Oct-15 23:30 
Questionimage detect Pin
Swagot23-Jun-15 12:08
Swagot23-Jun-15 12:08 
GeneralMy vote of 5 Pin
Perić Željko29-Jul-12 8:11
professionalPerić Željko29-Jul-12 8:11 
GeneralYou got my 5.. Pin
shivamkalra17-Dec-09 22:37
shivamkalra17-Dec-09 22:37 
GeneralPossibly re-inventing the wheel...there has been a similar article written prior to yours Pin
daluu30-Dec-07 14:17
daluu30-Dec-07 14:17 
GeneralRe: Possibly re-inventing the wheel...there has been a similar article written prior to yours Pin
DreamInHex31-Dec-07 9:16
DreamInHex31-Dec-07 9:16 
GeneralRe: Possibly re-inventing the wheel...there has been a similar article written prior to yours Pin
Sing Abend20-Mar-21 10:11
professionalSing Abend20-Mar-21 10:11 
GeneralGrayScale Pin
terry mohre17-Sep-07 11:02
terry mohre17-Sep-07 11:02 
GeneralRe: GrayScale Pin
DreamInHex19-Sep-07 9:09
DreamInHex19-Sep-07 9:09 
Generalcheck this out ... Pin
Dr Satan17-Sep-07 10:40
Dr Satan17-Sep-07 10:40 
GeneralRe: check this out ... Pin
DreamInHex19-Sep-07 9:10
DreamInHex19-Sep-07 9:10 
Generalimages by block, not only pixel [modified] Pin
roberto galbiati11-Sep-07 23:21
professionalroberto galbiati11-Sep-07 23:21 

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.