If you got here, you probably want to know how Ascii Art works and how to use C# to transform images into text. We’ll do this by making good use of LockBits() and UnlockBits(), and also, a pointer – going unsafe !
I know those make everything more complicated, but they’re more efficient.
• using 2 loops and a pointer, it gets the color of each pixel in the image (the image, stored in memory, looks like a two-dimensional array of pixels)
• for each pixel, it adds a character into a text file, depending on the alpha (transparency)
Now if you got a basic idea about how this works, you can build your own program – no need to worry about the source code, you’ll find everything here, including the necessary explainations.
Start by creating a Forms Project, make sure you have checked Allow unsafe code from Project->Properties->Build.
In form1_load add the following line to load the image from the executable’s directory:
Then, we transform this image into a Bitmap, and resize it to 100×100 pixels – don’t use HD images there, because it will take some time to check every pixel:
Now we need a StringBuilder in which we store the characters corresponding to the image’s pixels.
Update: as Alex Stuart pointed out, it is more efficient to use a StringBuilder instead of a string (why ?).
LockBits() – locks the image in the system’s memory so we can directly get pixel’s attributes by using a pointer
UnlockBits() – releases the memory used
As you know, an image is created by a group of pixels and each pixel takes 4 bytes of memory, that means it has 4 properties: Red, Green, Blue and Alpha/transparency. From the memory we can read each pixel’s property.
Each pixel must be transformed into a character with the same color and all the characters must be the same width and height (monospaced) so we maintain the aspect ratio.
Finally, we get this:
I know those make everything more complicated, but they’re more efficient.
How does an Ascii Art generator work?
• first, it opens the Image and resizes it to a custom size (about 100×100)• using 2 loops and a pointer, it gets the color of each pixel in the image (the image, stored in memory, looks like a two-dimensional array of pixels)
• for each pixel, it adds a character into a text file, depending on the alpha (transparency)
Now if you got a basic idea about how this works, you can build your own program – no need to worry about the source code, you’ll find everything here, including the necessary explainations.
Start by creating a Forms Project, make sure you have checked Allow unsafe code from Project->Properties->Build.
In form1_load add the following line to load the image from the executable’s directory:
1
2
|
Image img = Image.FromFile("image.png");
|
1
2
3
4
|
Bitmap bmp = new Bitmap(img, 100, 100);
// you can increase the Ascii Art's quality by increasing the bitmap's dimensions
// this also increases the time taken by the conversion process...
|
Update: as Alex Stuart pointed out, it is more efficient to use a StringBuilder instead of a string (why ?).
1.From Pixel to Char
As I said, we’ll use those 2 functions :LockBits() – locks the image in the system’s memory so we can directly get pixel’s attributes by using a pointer
UnlockBits() – releases the memory used
As you know, an image is created by a group of pixels and each pixel takes 4 bytes of memory, that means it has 4 properties: Red, Green, Blue and Alpha/transparency. From the memory we can read each pixel’s property.
Each pixel must be transformed into a character with the same color and all the characters must be the same width and height (monospaced) so we maintain the aspect ratio.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
private unsafe StringBuilder convert_image(Bitmap bmp)
{
StringBuilder asciiResult = new StringBuilder(); //here we store the ascii-art string
//setting the font's size & type (Courier new is monospace)
asciiResult.Append("<body style=\"font-family: 'Courier New', Courier, monospace;font-size: 7px;\">");
//storing the image's height & width
int bmpHeight = bmp.Height;
int bmpWidth = bmp.Width;
//here we lock the image in the memory by using LockBits
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmpWidth, bmpHeight), ImageLockMode.ReadOnly, bmp.PixelFormat);
// bmpStride tells us how many pixels are on a line
// because images have multiple lines of pixels (like 2D arrays)
int bmpStride = bmpData.Stride;
// this gets the memory address of the first pixel in the image
// currentPixel is the pointer we'll use
byte* currentPixel = (byte*)bmpData.Scan0;
for (int y = 0; y < bmpHeight; y++)
{
for (int x = 0; x < bmpWidth; x++)
{
// as I said a pixel takes 4 bytes of memory so it has 4 attributes
int r = currentPixel[x*4];
int g = currentPixel[x*4 + 1];
int b = currentPixel[x*4 + 2];
int alpha = currentPixel[x * 4 + 3];
// appending the character to the ascii-art stringbuilder
// note there's a custom function 'getAsciiChar()' - I'll explain it soon
asciiResult.Append(String.Format("<span style='color:rgb({0},{1},{2});'>{3}</span>", r, g, b, getAsciiChar(alpha)));
}
// reached end of this line, by adding bmpStride (number of pixels on each line)
// to the memory address, it gives us the adress of the first pixel on the next line
currentPixel += bmpStride;
asciiResult.Append("<br>");
}
asciiResult.Append("</body>"); // closing the body tag we opened at the beginning
bmp.UnlockBits(bmpData); //removing the image from the memory
return asciiResult; // returning the ascii-art stringbuilder
}
|
2.Choosing the right character
There’s a function in the code above that I’ll explain here: getAsciiChar(). What it does? It returns a character depending on the transparency of the current pixel (so it looks like true ascii art).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
private char getAsciiChar(int alpha)
{
if (alpha >= 240)
return '@';
if (alpha >= 200)
return '#';
if (alpha >= 160)
return '$';
if (alpha >= 120)
return '%';
if (alpha >= 80)
return '8';
if (alpha >= 40)
return '|';
return '.';
}
|
3.Displaying the Ascii-Art
Now we just have to display our image, which is easily done using this:
1
2
3
4
5
6
7
|
private void show_image(StringBuilder asciiResult)
{
StreamWriter sw = new StreamWriter("image.html");
sw.Write(asciiResult.ToString());
sw.Close();
}
|
Sign up here with your email
ConversionConversion EmoticonEmoticon